'light' theme (console & apps):
- Rework to reduce contrast (ticket #1996)
- Add custom radio/checkbox icons for Chrome/Blink

Console:
- Fix reseed failure notice overlap when "check connection" message shown
- Enhance presentation of console "error 500" messages
- Cosmetic tweaks to welcome page presentation, arabic welcome page tidyup
- Migrate .smallhead th class to tr to simplify markup
- Sidebar:
  - Remove "Congestion" from default sidebar (still present in default
    advanced sidebar)
  - Fix label wrapping issue with translations
  - Migrate sidepanel news section to tables to permit better control over
    wrapping and placement (ticket #1996)
  - Move news item dates to tooltips to lessen chance of headlines wrapping
    and cleanup presentation
  - Increase maximum length of local tunnel links in the sidebar (and handle
    overflow with CSS) to minimize chance of default tunnels display
    truncating in translations
  - Add the power off icon to sidebar restart button on hover to clarify
    button function (ticket #1996)
- /configadvanced: Move floodfill status to row above to remove unnecessary
  whitespace and cleanup UI
- /debug: Add debug icon to h2 headers to aid navigation
- /graphs: Improve presentation of graph images by removing rendered border
  and setting background color to better integrate with themes
- /home: Widen div.app to lessen chance of truncation (classic/midnight)
- /jars:
  - Minimize table overflow with conditional scaling of content
  - One-click select for revision, sha256 & unsigned mods
- /logs:
  - Reduce line/entry spacing for router and critical logs (#ticket 1996)
  - Multi-column view for changelog at >=1400px to address horizontal
    whitespace issue
- /netdb:
  - Multi-column display of Leases in LeaseSet tables
  - Better alignment of Lease elements, iconify gateway
  - Placeholder flag for unknown/unresolved peers for [flag+dest] combos to
    improve presentation / fix deadspace issue
- Chinese:
  - Fix alignment of table contents in /tunnels (classic)
  - Fix sidebar content display width (midnight)
  - Cosmetic fixes (all themes)

I2PTunnel:
- Ensure message log font scales proportionately
- Fix overflow issue with dropdowns in Chrome/blink (dark/midnight)

SusiDNS
- Reduce width of filter buttons to lessen chance of wrapping (ticket #1996)
- Add non-selectable items to improve copy/paste of hosts in address books
- Make dest addresses in host list only display scrollbar on focus to reduce
  screen clutter
- Hide broken imagegen images

SusiMail: Enhance presentation of 'postman' links on login panel
This commit is contained in:
str4d
2017-07-27 02:14:34 +00:00
parent 674f523cba
commit e284675e5a
38 changed files with 4731 additions and 1463 deletions

View File

@ -384,12 +384,15 @@ class NetDbRenderer {
buf.append("</td></tr>");
}
buf.append("<tr><td colspan=\"2\"><ul class=\"netdb_leases\">");
for (int i = 0; i < ls.getLeaseCount(); i++) {
Lease lease = ls.getLease(i);
buf.append("<tr><td colspan=\"2\">");
buf.append("<b>").append(_t("Lease")).append(' ').append(i + 1).append(":</b> ").append(_t("Gateway")).append(' ');
buf.append("<li><b>").append(_t("Lease")).append(' ').append(i + 1).append(":</b> <span class=\"netdb_gateway\" title=\"")
.append(_t("Gateway")).append("\"><img src=\"themes/console/images/info/gateway.png\" alt=\"")
.append(_t("Gateway")).append("\"></span> <span class=\"tunnel_peer\">");
buf.append(_context.commSystem().renderPeerHTML(lease.getGateway()));
buf.append(' ').append(_t("Tunnel")).append(' ').append(lease.getTunnelId().getTunnelId()).append(' ');
buf.append("</span> <span class=\"netdb_tunnel\">").append(_t("Tunnel")).append(" <span class=\"tunnel_id\">")
.append(lease.getTunnelId().getTunnelId()).append("</span></span> ");
if (debug) {
long exl = lease.getEndDate().getTime() - now;
if (exl > 0)
@ -397,8 +400,9 @@ class NetDbRenderer {
else
buf.append("<b class=\"netdb_expiry\">").append(_t("Expired {0} ago", DataHelper.formatDuration2(0-exl))).append("</b>");
}
buf.append("</td></tr>\n");
buf.append("</li>");
}
buf.append("</ul></td></tr>\n");
buf.append("</table>\n");
out.write(buf.toString());
buf.setLength(0);

View File

@ -344,49 +344,49 @@ public class PeerHelper extends HelperBase {
}
buf.append(".</h3>\n");
buf.append("<div class=\"widescroll\"><table id=\"udpconnections\">\n");
buf.append("<tr><th class=\"smallhead\" nowrap><a href=\"#def.peer\">").append(_t("Peer")).append("</a><br>");
buf.append("<tr class=\"smallhead\"><th nowrap><a href=\"#def.peer\">").append(_t("Peer")).append("</a><br>");
if (sortFlags != FLAG_ALPHA)
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by peer hash"), FLAG_ALPHA);
buf.append("</th><th class=\"smallhead\" nowrap><a href=\"#def.dir\" title=\"")
buf.append("</th><th nowrap><a href=\"#def.dir\" title=\"")
.append(_t("Direction/Introduction")).append("\">").append(_t("Dir"))
.append("</a></th><th class=\"smallhead\" nowrap>").append(_t("IPv6"))
.append("</th><th class=\"smallhead\" nowrap><a href=\"#def.idle\">").append(_t("Idle")).append("</a><br>");
.append("</a></th><th nowrap>").append(_t("IPv6"))
.append("</th><th nowrap><a href=\"#def.idle\">").append(_t("Idle")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by idle inbound"), FLAG_IDLE_IN);
buf.append(" / ");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by idle outbound"), FLAG_IDLE_OUT);
buf.append("</th>");
buf.append("<th class=\"smallhead\" nowrap><a href=\"#def.rate\">").append(_t("In/Out")).append("</a><br>");
buf.append("<th nowrap><a href=\"#def.rate\">").append(_t("In/Out")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by inbound rate"), FLAG_RATE_IN);
buf.append(" / ");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound rate"), FLAG_RATE_OUT);
buf.append("</th>\n");
buf.append("<th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.up\">").append(_t("Up")).append("</a><br>");
buf.append("<th nowrap><span class=\"peersort\"><a href=\"#def.up\">").append(_t("Up")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by connection uptime"), FLAG_UPTIME);
buf.append("</span></th><th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.skew\">").append(_t("Skew")).append("</a><br>");
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.skew\">").append(_t("Skew")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by clock skew"), FLAG_SKEW);
buf.append("</span></th>\n");
buf.append("<th class=\"smallhead\" nowrap><a href=\"#def.cwnd\">CWND</a><br>");
buf.append("<th nowrap><a href=\"#def.cwnd\">CWND</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by congestion window"), FLAG_CWND);
buf.append("</th><th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.ssthresh\">SST</a><br>");
buf.append("</th><th nowrap><span class=\"peersort\"><a href=\"#def.ssthresh\">SST</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by slow start threshold"), FLAG_SSTHRESH);
buf.append("</span></th>\n");
buf.append("<th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.rtt\">RTT</a><br>");
buf.append("<th nowrap><span class=\"peersort\"><a href=\"#def.rtt\">RTT</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time"), FLAG_RTT);
//buf.append("</th><th class=\"smallhead\" nowrap><a href=\"#def.dev\">").append(_t("Dev")).append("</a><br>");
//buf.append("</th><th nowrap><a href=\"#def.dev\">").append(_t("Dev")).append("</a><br>");
//appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time deviation"), FLAG_DEV);
buf.append("</span></th><th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.rto\">RTO</a><br>");
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.rto\">RTO</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by retransmission timeout"), FLAG_RTO);
buf.append("</span></th>\n");
buf.append("<th class=\"smallhead\" nowrap><a href=\"#def.mtu\">MTU</a><br>");
buf.append("<th nowrap><a href=\"#def.mtu\">MTU</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound maximum transmit unit"), FLAG_MTU);
buf.append("</th><th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.send\">").append(_t("TX")).append("</a><br>");
buf.append("</th><th nowrap><span class=\"peersort\"><a href=\"#def.send\">").append(_t("TX")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets sent"), FLAG_SEND);
buf.append("</span></th><th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.recv\">").append(_t("RX")).append("</a><br>");
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.recv\">").append(_t("RX")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received"), FLAG_RECV);
buf.append("</span></th>\n");
buf.append("<th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.resent\">").append(_t("Dup TX")).append("</a><br>");
buf.append("<th nowrap><span class=\"peersort\"><a href=\"#def.resent\">").append(_t("Dup TX")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets retransmitted"), FLAG_RESEND);
buf.append("</span></th><th class=\"smallhead\" nowrap><span class=\"peersort\"><a href=\"#def.dupRecv\">").append(_t("Dup RX")).append("</a><br>");
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.dupRecv\">").append(_t("Dup RX")).append("</a><br>");
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received more than once"), FLAG_DUP);
buf.append("</span></th></tr>\n");
out.write(buf.toString());

View File

@ -205,23 +205,23 @@ class ProfileOrganizerRenderer {
//buf.append("<h2><a name=\"flood\"></a>").append(_t("Floodfill and Integrated Peers"))
// .append(" (").append(integratedPeers.size()).append(")</h2>\n");
buf.append("<div class=\"widescroll\"><table id=\"floodfills\">");
buf.append("<tr>");
buf.append("<th class=\"smallhead\">").append(_t("Peer")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Caps")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Integ. Value")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Heard About")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Heard From")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Good Send")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Bad Send")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("10m Resp. Time")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("1h Resp. Time")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("1d Resp. Time")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Good Lookup")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Bad Lookup")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Good Store")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("Last Bad Store")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("1h Fail Rate")).append("</th>");
buf.append("<th class=\"smallhead\">").append(_t("1d Fail Rate")).append("</th>");
buf.append("<tr class=\"smallhead\">");
buf.append("<th>").append(_t("Peer")).append("</th>");
buf.append("<th>").append(_t("Caps")).append("</th>");
buf.append("<th>").append(_t("Integ. Value")).append("</th>");
buf.append("<th>").append(_t("Last Heard About")).append("</th>");
buf.append("<th>").append(_t("Last Heard From")).append("</th>");
buf.append("<th>").append(_t("Last Good Send")).append("</th>");
buf.append("<th>").append(_t("Last Bad Send")).append("</th>");
buf.append("<th>").append(_t("10m Resp. Time")).append("</th>");
buf.append("<th>").append(_t("1h Resp. Time")).append("</th>");
buf.append("<th>").append(_t("1d Resp. Time")).append("</th>");
buf.append("<th>").append(_t("Last Good Lookup")).append("</th>");
buf.append("<th>").append(_t("Last Bad Lookup")).append("</th>");
buf.append("<th>").append(_t("Last Good Store")).append("</th>");
buf.append("<th>").append(_t("Last Bad Store")).append("</th>");
buf.append("<th>").append(_t("1h Fail Rate")).append("</th>");
buf.append("<th>").append(_t("1d Fail Rate")).append("</th>");
buf.append("</tr>");
RateAverages ra = RateAverages.getTemp();
for (PeerProfile prof : order) {
@ -290,31 +290,31 @@ class ProfileOrganizerRenderer {
.append("</td></tr>");
buf.append("<tr id=\"capabilities_key\"><td colspan=\"2\"><table><tbody>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>B:</b></td><td>").append(_t("SSU Testing")).append("</td>")
.append("<td><b>C:</b></td><td>").append(_t("SSU Introducer")).append("</td>")
.append("<td><b>B</b></td><td>").append(_t("SSU Testing")).append("</td>")
.append("<td><b>C</b></td><td>").append(_t("SSU Introducer")).append("</td>")
.append("<td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>f:</b></td><td>").append(_t("Floodfill")).append("</td>")
.append("<td><b>H:</b></td><td>").append(_t("Hidden")).append("</td>")
.append("<td><b>f</b></td><td>").append(_t("Floodfill")).append("</td>")
.append("<td><b>H</b></td><td>").append(_t("Hidden")).append("</td>")
.append("<td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>K:</b></td><td>").append(_t("Under {0} shared bandwidth", "12KBps")).append("</td>")
.append("<td><b>L:</b></td><td>").append(_t("{0} shared bandwidth", "12 - 32KBps")).append("</td>")
.append("<td><b>K</b></td><td>").append(_t("Under {0} shared bandwidth", "12KBps")).append("</td>")
.append("<td><b>L</b></td><td>").append(_t("{0} shared bandwidth", "12 - 32KBps")).append("</td>")
.append("<td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>M:</b></td><td>").append(_t("{0} shared bandwidth", "32 - 64KBps")).append("</td>")
.append("<td><b>N:</b></td><td>").append(_t("{0} shared bandwidth", "64 - 128KBps")).append("</td>")
.append("<td><b>M</b></td><td>").append(_t("{0} shared bandwidth", "32 - 64KBps")).append("</td>")
.append("<td><b>N</b></td><td>").append(_t("{0} shared bandwidth", "64 - 128KBps")).append("</td>")
.append("<td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>O:</b></td><td>").append(_t("{0} shared bandwidth", "128 - 256KBps")).append("</td>")
.append("<td><b>P:</b></td><td>").append(_t("{0} shared bandwidth", "256 - 2000KBps")).append("</td>")
.append("<td><b>O</b></td><td>").append(_t("{0} shared bandwidth", "128 - 256KBps")).append("</td>")
.append("<td><b>P</b></td><td>").append(_t("{0} shared bandwidth", "256 - 2000KBps")).append("</td>")
.append("<td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>R:</b></td><td>").append(_t("Reachable")).append("</td>")
.append("<td><b>U:</b></td><td>").append(_t("Unreachable")).append("</td>")
.append("<td><b>R</b></td><td>").append(_t("Reachable")).append("</td>")
.append("<td><b>U</b></td><td>").append(_t("Unreachable")).append("</td>")
.append("<td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td>")
.append("<td><b>X:</b></td><td>").append(_t("Over {0} shared bandwidth", "2000KBps")).append("</td>")
.append("<td><b>X</b></td><td>").append(_t("Over {0} shared bandwidth", "2000KBps")).append("</td>")
.append("<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr>");
buf.append("<tr><td>&nbsp;</td><td colspan=\"5\">").append(_t("Note: For P and X bandwidth tiers, O is included for the purpose of backward compatibility in the NetDB."))
.append("</tr>");

View File

@ -709,7 +709,7 @@ class SummaryBarRenderer {
//buf.append("<h3><a href=\"/configupdate\">")
buf.append("<h3><a href=\"/news\">")
.append(_t("News &amp; Updates"))
.append("</a></h3><hr class=\"b\"><div class=\"newsheadings\">\n");
.append("</a></h3><hr class=\"b\"><div class=\"sb_newsheadings\">\n");
// Get news content.
List<NewsEntry> entries = Collections.emptyList();
ClientAppManager cmgr = _context.clientAppManager();
@ -719,7 +719,7 @@ class SummaryBarRenderer {
entries = nmgr.getEntries();
}
if (!entries.isEmpty()) {
buf.append("<ul>\n");
buf.append("<table>\n");
DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT);
// the router sets the JVM time zone to UTC but saves the original here so we can get it
fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
@ -731,29 +731,30 @@ class SummaryBarRenderer {
if (i >= min && entry.updated > 0 &&
entry.updated < _context.clock().now() - 60*24*60*60*1000L)
break;
buf.append("<li><a href=\"/?news=1&amp;consoleNonce=")
buf.append("<tr><td><a href=\"/?news=1&amp;consoleNonce=")
.append(consoleNonce)
.append("\">");
.append("\"");
if (entry.updated > 0) {
Date date = new Date(entry.updated);
buf.append(fmt.format(date))
.append(": ");
// tooltip to tag for translation post 0.9.31 release
buf.append(" title=\"Published: ").append(fmt.format(date)).append("\"");
}
buf.append(">");
buf.append(entry.title)
.append("</a></li>\n");
.append("</a></td></tr>\n");
if (++i >= max)
break;
}
buf.append("</ul>\n");
//buf.append("<a href=\"/news\">")
// .append(_t("Show all news"))
// .append("</a>\n");
buf.append("</table>\n");
} else {
buf.append("<center><i>")
.append(_t("none"))
.append("</i></center>");
}
// Add post-headings stuff.
//buf.append("<a href=\"/news\">")
//.append(_t("Show all news"))
//.append("</a>\n");
buf.append("</div>\n");
}
return buf.toString();

View File

@ -45,16 +45,15 @@ public class SummaryHelper extends HelperBase {
static final String DEFAULT_FULL =
"HelpAndFAQ" + S +
"ShortGeneral" + S +
"Bandwidth" + S +
"UpdateStatus" + S +
"FirewallAndReseedStatus" + S +
"Bandwidth" + S +
"NetworkReachability" + S +
"FirewallAndReseedStatus" + S +
"I2PServices" + S +
"I2PInternals" + S +
"Peers" + S +
"Tunnels" + S +
"TunnelStatus" + S +
"Congestion" + S +
"RestartStatus" + S +
"Destinations" + S +
"";
@ -62,10 +61,10 @@ public class SummaryHelper extends HelperBase {
static final String DEFAULT_FULL_ADVANCED =
"HelpAndFAQ" + S +
"ShortGeneral" + S +
"Bandwidth" + S +
"UpdateStatus" + S +
"FirewallAndReseedStatus" + S +
"Bandwidth" + S +
"NetworkReachability" + S +
"FirewallAndReseedStatus" + S +
"I2PServices" + S +
"I2PInternals" + S +
"Advanced" + S +
@ -80,11 +79,11 @@ public class SummaryHelper extends HelperBase {
static final String DEFAULT_MINIMAL =
"ShortGeneral" + S +
"Bandwidth" + S +
"NewsHeadings" + S +
"UpdateStatus" + S +
"NewsHeadings" + S +
"NetworkReachability" + S +
"RestartStatus" + S +
"FirewallAndReseedStatus" + S +
"RestartStatus" + S +
"Destinations" + S +
"";
@ -516,10 +515,11 @@ public class SummaryHelper extends HelperBase {
buf.append("client.png\" alt=\"Client\" title=\"").append(_t("Client")).append("\">");
buf.append("</td><td align=\"left\"><b><a href=\"tunnels#").append(h.toBase64().substring(0,4));
buf.append("\" target=\"_top\" title=\"").append(_t("Show tunnels")).append("\">");
if (name.length() <= 20)
// Increase permitted max length of tunnel name & handle overflow with css
if (name.length() <= 32)
buf.append(DataHelper.escapeHTML(name));
else
buf.append(DataHelper.escapeHTML(name.substring(0,18))).append("&hellip;");
buf.append(DataHelper.escapeHTML(name.substring(0,29))).append("&hellip;");
buf.append("</a></b></td>\n");
LeaseSet ls = _context.netDb().lookupLeaseSetLocally(h);
if (ls != null && _context.tunnelManager().getOutboundClientTunnelCount(h) > 0) {

View File

@ -35,6 +35,9 @@ class SummaryRenderer {
private final Log _log;
private final SummaryListener _listener;
private final I2PAppContext _context;
private static final Color BACK_COLOR = new Color(246, 246, 255);
private static final Color SHADEA_COLOR = new Color(246, 246, 255);
private static final Color SHADEB_COLOR = new Color(246, 246, 255);
private static final Color GRID_COLOR = new Color(100, 100, 100, 75);
private static final Color MGRID_COLOR = new Color(255, 91, 91, 110);
private static final Color AREA_COLOR = new Color(100, 160, 200, 200);
@ -131,8 +134,11 @@ class SummaryRenderer {
RrdGraphDef def = new RrdGraphDef();
// Override defaults
def.setColor(RrdGraphDef.COLOR_GRID, GRID_COLOR);
def.setColor(RrdGraphDef.COLOR_MGRID, MGRID_COLOR);
def.setColor(RrdGraphDef.COLOR_BACK, BACK_COLOR);
def.setColor(RrdGraphDef.COLOR_SHADEA, SHADEA_COLOR);
def.setColor(RrdGraphDef.COLOR_SHADEB, SHADEB_COLOR);
def.setColor(RrdGraphDef.COLOR_GRID, GRID_COLOR);
def.setColor(RrdGraphDef.COLOR_MGRID, MGRID_COLOR);
def.setFont(RrdGraphDef.FONTTAG_DEFAULT, new Font(DEFAULT_FONT_NAME, Font.PLAIN, 10));
def.setFont(RrdGraphDef.FONTTAG_TITLE, new Font(DEFAULT_FONT_NAME, Font.PLAIN, 10));
def.setFont(RrdGraphDef.FONTTAG_AXIS, new Font("Droid Sans Mono", Font.PLAIN, 10));