├── LICENSE ├── README.md ├── css └── main.css ├── favicon.ico ├── index.html ├── js ├── app.js ├── functions.js └── lib │ └── underscore-min.js └── run.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 libre-net-society 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # i2pd-webui 2 | Fancy i2pd web user interface 3 | 4 | # Usage: 5 | 6 | Git clone repo to your computer. 7 | 8 | Run i2pd with i2pcontrol port listening at 7650, for example: 9 | 10 | ./i2p --i2pcontrolport=7650 11 | 12 | Then run web ui: 13 | 14 | python3 run.py 15 | 16 | Visit http://127.0.0.1:8082/ and enjoy fancy i2pd webui! ;) 17 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Pure v0.6.0 3 | Copyright 2014 Yahoo! Inc. All rights reserved. 4 | Licensed under the BSD License. 5 | https://github.com/yahoo/pure/blob/master/LICENSE.md 6 | */ 7 | /*! 8 | normalize.css v^3.0 | MIT License | git.io/normalize 9 | Copyright (c) Nicolas Gallagher and Jonathan Neal 10 | */ 11 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap;-ms-align-content:flex-start;-webkit-align-content:flex-start;align-content:flex-start}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}.pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:0 rgba(0,0,0,0);background-color:#E6E6E6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:hover,.pure-button:focus{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000\9}.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{border:0;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}.pure-button-hidden{display:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129FEA}.pure-form input:not([type]):focus{outline:0;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked input[type=file],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-list,.pure-menu-item{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-link,.pure-menu-heading{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-separator{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-allow-hover:hover>.pure-menu-children,.pure-menu-active>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-link,.pure-menu-disabled,.pure-menu-heading{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:hover,.pure-menu-link:focus{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0}body { 12 | color: #777; 13 | } 14 | 15 | .pure-img-responsive { 16 | max-width: 100%; 17 | height: auto; 18 | } 19 | 20 | /* 21 | Add transition to containers so they can push in and out. 22 | */ 23 | #layout, 24 | #menu, 25 | .menu-link { 26 | -webkit-transition: all 0.2s ease-out; 27 | -moz-transition: all 0.2s ease-out; 28 | -ms-transition: all 0.2s ease-out; 29 | -o-transition: all 0.2s ease-out; 30 | transition: all 0.2s ease-out; 31 | } 32 | 33 | /* 34 | This is the parent `
` that contains the menu and the content area. 35 | */ 36 | #layout { 37 | position: relative; 38 | padding-left: 0; 39 | } 40 | #layout.active #menu { 41 | left: 150px; 42 | width: 150px; 43 | } 44 | 45 | #layout.active .menu-link { 46 | left: 150px; 47 | } 48 | /* 49 | The content `
` is where all your content goes. 50 | */ 51 | .content { 52 | margin: 0 auto; 53 | padding: 0 2em; 54 | max-width: 800px; 55 | margin-bottom: 50px; 56 | line-height: 1.6em; 57 | } 58 | 59 | .header { 60 | margin: 0; 61 | color: #333; 62 | text-align: center; 63 | padding: 2.5em 2em 0; 64 | border-bottom: 1px solid #eee; 65 | } 66 | .header h1 { 67 | margin: 0.2em 0; 68 | font-size: 3em; 69 | font-weight: 300; 70 | } 71 | .header h2 { 72 | font-weight: 300; 73 | color: #ccc; 74 | padding: 0; 75 | margin-top: 0; 76 | } 77 | 78 | .content-subhead { 79 | margin: 50px 0 20px 0; 80 | font-weight: 300; 81 | color: #888; 82 | } 83 | 84 | 85 | 86 | /* 87 | The `#menu` `
` is the parent `
` that contains the `.pure-menu` that 88 | appears on the left side of the page. 89 | */ 90 | 91 | #menu { 92 | margin-left: -150px; /* "#menu" width */ 93 | width: 150px; 94 | position: fixed; 95 | top: 0; 96 | left: 0; 97 | bottom: 0; 98 | z-index: 1000; /* so the menu or its navicon stays above all content */ 99 | background: #191818; 100 | overflow-y: auto; 101 | -webkit-overflow-scrolling: touch; 102 | } 103 | /* 104 | All anchors inside the menu should be styled like this. 105 | */ 106 | #menu a { 107 | color: #999; 108 | border: none; 109 | padding: 0.6em 0 0.6em 0.6em; 110 | } 111 | 112 | /* 113 | Remove all background/borders, since we are applying them to #menu. 114 | */ 115 | #menu .pure-menu, 116 | #menu .pure-menu ul { 117 | border: none; 118 | background: transparent; 119 | } 120 | 121 | /* 122 | Add that light border to separate items into groups. 123 | */ 124 | #menu .pure-menu ul, 125 | #menu .pure-menu .menu-item-divided { 126 | border-top: 1px solid #333; 127 | } 128 | /* 129 | Change color of the anchor links on hover/focus. 130 | */ 131 | #menu .pure-menu li a:hover, 132 | #menu .pure-menu li a:focus { 133 | background: #333; 134 | } 135 | 136 | /* 137 | This styles the selected menu item `
  • `. 138 | */ 139 | #menu .pure-menu-selected, 140 | #menu .pure-menu-heading { 141 | background: #1f8dd6; 142 | } 143 | /* 144 | This styles a link within a selected menu item `
  • `. 145 | */ 146 | #menu .pure-menu-selected a { 147 | color: #fff; 148 | } 149 | 150 | /* 151 | This styles the menu heading. 152 | */ 153 | #menu .pure-menu-heading { 154 | font-size: 110%; 155 | color: #fff; 156 | margin: 0; 157 | } 158 | 159 | /* -- Dynamic Button For Responsive Menu -------------------------------------*/ 160 | 161 | /* 162 | The button to open/close the Menu is custom-made and not part of Pure. Here's 163 | how it works: 164 | */ 165 | 166 | /* 167 | `.menu-link` represents the responsive menu toggle that shows/hides on 168 | small screens. 169 | */ 170 | .menu-link { 171 | position: fixed; 172 | display: block; /* show this only on small screens */ 173 | top: 0; 174 | left: 0; /* "#menu width" */ 175 | background: #000; 176 | background: rgba(0,0,0,0.7); 177 | font-size: 10px; /* change this value to increase/decrease button size */ 178 | z-index: 10; 179 | width: 2em; 180 | height: auto; 181 | padding: 2.1em 1.6em; 182 | } 183 | 184 | .menu-link:hover, 185 | .menu-link:focus { 186 | background: #000; 187 | } 188 | 189 | .menu-link span { 190 | position: relative; 191 | display: block; 192 | } 193 | 194 | .menu-link span, 195 | .menu-link span:before, 196 | .menu-link span:after { 197 | background-color: #fff; 198 | width: 100%; 199 | height: 0.2em; 200 | } 201 | 202 | .menu-link span:before, 203 | .menu-link span:after { 204 | position: absolute; 205 | margin-top: -0.6em; 206 | content: " "; 207 | } 208 | 209 | .menu-link span:after { 210 | margin-top: 0.6em; 211 | } 212 | 213 | 214 | /* -- Responsive Styles (Media Queries) ------------------------------------- */ 215 | 216 | /* 217 | Hides the menu at `48em`, but modify this based on your app's needs. 218 | */ 219 | @media (min-width: 48em) { 220 | 221 | .header, 222 | .content { 223 | padding-left: 2em; 224 | padding-right: 2em; 225 | } 226 | 227 | #layout { 228 | padding-left: 150px; /* left col width "#menu" */ 229 | left: 0; 230 | } 231 | #menu { 232 | left: 150px; 233 | } 234 | 235 | .menu-link { 236 | position: fixed; 237 | left: 150px; 238 | display: none; 239 | } 240 | 241 | #layout.active .menu-link { 242 | left: 150px; 243 | } 244 | } 245 | 246 | @media (max-width: 48em) { 247 | /* Only apply this when the window is small. Otherwise, the following 248 | case results in extra padding on the left: 249 | * Make the window small. 250 | * Tap the menu to trigger the active state. 251 | * Make the window large again. 252 | */ 253 | #layout.active { 254 | position: relative; 255 | left: 150px; 256 | } 257 | } 258 | 259 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l-n-s/i2pd-webui/33bc65d1fe0530e85d3248423be06025b5b3fab0/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | I2Pd webUI 0.1a (demo) 6 | 7 | 8 | 9 | 10 | 50 | 51 | 68 | 69 | 80 | 81 | 82 | 83 |
    84 | 85 | 86 | 87 | 88 | 99 | 100 |
    101 | 104 |
    105 |
    106 | 107 | 108 | 109 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Quick and dirty demo control panel for PurpleI2P 3 | * 4 | */ 5 | 6 | var ready = new Event('ready'); 7 | 8 | var app = { 9 | 'getToken': function() { 10 | doAjax(function() { 11 | if (this.readyState == 4) { 12 | if (this.status == "200" && this.responseText != "") { 13 | window.token = JSON.parse(this.responseText).result.Token; 14 | document.dispatchEvent(ready); 15 | } else { 16 | document.open(); document.write(this.responseText); document.close(); 17 | throw new Error("Something is not working"); 18 | } 19 | } 20 | }, _jrc("Authenticate", { "API": 1, "Password": app.password })); 21 | }, 22 | 23 | 'init': function() { 24 | app.getToken(); 25 | document.addEventListener('ready', router, false); 26 | }, 27 | 28 | 'password': "itoopie", 29 | }; 30 | 31 | route("/", function(){ 32 | basicRender("home", _jrc("RouterInfo", { 33 | "i2p.router.version": "", 34 | "i2p.router.net.status": "", 35 | "Token": window.token 36 | }), frontPageEvents); 37 | 38 | fetchStats(); 39 | window.refresh = setInterval(fetchStats, 10000); 40 | }); 41 | 42 | route("/config", function(){ 43 | var jsonData = _jrc("NetworkSetting", { 44 | "i2p.router.net.bw.in": null, 45 | "i2p.router.net.bw.out": null, 46 | //"i2p.router.net.bw.share": null, 47 | "Token": window.token 48 | }); 49 | basicRender("config", jsonData, configPageEvents); 50 | }); 51 | 52 | route("/help", function(){ 53 | basicRender("help"); 54 | }); 55 | 56 | 57 | -------------------------------------------------------------------------------- /js/functions.js: -------------------------------------------------------------------------------- 1 | // PureCSS menu toggle 2 | (function (window, document) { 3 | 4 | var layout = document.getElementById('layout'), 5 | menu = document.getElementById('menu'), 6 | menuLink = document.getElementById('menuLink'); 7 | 8 | function toggleClass(element, className) { 9 | var classes = element.className.split(/\s+/), 10 | length = classes.length, 11 | i = 0; 12 | 13 | for(; i < length; i++) { 14 | if (classes[i] === className) { 15 | classes.splice(i, 1); 16 | break; 17 | } 18 | } 19 | // The className is not found 20 | if (length === classes.length) { 21 | classes.push(className); 22 | } 23 | 24 | element.className = classes.join(' '); 25 | } 26 | 27 | menuLink.onclick = function (e) { 28 | var active = 'active'; 29 | 30 | e.preventDefault(); 31 | toggleClass(layout, active); 32 | toggleClass(menu, active); 33 | toggleClass(menuLink, active); 34 | }; 35 | 36 | }(this, this.document)); 37 | 38 | function msToString(mseconds) { 39 | // ms to string for uptime 40 | var seconds = mseconds / 1000; 41 | var numdays = Math.floor(seconds / 86400); 42 | var numhours = Math.floor((seconds % 86400) / 3600); 43 | var numminutes = Math.floor(((seconds % 86400) % 3600) / 60); 44 | var numseconds = ((seconds % 86400) % 3600) % 60; 45 | 46 | return numdays + "d " + numhours + "h " + numminutes + "m " + numseconds + "s"; 47 | } 48 | 49 | function doAjax(responseHandler, data) { 50 | responseHandler = responseHandler || null; 51 | data = data || null; 52 | var xmlHttpRequst = new XMLHttpRequest(); 53 | 54 | xmlHttpRequst.open("POST", "", true); 55 | xmlHttpRequst.setRequestHeader('Content-Type', 'application/json'); 56 | xmlHttpRequst.onreadystatechange = responseHandler; 57 | xmlHttpRequst.send(data); 58 | } 59 | 60 | function _id(id) { 61 | return document.getElementById(id); 62 | } 63 | 64 | function _inHTML(id, value) { 65 | _id(id).innerHTML = value; 66 | return true; 67 | } 68 | 69 | function renderPage(templateId, args) { 70 | // write template to main div 71 | args = args || {}; 72 | var template = _.template(_id(templateId).innerHTML)(args); 73 | _inHTML("main", template); 74 | } 75 | 76 | function basicRender(templateId, jsonData, onReady) { 77 | jsonData = jsonData || null; 78 | onReady = onReady || null; 79 | 80 | if (jsonData) { 81 | doAjax(function(){ 82 | if (this.readyState == 4 && this.status == "200" && this.responseText != "") { 83 | //console.log(this.responseText); 84 | var result = JSON.parse(this.responseText).result; 85 | renderPage(templateId, {"r": result}); 86 | if (onReady) onReady(); 87 | } 88 | }, jsonData); 89 | } else { 90 | renderPage(templateId); 91 | if (onReady) onReady(); 92 | } 93 | }; 94 | 95 | function _jrc(method, params) { 96 | // JsonRpcConverter 97 | var obj = { 98 | "id": 1, 99 | "method": method, 100 | "params": params, 101 | "jsonrpc": "2.0" 102 | } 103 | return JSON.stringify(obj); 104 | } 105 | 106 | function fetchStats() { 107 | var jsonData = _jrc("RouterInfo", { 108 | "i2p.router.uptime": "", 109 | "i2p.router.net.tunnels.participating": "", 110 | "i2p.router.netdb.activepeers": "", 111 | "i2p.router.netdb.knownpeers": "", 112 | "i2p.router.net.bw.inbound.1s": "", 113 | "i2p.router.net.bw.outbound.1s": "", 114 | "Token": window.token 115 | }); 116 | 117 | doAjax(function(){ 118 | if (this.readyState == 4 && this.status == "200" && this.responseText != "") { 119 | //console.log(this.responseText); 120 | var result = JSON.parse(this.responseText).result; 121 | _inHTML("uptime", msToString(result["i2p.router.uptime"])); 122 | _inHTML("tunnels-participating", result["i2p.router.net.tunnels.participating"]); 123 | _inHTML("activepeers", result["i2p.router.netdb.activepeers"]); 124 | _inHTML("knownpeers", result["i2p.router.netdb.knownpeers"]); 125 | _inHTML("bw-in", result["i2p.router.net.bw.inbound.1s"]); 126 | _inHTML("bw-out", result["i2p.router.net.bw.outbound.1s"]); 127 | } 128 | }, jsonData); 129 | }; 130 | 131 | /* Routing */ 132 | var routes = {}; 133 | function route(path, controller) { 134 | routes[path] = {controller: controller}; 135 | } 136 | 137 | var el = null; 138 | function router() { 139 | el = el || _id("main"); 140 | var url = location.hash.slice(1) || '/'; 141 | var route = routes[url]; 142 | 143 | if (el && route.controller) { 144 | if (window.refresh) clearInterval(window.refresh); 145 | route.controller(); 146 | } 147 | } 148 | 149 | /* 150 | * Do RouterManager request and reload on success 151 | */ 152 | function dumbRequest(data) { 153 | doAjax(function() { 154 | if (this.readyState == 4 && this.status == "200") 155 | window.location.reload(); 156 | }, _jrc("RouterManager", data)); 157 | } 158 | 159 | function frontPageEvents() { 160 | _id("restart").addEventListener('click', function() { 161 | dumbRequest({"Restart": null, "Token": window.token}); 162 | }); 163 | _id("reseed").addEventListener('click', function(){ 164 | dumbRequest({"Reseed": null, "Token": window.token}); 165 | }); 166 | _id("shutdown").addEventListener('click', function(){ 167 | dumbRequest({"Shutdown": null, "Token": window.token}); 168 | }); 169 | } 170 | 171 | function configPageEvents() { 172 | _id("config-form").addEventListener('submit', function(evt) { 173 | evt.preventDefault(); 174 | var form = _id("config-form"); 175 | doAjax(function() { 176 | if (this.readyState == 4 && this.status == "200") { 177 | var result = JSON.parse(this.responseText).result; 178 | if (result["SettingsSaved"] == true) 179 | alert("Network settings changed!"); 180 | } 181 | }, _jrc("NetworkSetting", { 182 | "i2p.router.net.bw.in": form.bwin.value, 183 | "i2p.router.net.bw.out": form.bwout.value, 184 | //"i2p.router.net.bw.share": form.bwshare.value, 185 | "Token": window.token, 186 | })); 187 | return false; 188 | }); 189 | } 190 | -------------------------------------------------------------------------------- /js/lib/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.8.3 2 | // http://underscorejs.org 3 | // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this); 6 | //# sourceMappingURL=underscore-min.map -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | import http.server 3 | import urllib.request, urllib.error, urllib.parse 4 | import os 5 | from os import sep 6 | import ssl 7 | 8 | #For localhost without certificates properly set up 9 | #SSL: CERTIFICATE_VERIFY_FAILED urllib.request.urlopen 10 | #https://www.python.org/dev/peps/pep-0476/ 11 | try: 12 | _create_unverified_https_context = ssl._create_unverified_context 13 | except AttributeError: 14 | # Legacy Python that doesn't verify HTTPS certificates by default 15 | pass 16 | else: 17 | # Handle target environment that doesn't support HTTPS verification 18 | ssl._create_default_https_context = _create_unverified_https_context 19 | 20 | #WebUI Settings 21 | listen_port = 8082 22 | server_address = "127.0.0.1" 23 | 24 | #i2pd I2PControl port. https is mandatory by i2pcontrol 25 | i2pcontrol_url = "https://127.0.0.1:7650/" 26 | 27 | #Alternate solution SSL: CERTIFICATE_VERIFY_FAILED 28 | #gcontext = ssl._create_unverified_context() 29 | gcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1) 30 | urllib.request.HTTPSHandler(context=gcontext) 31 | 32 | __location__ = os.path.realpath( 33 | os.path.join(os.getcwd(), os.path.dirname(__file__))) 34 | 35 | #Fix Chrome can not load page at all 36 | #Fix ERR_INVALID_HTTP_RESPONSE when load css,js,ico files 37 | #Fix css was ignored due to mime type mismatch 38 | def get_header_type(file_path): 39 | file_types_pairs = [(".js","application/javascript"),(".css","text/css"), 40 | (".ico","image/x-icon")] 41 | file_dict = dict(file_types_pairs) 42 | filename, file_extension = os.path.splitext(file_path) 43 | #print(file_extension) 44 | if file_extension in file_dict: 45 | return file_dict[file_extension] 46 | return "text/html" 47 | 48 | def resp_html(s): 49 | """Response to GET requests with actual documents""" 50 | legal_files = ["/js/lib/underscore-min.js", "/js/app.js", "/js/functions.js", 51 | "/css/main.css","/favicon.ico"] 52 | 53 | if s.path == "/": 54 | s.send_response(200) 55 | s.send_header("Content-Type", "text/html") 56 | s.end_headers() 57 | with open(os.path.join(__location__, "index.html"), 'rb') as f: 58 | s.wfile.write(f.read()) 59 | elif s.path in legal_files: 60 | file_path = s.path[1:].replace("/", sep) 61 | with open(os.path.join(__location__, file_path), 'rb') as f: 62 | s.send_response(200) 63 | s.send_header("Content-Type", get_header_type(file_path)) 64 | s.end_headers() 65 | s.wfile.write(f.read()) 66 | else: 67 | s.send_error(404, "Not Found") 68 | print("404 Not Found") 69 | 70 | def proxy_request(data, s): 71 | """Send data to i2pcontrol port and return response""" 72 | 73 | req = urllib.request.Request(i2pcontrol_url, data) 74 | print(("--> sending to i2pcontrol", data)) 75 | try: 76 | response = urllib.request.urlopen(req) 77 | s.send_response(200) 78 | s.send_header("Content-Type", "application/json") 79 | s.end_headers() 80 | resp = response.read() 81 | print(("<-- recieved from i2pcontrol", resp)) 82 | s.wfile.write(resp) 83 | except urllib.error.URLError as e: 84 | print(e) 85 | s.send_error(500, "Cannot connect to I2PControl port" + e) 86 | 87 | class MyHandler(http.server.BaseHTTPRequestHandler): 88 | def do_GET(s): 89 | resp_html(s) 90 | 91 | def do_POST(s): 92 | content_length = int(s.headers['Content-Length']) 93 | post_data = s.rfile.read(content_length) 94 | proxy_request(post_data, s) 95 | 96 | if __name__ == "__main__": 97 | httpd = http.server.HTTPServer((server_address, listen_port), MyHandler) 98 | try: 99 | print(("WebUI is listening at http://"+ server_address +":" + str(listen_port))) 100 | httpd.serve_forever() 101 | except KeyboardInterrupt: 102 | pass 103 | 104 | httpd.server_close() 105 | --------------------------------------------------------------------------------