├── LICENSE ├── README.md ├── chartjs └── chart.bundle.min.js ├── content.json ├── css ├── Animation.css ├── Left.css ├── Menu.css ├── MuteList.css ├── PageFiles.css ├── PageStats.css ├── Right.css ├── Selectbar.css ├── ZeroHello.css ├── all.css ├── dark.css ├── fonts.css ├── icons.css └── mobile.css ├── img ├── loading-circle.gif ├── logo.png ├── logo.svg ├── logo_big.png └── world.png ├── index.html ├── js ├── Head.coffee ├── PageFiles │ ├── Bigfiles.coffee │ ├── FilesResult.coffee │ ├── PageFiles.coffee │ └── SiteFiles.coffee ├── PageSites │ ├── Dashboard.coffee │ ├── FeedList.coffee │ ├── MuteList.coffee │ ├── Site.coffee │ ├── SiteList.coffee │ └── Trigger.coffee ├── PageStats │ ├── Chart.coffee │ ├── ChartBig.coffee │ ├── ChartLegend.coffee │ ├── ChartRadar.coffee │ ├── ChartTimeline.coffee │ ├── ChartWorld.coffee │ ├── PageStats.coffee │ └── StatList.coffee ├── ZeroHello.coffee ├── all.js ├── lib │ ├── Class.coffee │ ├── Promise.coffee │ ├── Property.coffee │ ├── Prototypes.coffee │ ├── maquette.js │ └── marked.min.js └── utils │ ├── Animation.coffee │ ├── Dollar.coffee │ ├── ItemList.coffee │ ├── Menu.coffee │ ├── Prototypes.coffee │ ├── RateLimit.coffee │ ├── RateLimitCb.coffee │ ├── Text.coffee │ ├── Time.coffee │ ├── Translate.coffee │ └── ZeroFrame.coffee ├── languages ├── da.json ├── es.json ├── fa.json ├── fr.json ├── hu.json ├── it.json ├── jp.json ├── nl.json ├── pl.json ├── pt-br.json ├── pt.json ├── ru.json ├── sk.json ├── sl.json ├── tr.json ├── uk.json ├── zh-tw.json └── zh.json └── template-new ├── content.json-default ├── index.html └── js └── ZeroFrame.js /README.md: -------------------------------------------------------------------------------- 1 | # ZeroHello 2 | 3 | Homepage and visited site manager for [ZeroNet](https://github.com/HelloZeroNet/ZeroNet). 4 | 5 | ZeroNet address: http://127.0.0.1:43110/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D 6 | 7 | ## Screenshot 8 | 9 | ![Screenshot](http://i.imgur.com/H60OAHY.png) 10 | -------------------------------------------------------------------------------- /content.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D", 3 | "background-color": "#F2F4F6", 4 | "background-color-dark": "#383F46", 5 | "description": "New ZeroHello", 6 | "files": { 7 | "chartjs/chart.bundle.min.js": { 8 | "sha512": "f8b84bec9157a76d7fa620998264edf84d7efe94ce472f6c3c60268346d4081a", 9 | "size": 208221 10 | }, 11 | "css/all.css": { 12 | "sha512": "d5ae233bf85e908715eb301c2ed569fa1a0903cc08522d67d49ebda3e40ad4e6", 13 | "size": 235714 14 | }, 15 | "img/loading-circle.gif": { 16 | "sha512": "339baf1bccb9b80ae29c2493e73b40cf0588c96fc095954b475dea0538aaa929", 17 | "size": 2346 18 | }, 19 | "img/logo.png": { 20 | "sha512": "81884a41736056c54274a7332f233b872fdf9e298b6dda91d655cc7105221682", 21 | "size": 11379 22 | }, 23 | "img/logo.svg": { 24 | "sha512": "ff770c076d196585cfa08d77bf0299c2c48a71b2ba61a84bc9ae0062cabab6d4", 25 | "size": 1001 26 | }, 27 | "img/logo_big.png": { 28 | "sha512": "81884a41736056c54274a7332f233b872fdf9e298b6dda91d655cc7105221682", 29 | "size": 11379 30 | }, 31 | "img/world.png": { 32 | "sha512": "2741b5107146c26ec6ff4de4f5b2219c848cbd7405e03718c2dca2b2c0acc15f", 33 | "size": 13735 34 | }, 35 | "index.html": { 36 | "sha512": "09a6f500faf2fc962e493505d627d5df9259cb1427c3afd4537cdf1ab9741d6a", 37 | "size": 1330 38 | }, 39 | "js/all.js": { 40 | "sha512": "62a8401380dc1095862622f6a39be910d330181e65ce3defb8c41d8a5613b37e", 41 | "size": 294251 42 | }, 43 | "languages/da.json": { 44 | "sha512": "a11d1da29b791c2de8a926d5c9177011dc00ab569a2b1fc8b435e5615658827f", 45 | "size": 4011 46 | }, 47 | "languages/es.json": { 48 | "sha512": "1281c97a6319d13aa0896cbadc8d5c33c465b54fd8b7701515ceefc64a1ebb0e", 49 | "size": 4354 50 | }, 51 | "languages/fr.json": { 52 | "sha512": "d1aae78afcdc6a93d3923abcb2f6d4738570452d8f79beea33b8476179b07579", 53 | "size": 4354 54 | }, 55 | "languages/hu.json": { 56 | "sha512": "2d9a53a395a590273c8a20dfb2bc2d4db74aadbcae6dcf5a8cf1872ead9b25b8", 57 | "size": 4454 58 | }, 59 | "languages/it.json": { 60 | "sha512": "a536f8757cc7787f2c5dfb94b18532ba1aee9adc851b97a4bfc372e40063aaba", 61 | "size": 4665 62 | }, 63 | "languages/jp.json": { 64 | "sha512": "ad621ae4090da223abbca967c87174d13f3819e9dabc193061e6b9fccd8575b6", 65 | "size": 4737 66 | }, 67 | "languages/nl.json": { 68 | "sha512": "49ce3f99d856bb5ff4c495676c9bfc70de9343e6c1a4d61e3c0195f6835ff1dd", 69 | "size": 4553 70 | }, 71 | "languages/pl.json": { 72 | "sha512": "7af9a32eb1cc223b3065fff2591c99a44f33a38fd035c75737108ffb524e62b8", 73 | "size": 4718 74 | }, 75 | "languages/pt-br.json": { 76 | "sha512": "07c549e2da7f6a793930bf2d0f98ca6c02d441872c7f911fd3efbb0dce23ea9e", 77 | "size": 8919 78 | }, 79 | "languages/pt.json": { 80 | "sha512": "3c2e80fc40cca01f289050337c022d74eb28b8b8b5c0bcd8261ba132d974b3d5", 81 | "size": 4322 82 | }, 83 | "languages/ru.json": { 84 | "sha512": "e6b9c5f1c465d9d1f514cc2f245c09a640bd680c41a24001ac9d79756366c121", 85 | "size": 5521 86 | }, 87 | "languages/sk.json": { 88 | "sha512": "e443c8696ec967fe3fa8facb73d30a3c19f0fb8845765fd0169d7cf2fa1425f8", 89 | "size": 6385 90 | }, 91 | "languages/sl.json": { 92 | "sha512": "722398f7c26146a1ac79830b21c9633aacfd3319dcbe009feab4a1915c5ebe2f", 93 | "size": 4266 94 | }, 95 | "languages/tr.json": { 96 | "sha512": "7b6a8eb77e686f2228f94198eb1aad95b87e79987ffa455c4e88ed7e34777f8f", 97 | "size": 4371 98 | }, 99 | "languages/uk.json": { 100 | "sha512": "26b2b1a0a14cc9277335cc054b2aa1082407cc15d8d25c91381d51c75cdba04d", 101 | "size": 7738 102 | }, 103 | "languages/zh-tw.json": { 104 | "sha512": "6baee6b1b7040ae07966d8546de62285331d94c3540a165a70dedf2abc52608a", 105 | "size": 6746 106 | }, 107 | "languages/zh.json": { 108 | "sha512": "13885bc1b1c368788ee7d311f21b75e667329cd5e64142a12eb6c3eb4c8ffe83", 109 | "size": 9117 110 | }, 111 | "template-new/content.json-default": { 112 | "sha512": "7e6068f0105c5f6a0e1237baa1dfbbb353b40042c7ccedc306c6f88a446469aa", 113 | "size": 474 114 | }, 115 | "template-new/index.html": { 116 | "sha512": "542f7724432a22ceb8821b4241af4d36cfd81e101b72d425c6c59e148856537e", 117 | "size": 1114 118 | }, 119 | "template-new/js/ZeroFrame.js": { 120 | "sha512": "ace368eb399ec01f0a1da6e16c8a8b4d1b2e0a432a6f01778c446a5134db0e9c", 121 | "size": 3772 122 | } 123 | }, 124 | "ignore": "(js|css)/(?!all.(js|css))", 125 | "inner_path": "content.json", 126 | "modified": 1568919401, 127 | "postmessage_nonce_security": true, 128 | "signers_sign": "HLcq242ZHh4nTexhe6kvkBroycZ1JpF4pjlLGxbhjKAwDAfdCZ/gxUwM9aIN6OrD8K5YqAfvIVlbwkLMB1XSEDo=", 129 | "signs": { 130 | "1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D": "HFUeuP7s0xwrJHF5o9rDkttqBQPeIHwkq90W9xsUeS6wUzR0he9LBuoysQVO4Wr/jJrv+EX0RhFfTVfoQxQH2OE=" 131 | }, 132 | "signs_required": 1, 133 | "title": "ZeroHello", 134 | "translate": ["js/all.js"], 135 | "viewport": "width=device-width, initial-scale=0.8", 136 | "zeronet_version": "0.7.1" 137 | } -------------------------------------------------------------------------------- /css/Animation.css: -------------------------------------------------------------------------------- 1 | .animate { transition: all 0.3s ease-out !important; } 2 | .animate-back { transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; } 3 | .animate-inout { transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; } 4 | 5 | .cursor { color: #999; animation: pulse 1.5s infinite ease-in-out; } 6 | @keyframes pulse { 7 | 0% { opacity: 0 } 8 | 5% { opacity: 1 } 9 | 30% { opacity: 1 } 10 | 70% { opacity: 0 } 11 | 100% { opacity: 0 } 12 | } 13 | 14 | .bounce { animation: bounce .3s infinite alternate ease-out; } 15 | @keyframes bounce { 16 | 0% { transform: translateY(0); opacity: 1 } 17 | 100% { transform: translateY(-3px); opacity: 0.7 } 18 | } 19 | 20 | 21 | .loader { 22 | width: 100px; 23 | height: 100px; 24 | transform: scale(0.4); 25 | position: absolute; 26 | right: -73px; 27 | top: -30px; 28 | } 29 | .loader .arc { 30 | position: relative; 31 | margin: 25% 0 0 25%; 32 | width: 50%; 33 | height: 50%; 34 | border: 4px solid #B475EA; 35 | border-radius: 50%; 36 | -webkit-animation: rotate 2.9s infinite linear; 37 | animation: rotate 2.9s infinite linear; 38 | } 39 | .loader .arc::before, .loader .arc::after { 40 | content: ''; 41 | position: absolute; 42 | top: -5px; 43 | width: 33px; 44 | height: 62px; 45 | background-color: #FFF; 46 | -webkit-animation: rotate 2s infinite ease; 47 | animation: rotate 2s infinite ease; 48 | } 49 | .loader .arc::before { 50 | left: -6px; 51 | -webkit-transform-origin: 29px 29px; 52 | -ms-transform-origin: 29px 29px; 53 | transform-origin: 29px 29px; 54 | } 55 | .loader .arc::after { 56 | left: 27px; 57 | -webkit-transform-origin: 0 29px; 58 | -ms-transform-origin: 0 29px; 59 | transform-origin: 0 29px; 60 | -webkit-animation-delay: 0.5s; 61 | animation-delay: 0.5s; 62 | } 63 | 64 | 65 | @-webkit-keyframes rotate { 66 | 100% { 67 | -webkit-transform: rotate(360deg); 68 | transform: rotate(360deg); 69 | } 70 | } 71 | 72 | @keyframes rotate { 73 | 100% { 74 | -webkit-transform: rotate(360deg); 75 | transform: rotate(360deg); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /css/Left.css: -------------------------------------------------------------------------------- 1 | .left { 2 | width: 400px; position: absolute; box-sizing: border-box; min-height: 100%; padding-top: 0px; z-index: 1; backface-visibility: hidden; 3 | color: white; background-color: #232A31; background-image: linear-gradient(352deg, #322641, #2A333C); /* linear-gradient(352deg, #232A31, #2A333C);*/ 4 | } 5 | body:after { 6 | width: 400px; position: fixed; height: 100%; background-color: #322641; 7 | display: block; z-index: -1; left: 0px; top: 0px; content: ""; 8 | } 9 | .left h2 { text-transform: uppercase; font-size: 13px; font-weight: 500; padding: 10px; padding-left: 45px; letter-spacing: 1px; } 10 | .left .logo { background-color: #1E2429; padding: 20px; margin-bottom: 30px; display: block; text-decoration: none; color: white; transition: all 0.3s } 11 | .left .logo span { vertical-align: 39%; font-size: 27px; font-family: 'Text Me One'; } 12 | .left .logo img { padding: 4px; padding-left: 13px; } 13 | 14 | .left #Head { } 15 | .left #Head .settings { position: absolute; color: #8A91A2; right: 0px; font-size: 35px; text-decoration: none; padding: 24px; transition: all 0.3s } 16 | .left #Head .settings:hover { color: #FFF; transition: none } 17 | .left #Head .menu { left: 98%; top: 72px; white-space: nowrap; } 18 | .left #Head .modes { padding-left: 29px; } 19 | .left #Head .modes .mode { 20 | padding: 5px; text-transform: uppercase; letter-spacing: 2px; margin: 10px; background-color: rgba(0,0,0,0); outline: 5px solid rgba(0,0,0,0); 21 | font-size: 80%; text-decoration: none; color: white; opacity: 0.5; transition: all 0.3s 22 | } 23 | .left #Head .modes .mode:hover { opacity: 1; transition: none } 24 | .left #Head .modes .mode:active { background-color: rgba(133, 239, 255, 0.09); outline: 5px solid rgba(133, 239, 255, 0.09); transition: none } 25 | .left #Head .modes .mode:focus { transition: all 0.3s } 26 | .left #Head .modes .mode.active { font-size: 90%; border-bottom: 1px solid #83EFFF; color: #83EFFF; opacity: 1 } 27 | 28 | .left .site-filter { 29 | margin-left: 33px; width: 85%; background-color: #1e2429; border: 1px solid #3d444b; margin-bottom: 30px; 30 | padding: 13px; box-sizing: border-box; color: white; transition: all 0.3s; font-family: Roboto; font-size: 16px; 31 | } 32 | .left .site-filter:focus { border-color: #6c7884; outline: none } 33 | .left .filter-num { 34 | padding-right: 34px; font-weight: bold; box-sizing: border-box; font-size: 11px; text-transform: uppercase; color: #7E8186; 35 | pointer-events: none; width: 300px; display: inline-block; margin-left: -300px; text-align: right; vertical-align: 2px; 36 | } 37 | .left .filter-clear { color: white; text-decoration: none; margin-left: -41px; opacity: 0.5; padding: 16px; } 38 | .left .filter-clear:hover { opacity: 1; } 39 | 40 | .SiteSection .section-title { display: inline-block; width: 100%; opacity: 0.4; } 41 | .SiteSection .section-title:active { opacity: 0.8; transition: none; } 42 | .SiteSection .section-title:focus { transition: all 0.3s; } 43 | .SiteSection .section-title:hover .hide-title { text-decoration: underline; } 44 | .SiteSection .section-title .hide-title { 45 | font-size: 9px; float: right; line-height: 15px; margin-right: 20px; 46 | opacity: 0; pointer-events: none; transition: all 0.5s; transform: TranslateY(5px); 47 | } 48 | .SiteSection.hidden .section-title .hide-title { opacity: 1; pointer-events: all; transform: TranslateY(0px); } 49 | .SiteSection { transition: all 0.3s ease-in-out; } 50 | .SiteList.hidden { margin-bottom: 0px; } 51 | 52 | .SiteList { margin-bottom: 40px; } 53 | .SiteList.hidden { margin-bottom: 40px; } 54 | .SiteList .site { border-bottom: 1px solid #3D444B; position: relative; white-space: nowrap; } 55 | .SiteList .site.disabled:hover { background-color: rgba(173,136,183,0.04); } 56 | .SiteList .site.disabled .circle { opacity: 0.3 } 57 | .SiteList .site.disabled .inner { color: white; opacity: 0.3 } 58 | .SiteList .site:last-child { border-bottom: none } 59 | .SiteList .site .circle { pointer-events: none; position: absolute; margin-top: 17px; margin-left: 27px; font-size: 12px; font-family: Verdana; } 60 | .SiteList .site .inner { color: #C2CBDB; text-decoration: none; display: block; padding: 15px 25px; padding-left: 45px; transition: all 0.3s; white-space: normal; } 61 | .SiteList .site .inner:hover { background-color: rgba(173,136,183,0.08); transition: none; color: white } 62 | .SiteList .site .inner:active, .SiteList .site .inner:focus { background-color: rgba(173,136,183,0.2); transition: none; color: white; outline: none } 63 | .SiteList .site .inner .title { max-width: 157px; overflow: hidden; display: inline-block; white-space: nowrap; text-overflow: ellipsis; margin-bottom: -4px } 64 | .SiteList .site .inner .details { 65 | float: right; display: inline; font-size: 11px; text-transform: uppercase; line-height: 23px; 66 | opacity: 0.8; background-color: #17161F; padding: 2px 10px; margin-top: -4px; border-radius: 16px; 67 | } 68 | .SiteList .site .inner .details .modified { opacity: 0.6 } 69 | .SiteList .site.modified-lastday .inner .details .modified { opacity: 1 } 70 | .SiteList .site .settings { 71 | position: absolute; padding: 12px; right: 0px; top: -1px; color: white; text-decoration: none; opacity: 0; font-size: 22px 72 | } 73 | .SiteList .site .message { 74 | border-radius: 20px; position: absolute; top: 13px; font-size: 11px; opacity: 0; transition: all 0.3s ease-in-out; transition-delay: 0.1s; white-space: nowrap; 75 | text-transform: uppercase; padding: 5px 0px; background-color: #7DA1BF; color: white; max-width: 0px; overflow: hidden; height: 23px; box-sizing: border-box; /* af3bff */ 76 | display: inline-block; margin-left: 5px; z-index: 99; 77 | } 78 | 79 | .SiteList .site.working { background: transparent url(../img/loading-circle.gif) no-repeat 22px; backface-visibility: hidden; } 80 | .SiteList .site .message.done { background-color: #26c281 } 81 | .SiteList .site .message.error { background-color: #3C364F; color: #D5D4D9; } 82 | .SiteList .site .message.visible { padding: 5px 10px; opacity: 1; max-width: 140px; box-shadow: 7px 0px 15px #2F2A3F; } 83 | .SiteList .site:not(:hover) .message.collapsed { background-color: transparent; color: transparent; max-width: 15px; box-shadow: none } 84 | .SiteList .site .message.collapsed:before { 85 | content: "\01F514"; display: block; color: #99A0AF; margin-top: -2px; margin-left: -3px; position: absolute; 86 | transition: all 0.3s ease-in; transition-delay: 0.1s; font-family: Arial, Helvetica, 'Segoe UI Symbol', "Times", "Times New Roman", "serif", "sans-serif", "EmojiSymbols"; 87 | } 88 | .SiteList .site:hover .message.collapsed:before { opacity: 0; transform: scale(2); } 89 | 90 | .SiteList .site:hover .settings { opacity: 0.5; transition: none } 91 | .SiteList .site .settings:hover { opacity: 1; transition: none } 92 | .SiteList .menu { left: 99%; top: auto; white-space: nowrap; } 93 | .SiteList .menu-item { font-weight: lighter } 94 | 95 | .SiteList.more .site .settings { display: none } 96 | .SiteList.more .site .details { display: none } 97 | .SiteList.more .site .details.demo { display: inline; background-color: #8041f1; color: white; } 98 | 99 | .SiteList.needaction .site { background-color: rgba(255, 238, 59, 0.06) } 100 | .SiteList.needaction .site .details { display: none } 101 | .SiteList.needaction .site .details.needaction { 102 | display: inherit; color: black; text-decoration: none; background-color: #f1de3b; border-radius: 3px; border-bottom: 2px solid #b1a74b; opacity: 1; 103 | } 104 | .SiteList.needaction .site .details.needaction:hover { background-color: #fcff2b; transition: none !important } 105 | .SiteList.needaction .site .details.needaction:active { transform: translateY(1px); transition: none } 106 | 107 | .site-list-more { 108 | display: block; text-align: center; text-transform: uppercase; color: #AAA; padding-top: 15px; 109 | padding-bottom: 8px; font-size: 11px; box-shadow: inset 0px 17px 35px -15px #21212b; letter-spacing: 1px; 110 | } -------------------------------------------------------------------------------- /css/Menu.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | background-color: white; padding: 10px 0px; position: absolute; top: 0px; max-height: 0px; overflow: hidden; transform: translate(-100%, -30px); pointer-events: none; 3 | box-shadow: 0px 2px 8px rgba(0,0,0,0.3); border-radius: 2px; opacity: 0; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; z-index: 99; 4 | display: inline-block; z-index: 999; transform-style: preserve-3d; 5 | } 6 | .menu.menu-left { transform: translate(0%, -30px); } 7 | .menu.menu-left.visible { transform: translate(0%, 0px); } 8 | .menu.visible { 9 | opacity: 1; transform: translate(-100%, 0px); pointer-events: all; 10 | transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1); 11 | } 12 | 13 | .menu-item { 14 | display: block; text-decoration: none; color: black; padding: 6px 24px; transition: all 0.2s; border-bottom: none; font-weight: normal; 15 | max-height: 150px; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 6; -webkit-box-orient: vertical; display: -webkit-box; 16 | } 17 | .menu-item-separator { margin-top: 3px; margin-bottom: 3px; border-top: 1px solid #eee } 18 | 19 | .menu-item.noaction { cursor: default } 20 | .menu-item:hover:not(.noaction) { background-color: #F6F6F6; transition: none; color: inherit; cursor: pointer; color: black } 21 | .menu-item:active:not(.noaction), .menu-item:focus:not(.noaction) { background-color: #AF3BFF !important; color: white !important; transition: none } 22 | .menu-item.selected:before { 23 | content: "L"; display: inline-block; transform: rotateZ(45deg) scaleX(-1); 24 | font-weight: bold; position: absolute; margin-left: -14px; font-size: 12px; margin-top: 2px; 25 | } 26 | 27 | .menu-radio { white-space: normal; line-height: 26px } 28 | .menu-radio a { 29 | background-color: #EEE; width: 18.5%;; text-align: center; margin-top: 2px; margin-bottom: 2px; color: #666; font-weight: bold; 30 | text-decoration: none; font-size: 13px; transition: all 0.3s; text-transform: uppercase; display: inline-block; 31 | } 32 | .menu-radio a:hover, .menu-radio a.selected { transition: none; background-color: #AF3BFF !important; color: white !important } 33 | .menu-radio a.long { font-size: 10px; vertical-align: -1px; } 34 | -------------------------------------------------------------------------------- /css/MuteList.css: -------------------------------------------------------------------------------- 1 | #MuteList { 2 | position: absolute; z-index: 1; background-color: white; width: 100%; box-shadow: 0px 10px 25px 0px rgba(0,0,0,0.1); transform: rotateX(-42deg); transform-origin: top; 3 | padding-bottom: 0px; max-height: 0px; opacity: 1; overflow: hidden; transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1); white-space: nowrap; 4 | } 5 | #MuteList.visible { opacity: 1; padding-bottom: 60px; transform: rotateX(0deg) } 6 | 7 | .mute-empty { text-align: center; font-size: 15px; text-transform: uppercase; color: #AAA; font-weight: lighter; } 8 | 9 | .mute { padding: 10px 40px; border-top: 1px solid #EEE; padding-right: 60px; transition: all 0.3s } 10 | .mute.removed { opacity: 0.5; background-color: #EEE } 11 | .mute.mute-head { font-weight: lighter; border-top: none } 12 | .mute .mute-col { display: inline-block; width: 32%; max-width: 400px; margin-right: 10px; overflow: hidden; margin-right: 5px; vertical-align: top; white-space: normal; } 13 | .mute .cert_user_id { font-size: 18px; color: #2b333d; } 14 | .mute .auth_address { color: #99a3a7; font-size: 13px; font-family: monospace; overflow: hidden; padding-right: 25px; text-overflow: ellipsis; } 15 | .mute .reason { font-size: 13px; color: #99a3a7; display: inline; } 16 | .mute .reason p { display: inline } 17 | .mute .reason a { color: inherit; } 18 | .mute .date_added { font-size: 13px; display: inline; color: #99a3a7; font-weight: lighter; } 19 | 20 | #MuteList .action { 21 | display: inline-block; color: #999; font-weight: lighter; text-decoration: none; 22 | padding: 0px 20px; padding-bottom: 5px; 23 | } 24 | #MuteList .action .closer { 25 | backface-visibility: hidden; transform: rotateZ(0deg); transition: all 0.6s; font-size: 24px; display: inline-block; 26 | padding-bottom: 5px; padding-right: 3px; vertical-align: -2px; 27 | } 28 | #MuteList .removed .action .closer { transform: rotateZ(45deg); } 29 | #MuteList .action:hover { color: red; transition: none } 30 | #MuteList .action:focus .closer { transition: all 0.6s } 31 | 32 | .mute-hide { color: #797e85; text-transform: uppercase; padding: 30px 26px; display: inline-block; text-decoration: none; letter-spacing: 2px; transition: all 0.3s } 33 | .mute-hide:hover { color: #000 } 34 | .mute-hide:hover { opacity: 1; transition: none } 35 | .mute-hide:active { background-color: #F0F0F0; outline: 5px #F0F0F0; transition: none } 36 | .mute-hide:focus { transition: all 0.3s } 37 | 38 | #MuteList .include { margin-top: 50px; padding-left: 40px; transition: all 0.3s } 39 | #MuteList .include.removed { opacity: 0.5; background-color: #EEE } 40 | #MuteList .include h2 { padding: 10px 0px; font-size: 24px; display: inline-block; } 41 | #MuteList .include h2:before { content: "\203A"; position: absolute; margin-left: -20px; transform: rotate(90deg) scaleY(0.8); } 42 | #MuteList .include h2 a { color: #333; text-decoration: none; } 43 | #MuteList .include h2 a:hover { border-bottom: 1px solid #EEE; } 44 | #MuteList .include h2 .inner_path { font-size: 19px; } 45 | #MuteList .include .mute { padding: 10px 0px; } 46 | #MuteList .include .action { vertical-align: 0px; } 47 | -------------------------------------------------------------------------------- /css/PageStats.css: -------------------------------------------------------------------------------- 1 | #PageStats { margin-top: 50px; max-width: 1400px; padding-left: 40px; padding-right: 30px !important; position: relative; } 2 | 3 | .intervals { margin-top: -69px; right: 44px; position: absolute; } 4 | .interval { 5 | text-decoration: none; color: #ffffff7d; text-transform: uppercase; margin-left: 20px; display: inline-block; padding: 3px 0px ; 6 | font-size: 14px; font-weight: lighter; border-bottom: 2px solid transparent; transition: all 0.3s 7 | } 8 | .interval:hover { color: #ffffffAA; border-bottom-color: #ffffffAA; transition: none } 9 | .interval.active { color: white; border-bottom-color: white; } 10 | 11 | .ChartTimeline { margin-bottom: 20px; backface-visibility: hidden; } 12 | .timeline-items { background: radial-gradient(closest-corner at 39% 36%, #aecfea21, #0000ff00); } 13 | .timeline-item, .timeline-border { 14 | display: inline-block; width: 14%; background-color: #a8acfb0d; margin-right: 1px; transition: all 0.3s; 15 | padding: 15px; padding-bottom: 100px; box-sizing: border-box; color: white; text-decoration: none; vertical-align: top; 16 | } 17 | .timeline-item:hover { background-color: #3f404d; transition: none; } 18 | .timeline-item.active { background-color: white; color: black; padding-bottom: 130px; margin-top: -30px; box-shadow: 30px 0px 95px -31px black; transition: all 0.3s !important } 19 | .timeline-item .title { text-transform: uppercase; font-size: 80%; opacity: 0.6; letter-spacing: 1px; } 20 | .timeline-item .data { font-size: 150%; display: block; margin-top: 15px; font-weight: lighter; } 21 | 22 | .ChartTimeline .chart { 23 | width: 100%; height: 100px; position: absolute; margin-bottom: -185px; 24 | z-index: 999; position: relative; padding-right: 20px; box-sizing: border-box; pointer-events: none; 25 | } 26 | 27 | .timeline-borders { height: 100px; height: 180px; margin-bottom: -201px; pointer-events: none; transform: translateX(1px); z-index: 1000; position: relative; overflow: hidden } 28 | .timeline-border { background-color: transparent; border-right: 1px solid #1e2527; height: 185px } 29 | .timeline-border.active { box-shadow: 5px 108px 144px 0px #000000CC; } 30 | .timeline-border:last-child.active { box-shadow: -45px 108px 100px -37px #000000CC } 31 | 32 | .ChartLegend { text-align: right; margin-right: 20px; margin-bottom: 40px } 33 | .legend-items { width: 40%; display: inline-block; box-sizing: border-box } 34 | .legend-items.align-left { text-align: left; padding-left: 100px; width: 60% } 35 | .legend-item { display: inline-block; min-width: 30%; font-weight: lighter; line-height: 170%; margin-top: 50px; margin-right: 8px; opacity: 1; transition: all 0.3s } 36 | .legend-item.hidden { opacity: 0 } 37 | .legend-item .title { text-transform: uppercase; color: #9f9fb7; font-size: 90%; letter-spacing: 1px; font-weight: normal; } 38 | .legend-item .value { font-size: 120% } 39 | .legend-item .dot { vertical-align: 1px; } 40 | .legend-item .dots-container { display: inline-block; margin-left: 10px; text-align: left; } 41 | .legend-item .dots-fg { color: #16ffe9; display: inline-block; overflow: hidden; position: absolute; transition: all 0.3s } 42 | .legend-item .dots-bg { color: #777; } 43 | 44 | .ChartRadar { width: 50%; box-sizing: border-box; display: inline-block; vertical-align: top } 45 | .ChartRadar .canvas-container { padding: 15%; } 46 | .ChartRadar .radar-container { position: relative; } 47 | .radar-labels { width: 100%; height: 100%; position: absolute; top: 0px; } 48 | .radar-label { position: absolute; transform: translateY(-50%); width: 100px; margin-left: -50px; text-align: center; } 49 | .radar-label .title { text-decoration: none; color: white; } 50 | .radar-label .title:hover { text-decoration: underline } 51 | .radar-label .value { font-size: 80%; opacity: 0.5; white-space: nowrap; } 52 | 53 | .radar-legends { text-align: center } 54 | .radar-legend { 55 | display: inline-block; margin: 0px 16px; font-size: 73%; text-transform: uppercase; 56 | letter-spacing: 1px; color: #FFFFFFDD; padding: 7px 12px; transition: all 0.3s; text-decoration: none 57 | } 58 | .radar-legend:hover { transition: none; background-color: #b485bf2e; } 59 | .radar-legend .title { text-decoration: none; color: white } 60 | .radar-legend.active { background-color: #b485bf1e; } 61 | .radar-legend .legend-box { display: inline-block; width: 30px; height: 8px; margin-right: 6px; } 62 | 63 | .Charts { width: 50%; display: inline-block; box-sizing: border-box; padding: 1.5%; margin-top: 47px; } 64 | .Chart { margin-bottom: 20px; position: relative; background-image: radial-gradient(at 29% top, #eaaeda05, #cc00ff0a) } 65 | .Chart .canvas { width: 100%; display: block; } 66 | .Chart .titles { position: absolute; padding: 4%; height: 100%; box-sizing: border-box; width: 100%; } 67 | .Chart .titles a { color: white; text-decoration: none } 68 | .Chart .titles .title { font-weight: lighter; text-transform: uppercase; opacity: 0.8; letter-spacing: 1px; } 69 | .Chart .titles .value { font-size: 140%; font-weight: lighter; margin: 4px 0px; text-transform: uppercase; } 70 | .Chart .titles .details { bottom: 6%; position: absolute; color: #16ffe9; line-height: 23px; font-size: 12px; font-family: monospace } 71 | 72 | .ChartWorld { margin-top: 110px; position: relative; width: 65%; display: inline-block; vertical-align: top } 73 | .ChartWorld .map { opacity: 0.4; width: 100%; } 74 | .ChartWorld .map-points { position: absolute; width: 100%; z-index: 999; } 75 | /*.ChartWorld .map-point { 76 | position: absolute; width: 3px; height: 3px; background-color: #30758e; 77 | margin-left: -9px; margin-top: -9px; mix-blend-mode: screen; 78 | }*/ 79 | 80 | .StatList { width: 35%; display: inline-block; margin-top: 55px; padding: 1.5%; box-sizing: border-box; } 81 | .StatList h4 { text-transform: uppercase; color: #9f9fb7; font-size: 90%; letter-spacing: 1px; font-weight: normal; margin-bottom: 20px } 82 | .stat-list-item { clear: both; font-size: 90%; font-weight: lighter; line-height: 170%; } 83 | .stat-list-item .title { float: left } 84 | .stat-list-item .value { float: right; opacity: 0.8 } 85 | .stat-list-item.other { opacity: 0.5 } 86 | -------------------------------------------------------------------------------- /css/Selectbar.css: -------------------------------------------------------------------------------- 1 | .selectbar.visible { margin-top: 0px; visibility: visible } 2 | .selectbar { 3 | position: fixed; top: 0; background-color: white; box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); margin-top: -75px; transition: all 0.3s; visibility: hidden; 4 | z-index: 9999; color: black; border-left: 5px solid #af3bff; width: 100%; padding: 22px; font-size: 20px; font-weight: lighter; backface-visibility: hidden; 5 | } 6 | 7 | .selectbar .num { margin-left: 15px; min-width: 70px; text-align: right; display: inline-block; } 8 | .selectbar .size { margin-left: 10px; color: #9f9ba2; min-width: 110px; display: inline-block; } 9 | .selectbar .actions { display: inline-block; margin-left: 20px; font-size: 15px; text-transform: uppercase; line-height: 20px; } 10 | .selectbar .action { padding: 5px 20px; border: 1px solid #edd4ff; margin-left: 10px; border-radius: 30px; color: #af3bff; text-decoration: none; transition: all 0.3s } 11 | .selectbar .action:hover { border-color: #c788f3; transition: none; color: #9700ff } 12 | .selectbar .delete { color: #AAA; border-color: #DDD; } 13 | .selectbar .delete:hover { color: #333; border-color: #AAA } 14 | .selectbar .action:active { background-color: #af3bff; color: white; border-color: #af3bff; transition: none } 15 | .selectbar .cancel { margin: 20px; font-size: 12px; text-decoration: none; color: #999; text-transform: uppercase; } 16 | .selectbar .cancel:hover { color: #333; transition: none } -------------------------------------------------------------------------------- /css/ZeroHello.css: -------------------------------------------------------------------------------- 1 | body { background-color: #EDF2F5; font-family: Roboto, 'Segoe UI', Arial, 'Helvetica Neue'; margin: 0px; padding: 0px; backface-visibility: hidden; height: 100%; position: absolute; width: 100%; overflow-x: hidden; height: 15000px; -webkit-font-smoothing: subpixel-antialiased; overscroll-behavior-y: none; } 2 | body.loaded { height: 100%; overflow: auto } 3 | h1, h2, h3, h4 { font-family: 'Roboto', Arial, sans-serif; font-weight: 200; font-size: 30px; margin: 0px; padding: 0px } 4 | h1 { font-family: 'Text Me One', sans-serif } 5 | h3 a, h2 a { color: white; text-decoration: none; } 6 | a { color: #9760F9 } 7 | a:hover { text-decoration: none } 8 | input::placeholder { color: rgba(255, 255, 255, 0.3) } 9 | 10 | .link { background-color: transparent; outline: 5px solid transparent; transition: all 0.3s } 11 | .link:active { background-color: #EFEFEF; outline: 5px solid #EFEFEF; transition: none } 12 | 13 | .servedby { background-color: #AF3BFF; display: inline-block; padding: 10px; margin-left: -25px; color: white } 14 | .head, .topright { display: none } 15 | 16 | .emoji { font-family: "Segoe UI Symbol", "Symbola", "EmojiSymbols"; } 17 | 18 | /* Bottom */ 19 | 20 | .bottom { 21 | position: relative; z-index: 2; bottom: 0px; color: #C2CBDB; padding: 25px; margin-top: -65px; height: 65px; box-sizing: border-box; 22 | font-size: 11px; text-transform: uppercase; opacity: 0.6; font-family: monospace; overflow: hidden; transition: 0.3s all; 23 | } 24 | .bottom:hover { opacity: 0.9 } 25 | .bottom a { color: #C2CBDB; transition: 0.3s all; padding: 5px; margin: 2px } 26 | .bottom a:hover { background-color: #413254; transition: none; color: white } 27 | 28 | .heart { color: #af3bff; font-family: Verdana; } 29 | 30 | /* Pages */ 31 | 32 | #SiteList { transition: all 0.3s; height: 100%; float: left; padding-bottom: 230px; margin-top: 30px; } 33 | #SiteList .SiteList { width: 400px } 34 | #SiteList .details { transition: all 0.6s } 35 | #FeedList { transition: all 0.3s; height: 100%; } 36 | .PageFiles-container, .PageStats-container { width: 0px; float: left; } 37 | #PageFiles, #PageStats { transition: all 0.3s; opacity: 1; width: 100%; width: 100vw; transform: none; max-height: 100%; padding-right: 10px; box-sizing: border-box; padding-bottom: 50px; } 38 | #PageFiles .files { transition: all 0.3s; opacity: 1; transform: none } 39 | .left { transition: 0.6s all cubic-bezier(0.785, 0.135, 0.15, 0.86), z-index 0s; overflow: hidden; } 40 | body.changing { overflow-y: scroll; } 41 | 42 | /* PageSites */ 43 | body.changing .left { z-index: 1 !important; } 44 | #BodySites .left { z-index: 0; } 45 | #BodySites #PageStats, #BodyFiles #PageStats, body.changing #PageStats { opacity: 0; transform: translateY(30px); pointer-events: none; overflow: hidden; } 46 | #BodySites #PageFiles, #BodyStats #PageFiles, body.changing #PageFiles { opacity: 0; transform: translateY(30px); pointer-events: none; overflow: hidden; } 47 | #BodySites #PageFiles, #BodyStats #PageFiles { max-height: 0px } 48 | #BodySites #PageStats, #BodyFiles #PageStats { max-height: 0px } 49 | #BodySites #PageFiles .files, #BodyStats #PageFiles .files, body.changing #PageFiles .files { opacity: 0; transform: translateY(30px); pointer-events: none; } 50 | 51 | /* PageFiles */ 52 | #BodyFiles #SiteList, #BodyStats #SiteList { opacity: 0; visibility: hidden; transform: translateX(-20px); height: 0px; pointer-events: none; } 53 | #BodyFiles #SiteList .details, #BodyStats #SiteList .details { transform: translateX(180px); } 54 | #BodyFiles #FeedList, #BodyStats #FeedList { visibility: hidden; height: 0px; pointer-events: none; overflow: hidden } 55 | #BodyFiles #Head .settings, #BodyStats #Head .settings { opacity: 0; visibility: hidden; overflow: hidden } 56 | #BodyFiles .left, #BodyStats .left { width: 100%; background-image: linear-gradient(220deg, #372d56 0px, #162761 2000px); } 57 | #BodyFiles .left .logo, #BodyStats .left .logo { background-color: #162150 } 58 | #BodyFiles .right, #BodyStats .right { height: 0px } 59 | 60 | /* PageStats */ 61 | #BodyStats .left { /*background-image: linear-gradient(60deg, #272a38 0px, #1E2527 1260px);*/ min-height: 1260px; background-image: linear-gradient(60deg, #442f40 0px, #12252b 1260px) } 62 | #BodyStats .left .logo { background-color: rgba(0,0,0,0) } 63 | -------------------------------------------------------------------------------- /css/dark.css: -------------------------------------------------------------------------------- 1 | .theme-dark { background-color: #383f46 } 2 | .theme-dark .right { border-top-color: #22272d; background-color: #252b33; } 3 | .theme-dark .feeds-line { border-right-color: #52585f } 4 | .theme-dark .FeedList .feed .site { color: #fff } 5 | .theme-dark .FeedList .feed .title { color: #fff } 6 | .theme-dark .FeedList .feed .circle { background-color: #383f46 } 7 | .theme-dark .FeedList .feed .more { border-top-color: #535d66; background: linear-gradient(#404643 0%, #30363c 50%); } 8 | .theme-dark .FeedList .feed .body { color: #dadada } 9 | .theme-dark .FeedList .feed .body b { color: #87e2ff; } 10 | .theme-dark .feeds-filter { color: #c6ced7 } 11 | .theme-dark #Dashboard .dashboard-item { background-color: #383f46; color: rgba(255, 255, 255, 0.6) } 12 | .theme-dark h2, .theme-dark h3 { color: #f5faff } 13 | .theme-dark .FeedList .feed.mention .type { background-color: #6c706d; outline: 3px solid #6c706d; color: #fffcdd; } 14 | .theme-dark .search-info-stats td, .theme-dark .search-info-stats th { border-bottom-color: #555f69; color: white; } 15 | .theme-dark .search-info-stats td a { color: #82d9f4 } 16 | .theme-dark .highlight { color: #444; } 17 | 18 | .theme-dark .menu { background-color: #383b40; outline: 1px solid #4a4a4a; } 19 | .theme-dark .menu-item { color: #eaf0f5; } 20 | .theme-dark .menu-item:not(.noaction):hover { background-color: #45444b; color: white; } 21 | .theme-dark .menu-item-separator { border-top: 1px solid #505050; } 22 | .theme-dark .menu-radio a { background-color: #414141; color: #b9b9b9; } 23 | 24 | @media screen and (max-width: 970px) { 25 | .theme-dark .FeedList .feed .details { border-top: 1px solid #4d565e; border-bottom: none; padding-top: 20px } 26 | .theme-dark .right { background-color: #383f46; } 27 | .theme-dark .FeedList .feed { background-color: transparent; padding-top: 0; } 28 | .theme-dark .FeedList .feed:first-child .details { border-top: 0px; } 29 | } 30 | -------------------------------------------------------------------------------- /css/icons.css: -------------------------------------------------------------------------------- 1 | /* CSS icons (base on: http://one-div.com/) */ 2 | 3 | .icon-profile { font-size: 6px; top: 0em; border-radius: 0.7em 0.7em 0 0; background: #FFFFFF; width: 1.4em; height: 0.5em; position: relative; display: inline-block; margin-right: 4px; margin-left: 5px; } 4 | .icon-profile::before { position: absolute; content: ""; top: -1em; left: 0.31em; width: 0.8em; height: 0.85em; border-radius: 50%; background: #FFFFFF; } 5 | 6 | .icon-clock { 7 | display: inline-block; width: 10px; height: 12px; vertical-align: -2px; margin-right: 4px; 8 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAMCAMAAABstdySAAAAOVBMVEUAAAD///////////////////////////////////////////////////////////////////////8KOjVvAAAAEnRSTlMADAIb8DMPt2MI6dTLx76nnREnEhipAAAATElEQVQI1yXLyREAMQgDQYHvvVf5B2sZz4cuKKD+Uhwru6maNDJNOx5Ao4GQP7B6MBF0BI37SMYWO+FZdqt8YZmhvF5PqmtAeek9aU7IwwMHq3GJrQAAAABJRU5ErkJggg==); 9 | } 10 | 11 | .icon-heart { position: absolute; width: 17px; height: 13px; margin-top: 5px; margin-left: -8px; transition: all 0.3s } 12 | .icon-heart:before, .icon-heart:after { 13 | position: absolute; content: ""; left: 8px; top: 0; width: 8px; height: 13px; transition: all 0.3s; 14 | background: #D1D3D5; /*#FA6C8D;*/ /*border-radius: 25px 25px 0 0;*/ transform: rotate(-45deg); transform-origin: 0 100% 15 | } 16 | .icon-heart:after { left: 0; transform: rotate(45deg); transform-origin :100% 100% } 17 | .icon-magnifier { 18 | position: absolute; display: inline-block; background: #fff; border-radius: 30px; height: 6px; width: 6px; border: 2px solid #AAA; 19 | margin-left: 14px; margin-top: 15px; pointer-events: none 20 | } 21 | .icon-magnifier:after { content: ""; height: 2px; width: 6px; background: #AAA; position: absolute; top: 7px; left: 5px; transform: rotate(45deg); } 22 | .icon-arrow-down { 23 | display: inline-block; width: 12px; height: 12px; vertical-align: -1px; margin-left: 12px; margin-right: 12px; 24 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAElBMVEUAAAD+/v7+/v7+/v7+/v7///9B/zSpAAAABXRSTlMAgoZ6QMOPz98AAAA2SURBVAjXY0AFLCDCgcFUgIGBMZhBNZCBQTSIgSlUgDFUgQHIBXKAEqGhQGmgDFAWxAVyUAEAy80Ep8MEvosAAAAASUVORK5CYII=') 25 | } 26 | .icon-share { 27 | display: inline-block; width: 16px; height: 16px; 28 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA1klEQVQ4y2NgIAH8u6Tq9u+Syk0g/g3EW4F8CaI1/72kJgbU9BmI1wJxAhDfBhlCgu0qAf8vqfz/fVmbB8oHGfKbWNslgIqXggwAOjsBZAjUJTeR/WcGsuXfJTU5JI18QLFmqNOfQjX9hxgEElN1AykSAnIO/0dI/AXiRqgzQRreA3HF30vqXFCL9EAWgcIE5reFQPwY5IJfl/U4gOysfxCnegGxEsgCQoEDcl4OmthBIJ5PbOhSbABlXqA4ECmORuK8SURCIiKsyE/KUO9Qlpmg4YSRnQHKZzFFYyXf8QAAAABJRU5ErkJggg==') 29 | } 30 | 31 | .icon-pencil { 32 | display: inline-block; width: 16px; height: 16px; background-repeat: no-repeat; 33 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAOVBMVEUAAAC9w8e9w8e9w8e9w8e/xMi9w8e9w8e+w8e9w8e9w8e9w8e9w8e9w8e9w8e+w8e/xMi9w8e9w8fvY4+KAAAAEnRSTlMASPv3WQbwOTCkt4/psX4YDMWr+RRCAAAAUUlEQVQY06XLORKAMAxDUTs7kA3d/7AYGju0UfffjIgoHkxm0vB5bZyxKHx9eX0FJw0Y4bcXKQ4/CTtS5yqp5GFFOjGpVGl00k1pNDIb3Nv9AHC7BOZC4ZjvAAAAAElFTkSuQmCC+d0ckOwyAMRVGHUOO0gUyd+P8f7WApz4Iki9wFmyOEATrXLZcFp5LrGogPOxKp6zfFf9fZ1/I/cY7YZSS3U6S3XFZJmGBwL+FuJX/F1K0wUUlZyZGlXgXESthTEs4B8fh7xoVUDPGYJnsfkCRarKAgz8cAKbpD6pqDPz3XB8K6HdUEeN9NAAAAAElFTkSuQmCC'); 34 | } 35 | .icon-mute { 36 | width: 7px; height: 6px; background: currentColor; margin: 7px 0 0 0px; position: relative; 37 | display: inline-block; margin-right: 17px; vertical-align: 2px; opacity: 0.8; line-height: 12px; 38 | } 39 | .icon-mute:after { 40 | content: ''; position: absolute; width: 0; height: 0; border-style: solid; border-color: transparent currentColor transparent transparent; 41 | border-width: 7px 8px 7px 12px; left: -11px; top: -4px; 42 | } 43 | .icon-mute:before {content: '\00D7'; position: absolute; margin-left: 11px; top: -3px; font-size: 11px; } 44 | .icon-gear { display: inline-block; width: 25px; } 45 | -------------------------------------------------------------------------------- /css/mobile.css: -------------------------------------------------------------------------------- 1 | #Trigger { 2 | display: none; 3 | } 4 | 5 | @media screen and (max-width: 970px) { 6 | #Dashboard { 7 | white-space: normal; 8 | text-align: right; 9 | } 10 | #Dashboard .dashboard-item { 11 | margin-top: 0px; 12 | padding: 8px 18px; 13 | } 14 | #Dashboard .dashboard-item.donate { 15 | padding: 8px 17px; 16 | } 17 | .feeds-filters { 18 | margin-top: 15px 19 | } 20 | #Dashboard .menu-item { 21 | text-align: left; 22 | overflow: hidden; 23 | text-overflow: ellipsis; 24 | display: block; 25 | } 26 | .right { 27 | width: calc(100% - 350px); 28 | left: 350px; 29 | background-color: #f7f8f9; 30 | } 31 | .left, .SiteList .site { 32 | width: 350px; 33 | } 34 | .left, .SiteList .site.working { 35 | background-position-x: 6px !important; 36 | } 37 | #BodySites .left { 38 | background: #2A333C; 39 | transition: 0.2s all cubic-bezier(0.77, 0, 0.175, 1); 40 | border-bottom: 1px solid #2A333C; 41 | } 42 | .left #Head .menu { 43 | font-size: 90%; 44 | line-height: 180%; 45 | } 46 | .left h2 { 47 | padding-left: 19px; 48 | padding-right: 50px; 49 | } 50 | .left .site-filter { 51 | margin-left: 6px; 52 | width: 83%; 53 | } 54 | .left #FilePage { 55 | height: auto; 56 | position: relative; 57 | top: 92px; 58 | bottom: 0px; 59 | padding-top: 40px; 60 | } 61 | .left #SiteList .details { 62 | top: 15px; 63 | position: absolute; 64 | right: 30px; 65 | } 66 | .SiteList .site .inner { 67 | padding-left: 29px; 68 | } 69 | .SiteList .site .inner .title { 70 | max-width: 140px; 71 | } 72 | .SiteList .site .circle { 73 | margin-left: 11px; 74 | } 75 | .FeedList { 76 | margin-top: 10px; 77 | } 78 | .feeds-line { 79 | border-right: none; 80 | } 81 | .FeedList .feed .circle { 82 | margin-left: 0px; 83 | margin-top: -29px; 84 | } 85 | .FeedList .feed { 86 | margin: 0px 0px 10px 0px; 87 | background: #fff; 88 | } 89 | .FeedList .feed, .feeds-search { 90 | padding-left: 20px; 91 | } 92 | .feeds-search { 93 | margin-top: 10px; 94 | margin-left: -8px; 95 | margin-right: 56px; 96 | } 97 | .FeedList .feed .details { 98 | position: relative; 99 | left: unset; 100 | width: 100%; 101 | text-align: left; 102 | padding-bottom: 5px; 103 | margin-bottom: 5px; 104 | border-bottom: 1px solid #eee; 105 | padding-left: 20px; 106 | box-sizing: border-box; 107 | } 108 | .FeedList .feed .added { 109 | float: right; 110 | margin-top: 5px; 111 | } 112 | #FilePage .files .tbody .td.inner_path { 113 | overflow: hidden; 114 | } 115 | #BodyFiles .left { 116 | position: absolute; 117 | } 118 | #BodyFiles .left, #FilePage { 119 | min-width: 800px; 120 | } 121 | #BodyFiles .right { 122 | display: none; 123 | } 124 | #BodySites .left #Head .modes { 125 | padding-left: 29px; 126 | background-color: #2a333c; 127 | box-shadow: 0px -8px 0px 22px #2a333c; 128 | } 129 | .SiteList .site .message { 130 | z-index: inherit; 131 | } 132 | .welcome .site { 133 | font-size: 35px; 134 | } 135 | 136 | .search-help { margin-left: 0px; } 137 | } 138 | 139 | @media screen and (max-width: 799px) { 140 | .left { 141 | position: fixed; 142 | } 143 | 144 | .right { 145 | left: 0px; 146 | width: 100%; 147 | margin-left: 0px; 148 | } 149 | #BodySites .left::-webkit-scrollbar { 150 | display: none; 151 | } 152 | #BodySites .left { 153 | left: -350px; 154 | z-index: 1; 155 | overflow-x: hidden; 156 | overflow-y: scroll; 157 | bottom: 0px; 158 | height: 100%; 159 | } 160 | #BodySites .left.trigger-on { 161 | left: 0px; 162 | } 163 | #BodySites #Trigger { 164 | display: block; 165 | position: fixed; 166 | left: 0px; 167 | bottom: 16px; 168 | z-index: 100; 169 | border-radius: 0px 40px 40px 0px; 170 | transition: transform 0.2s; 171 | transform: rotateZ(0deg); 172 | box-shadow: 0px 0px 15px #737d8c; 173 | background-color: rgba(255, 255, 255, 0.75); 174 | } 175 | #BodyFiles #Trigger, #BodyStats #Trigger { 176 | display: none; 177 | } 178 | #Trigger .icon { 179 | display: block; 180 | -webkit-tap-highlight-color: transparent; 181 | color: #1E2429; 182 | padding: 13px 20px; 183 | text-decoration: none; 184 | transition: transform 0.6s; 185 | font-size: 13px; 186 | outline: none !important; 187 | } 188 | #Trigger .icon:active { 189 | background-color: transparent; 190 | } 191 | #Trigger .icon .arrow-right { 192 | border: solid currentColor; 193 | border-width: 0 3px 3px 0; 194 | display: inline-block; 195 | padding: 3px; 196 | transform: rotate(-45deg); 197 | } 198 | #Trigger.active { 199 | transform: translateX(348px); 200 | background-color: #2a333c; 201 | box-shadow: none; 202 | } 203 | #Trigger.active .icon { 204 | transform: rotateZ(180deg); 205 | background-color: transparent; 206 | color: white; 207 | } 208 | #Dashboard .menu, #Dashboard .menu.visible { 209 | position: fixed; 210 | overflow-x: auto; 211 | max-width: 100%; 212 | left: 100%; 213 | top: -15px; 214 | font-size: 80%; 215 | line-height: 180%; 216 | transform: translate(-100%, -30px); 217 | white-space: nowrap; 218 | } 219 | .menu-item .emoji { 220 | display: none; 221 | } 222 | #PageFilesDashboard { margin-top: 40px; margin-bottom: 40px; } 223 | } 224 | 225 | @media screen and (max-width: 500px) { 226 | .welcome .site { 227 | width: 100%; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /img/loading-circle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroHello/dfef4a1bb0591106b3ecc0ebe1452bd3120b1f32/img/loading-circle.gif -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroHello/dfef4a1bb0591106b3ecc0ebe1452bd3120b1f32/img/logo.png -------------------------------------------------------------------------------- /img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /img/logo_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroHello/dfef4a1bb0591106b3ecc0ebe1452bd3120b1f32/img/logo_big.png -------------------------------------------------------------------------------- /img/world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ZeroHello/dfef4a1bb0591106b3ecc0ebe1452bd3120b1f32/img/world.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Welcome to ZeroNet! 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 | 36 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /js/Head.coffee: -------------------------------------------------------------------------------- 1 | class Head extends Class 2 | constructor: -> 3 | @menu_settings = new Menu() 4 | 5 | formatUpdateInfo: -> 6 | if parseFloat(Page.server_info.version.replace(".", "0")) < parseFloat(Page.latest_version.replace(".", "0")) 7 | return "New version available!" 8 | else 9 | return "Up to date!" 10 | 11 | handleLanguageClick: (e) => 12 | if Page.server_info.rev < 1750 13 | return Page.cmd "wrapperNotification", ["info", "You need ZeroNet 0.5.1 to change the interface's language"] 14 | lang = e.target.hash.replace("#", "") 15 | Page.cmd "configSet", ["language", lang], -> 16 | Page.server_info.language = lang 17 | top.location = "?Home" 18 | return false 19 | 20 | renderMenuLanguage: => 21 | langs = ["da", "de", "en", "es", "fa", "fr", "hu", "it", "nl", "pl", "pt", "pt-br", "ru", "sk", "tr", "uk", "zh", "zh-tw"] 22 | if Page.server_info.language and Page.server_info.language not in langs 23 | langs.push Page.server_info.language 24 | 25 | h("div.menu-radio", 26 | h("div", "Language: "), 27 | for lang in langs 28 | [ 29 | h("a", {href: "#"+lang, onclick: @handleLanguageClick, classes: {selected: Page.server_info.language == lang, long: lang.length > 2}}, lang), 30 | " " 31 | ] 32 | ) 33 | 34 | handleThemeClick: (e) => 35 | if Page.server_info.rev < 3670 36 | return Page.cmd "wrapperNotification", ["info", "You need ZeroNet 0.6.4 to change the interface's theme"] 37 | 38 | theme = e.target.hash.replace("#", "") 39 | 40 | if theme == "system" 41 | if Page.server_info.rev < 4085 42 | return Page.cmd "wrapperNotification", ["info", "You need ZeroNet 0.7.0 to use system's theme"] 43 | 44 | DARK = "(prefers-color-scheme: dark)" 45 | mqDark = window.matchMedia(DARK) 46 | 47 | Page.cmd "userGetGlobalSettings", [], (user_settings) -> 48 | if theme == "system" 49 | theme = if mqDark.matches then "dark" else "light" 50 | user_settings.use_system_theme = true 51 | else 52 | user_settings.use_system_theme = false 53 | 54 | user_settings.theme = theme 55 | 56 | Page.server_info.user_settings = user_settings 57 | document.getElementById("style-live").innerHTML = "* { transition: all 0.5s ease-in-out }" 58 | Page.cmd "userSetGlobalSettings", [user_settings] 59 | setTimeout ( -> 60 | document.body.className = document.body.className.replace(/theme-[a-z]+/, "") 61 | document.body.className += " theme-#{theme}" 62 | setTimeout ( -> 63 | document.getElementById("style-live").innerHTML = "" 64 | ), 1000 65 | ), 300 66 | 67 | return false 68 | 69 | renderMenuTheme: => 70 | themes = {"system": _("system"), "light": _("light"), "dark": _("dark")} 71 | 72 | if Page.server_info.user_settings.use_system_theme 73 | theme_selected = "system" 74 | else 75 | theme_selected = Page.server_info.user_settings?.theme 76 | if not theme_selected then theme_selected = "system" 77 | 78 | h("div.menu-radio.menu-themes", 79 | h("div", "Theme: "), 80 | for theme_id, theme_title of themes 81 | [ 82 | h("a", {href: "#" + theme_id, onclick: @handleThemeClick, classes: {selected: theme_selected == theme_id, long: true}}, theme_title), 83 | " " 84 | ] 85 | ) 86 | 87 | handleCreateSiteClick: => 88 | if Page.server_info.rev < 1770 89 | return Page.cmd "wrapperNotification", ["info", "You need to update your ZeroNet client to use this feature"] 90 | Page.cmd("siteClone", [Page.site_info.address, "template-new"]) 91 | 92 | handleBackupClick: => 93 | if Page.server_info.rev < 2165 94 | return Page.cmd "wrapperNotification", ["info", "You need to update your ZeroNet client to use this feature"] 95 | Page.cmd("serverShowdirectory", "backup") 96 | return Page.cmd "wrapperNotification", ["info", "Backup users.json file to keep your identity safe."] 97 | 98 | 99 | 100 | handleSettingsClick: => 101 | Page.settings.sites_orderby ?= "peers" 102 | orderby = Page.settings.sites_orderby 103 | 104 | @menu_settings.items = [] 105 | @menu_settings.items.push ["Update all sites", @handleUpdateAllClick] 106 | @menu_settings.items.push ["---"] 107 | @menu_settings.items.push ["Order sites by peers", ( => @handleOrderbyClick("peers") ), (orderby == "peers")] 108 | @menu_settings.items.push ["Order sites by update time", ( => @handleOrderbyClick("modified") ), (orderby == "modified")] 109 | @menu_settings.items.push ["Order sites by add time", ( => @handleOrderbyClick("addtime") ), (orderby == "addtime")] 110 | @menu_settings.items.push ["Order sites by size", ( => @handleOrderbyClick("size") ), (orderby == "size")] 111 | @menu_settings.items.push ["---"] 112 | @menu_settings.items.push [@renderMenuTheme(), null ] 113 | @menu_settings.items.push ["---"] 114 | @menu_settings.items.push [@renderMenuLanguage(), null ] 115 | @menu_settings.items.push ["---"] 116 | @menu_settings.items.push ["Create new, empty site", @handleCreateSiteClick] 117 | @menu_settings.items.push ["---"] 118 | @menu_settings.items.push [[h("div.icon-mute", ""), "Manage blocked users and sites"], @handleManageBlocksClick] 119 | if Page.server_info.plugins.indexOf("UiConfig") >= 0 120 | @menu_settings.items.push [[h("div.icon-gear.emoji", "\u2699\uFE0E"), "Configuration"], "/Config"] 121 | if Page.server_info.plugins.indexOf("UiPluginManager") >= 0 122 | @menu_settings.items.push [[h("div.icon-gear.emoji", "\u2B21"), "Plugins"], "/Plugins"] 123 | @menu_settings.items.push ["---"] 124 | if not Page.server_info.multiuser or Page.server_info.multiuser_admin 125 | @menu_settings.items.push ["Show data directory", @handleBackupClick] 126 | @menu_settings.items.push ["Version #{Page.server_info.version} (rev#{Page.server_info.rev}): #{@formatUpdateInfo()}", @handleUpdateZeronetClick] 127 | if not Page.server_info.multiuser or Page.server_info.multiuser_admin 128 | @menu_settings.items.push ["Shut down ZeroNet", @handleShutdownZeronetClick] 129 | 130 | if @menu_settings.visible 131 | @menu_settings.hide() 132 | else 133 | @menu_settings.show() 134 | return false 135 | 136 | handleUpdateAllClick: => 137 | for site in Page.site_list.sites 138 | if site.row.settings.serving 139 | Page.cmd "siteUpdate", {"address": site.row.address} 140 | 141 | handleOrderbyClick: (orderby) => 142 | Page.settings.sites_orderby = orderby 143 | Page.site_list.reorder() 144 | Page.saveSettings() 145 | 146 | handleTorClick: => 147 | return true 148 | 149 | handleManageBlocksClick: => 150 | if Page.server_info.rev < 1880 151 | return Page.cmd "wrapperNotification", ["info", "You need ZeroNet 0.5.2 to use this feature."] 152 | 153 | Page.projector.replace($("#MuteList"), Page.mute_list.render) 154 | Page.mute_list.show() 155 | 156 | handleUpdateZeronetClick: => 157 | if Page.server_info.updatesite 158 | Page.updateZeronet() 159 | else 160 | Page.cmd "wrapperConfirm", ["Update to latest development version?", "Update ZeroNet #{Page.latest_version}"], => 161 | Page.updateZeronet() 162 | return false 163 | 164 | 165 | handleShutdownZeronetClick: => 166 | Page.cmd "wrapperConfirm", ["Are you sure?", "Shut down ZeroNet"], => 167 | Page.cmd "serverShutdown" 168 | 169 | handleModeClick: (e) => 170 | if Page.server_info.rev < 1700 171 | Page.cmd "wrapperNotification", ["info", "This feature requires ZeroNet version 0.5.0"] 172 | else 173 | Page.handleLinkClick(e) 174 | return false 175 | 176 | render: => 177 | h("div#Head", 178 | h("a.settings", {href: "#Settings", onmousedown: @handleSettingsClick, onclick: Page.returnFalse}, ["\u22EE"]) 179 | @menu_settings.render() 180 | h("a.logo", {href: "?Home"}, [ 181 | h("img", {src: 'img/logo.svg', width: 40, height: 40, onerror: "this.src='img/logo.png'; this.onerror=null;"}), 182 | h("span", ["Hello ZeroNet_"]) 183 | ]), 184 | h("div.modes", [ 185 | h("a.mode.sites", {href: "?", classes: {active: Page.mode == "Sites"}, onclick: Page.handleLinkClick}, _("Sites")) 186 | h("a.mode.files", {href: "?Files", classes: {active: Page.mode == "Files"}, onclick: Page.handleLinkClick}, _("Files")) 187 | h("a.mode.stats", {href: "?Stats", classes: {active: Page.mode == "Stats"}, onclick: Page.handleLinkClick}, _("Stats")) 188 | ]) 189 | ) 190 | 191 | window.Head = Head 192 | -------------------------------------------------------------------------------- /js/PageFiles/Bigfiles.coffee: -------------------------------------------------------------------------------- 1 | class Bigfiles extends Class 2 | constructor: -> 3 | @files = new SiteFiles(@) 4 | @files.mode = "bigfiles" 5 | @files.limit = 100 6 | @files.update = @updateFiles 7 | @row = {"address": "bigfiles"} 8 | 9 | updateFiles: (cb) => 10 | if Page.server_info.rev < 3090 11 | return cb?() 12 | orderby = @files.orderby + (if @files.orderby_desc then " DESC" else "") 13 | Page.cmd "optionalFileList", {address: "all", filter: "downloaded,bigfile", limit: @files.limit+1, orderby: orderby}, (res) => 14 | for row in res 15 | row.site = Page.site_list.sites_byaddress[row.address] 16 | @files.items = res[0..@files.limit-1] 17 | @files.loaded = true 18 | @files.has_more = res.length > @files.limit 19 | Page.projector.scheduleRender() 20 | cb?() 21 | 22 | getHref: (row) => 23 | return row.inner_path 24 | 25 | render: => 26 | if not @files.items.length 27 | return [] 28 | 29 | h("div.site", [ 30 | h("div.title", [h("h3.name", "Bigfiles")]) 31 | @files.render() 32 | ]) 33 | 34 | window.Bigfiles = Bigfiles 35 | -------------------------------------------------------------------------------- /js/PageFiles/FilesResult.coffee: -------------------------------------------------------------------------------- 1 | class FilesResult extends Class 2 | constructor: -> 3 | @files = new SiteFiles(@) 4 | @files.mode = "result" 5 | @files.limit = 20 6 | @files.update = @updateFiles 7 | @row = {"address": "result"} 8 | @filter_inner_path = "" 9 | 10 | updateFiles: (cb) => 11 | @log "Update FilesResult", @filter_inner_path 12 | if Page.server_info.rev < 4120 13 | Page.projector.scheduleRender() 14 | return cb?() 15 | orderby = @files.orderby + (if @files.orderby_desc then " DESC" else "") 16 | Page.cmd "optionalFileList", {address: "all", filter: "downloaded", filter_inner_path: "%#{@filter_inner_path}%", limit: @files.limit+1, orderby: orderby}, (res) => 17 | for row in res 18 | row.site = Page.site_list.sites_byaddress[row.address] 19 | @files.items = res[0..@files.limit-1] 20 | @files.loaded = true 21 | @files.has_more = res.length > @files.limit 22 | Page.projector.scheduleRender() 23 | cb?() 24 | 25 | setFilter: (filter, cb) => 26 | @filter_inner_path = filter 27 | @updateFiles(cb) 28 | 29 | getHref: (row) => 30 | return row.inner_path 31 | 32 | render: => 33 | if Page.server_info.rev < 4120 34 | return h("div.empty", [ 35 | h("h4", "Feature not supported"), 36 | h("small", "You need to update to the latest version to use this feature") 37 | ]) 38 | if not @filter_inner_path 39 | return [] 40 | if not @files.items.length 41 | return h("div.empty", [ 42 | h("h4", "Filter result: #{@filter_inner_path}"), 43 | h("small", "No files found") 44 | ]) 45 | 46 | h("div.site", [ 47 | h("div.title", [h("h3.name", "Filter result: #{@filter_inner_path}")]) 48 | @files.render() 49 | ]) 50 | 51 | window.FilesResult = FilesResult -------------------------------------------------------------------------------- /js/PageFiles/SiteFiles.coffee: -------------------------------------------------------------------------------- 1 | class SiteFiles extends Class 2 | constructor: (@site) -> 3 | @limit = 10 4 | @selected = {} 5 | @items = [] 6 | @loaded = false 7 | @orderby = "time_downloaded" 8 | @mode = "site" 9 | @mode = "single_site" 10 | @orderby_desc = true 11 | @has_more = false 12 | 13 | getSites: => 14 | back = [] 15 | # Create separate fake site objects for bigfiles 16 | sites = {} 17 | for file in @items 18 | sites[file.site.row.address] ?= {row: file.site.row, files: {mode: @mode, items: [], selected: @selected, update: @update}} 19 | sites[file.site.row.address].files.items.push(file) 20 | 21 | for address, site of sites 22 | back.push(site) 23 | return back 24 | 25 | handleSelectClick: (e) => 26 | return false 27 | 28 | handleSelectEnd: (e) => 29 | document.body.removeEventListener('mouseup', @handleSelectEnd) 30 | @select_action = null 31 | 32 | handleSelectMousedown: (e) => 33 | inner_path = e.currentTarget.attributes.inner_path.value 34 | if @selected[inner_path] 35 | delete @selected[inner_path] 36 | @select_action = "deselect" 37 | else 38 | @selected[inner_path] = true 39 | @select_action = "select" 40 | Page.page_files.checkSelectedFiles() 41 | document.body.addEventListener('mouseup', @handleSelectEnd) 42 | e.stopPropagation() 43 | Page.projector.scheduleRender() 44 | return false 45 | 46 | handleRowMouseenter: (e) => 47 | if e.buttons and @select_action 48 | inner_path = e.target.attributes.inner_path.value 49 | if @select_action == "select" 50 | @selected[inner_path] = true 51 | else 52 | delete @selected[inner_path] 53 | Page.page_files.checkSelectedFiles() 54 | Page.projector.scheduleRender() 55 | return false 56 | 57 | handleOrderbyClick: (e) => 58 | orderby = e.currentTarget.attributes.orderby.value 59 | if @orderby == orderby 60 | @orderby_desc = not @orderby_desc 61 | @orderby = orderby 62 | @update() 63 | return false 64 | 65 | handleMoreClick: => 66 | @limit += 15 67 | @update() 68 | return false 69 | 70 | selectAll: => 71 | is_selected_all = @isSelectedAll() 72 | for item in @items 73 | if is_selected_all 74 | delete @selected[item.inner_path] 75 | else 76 | @selected[item.inner_path] = true 77 | Page.projector.scheduleRender() 78 | Page.page_files.checkSelectedFiles() 79 | 80 | handleSelectAllClick: => 81 | if @has_more 82 | @limit = 1000 83 | @update => 84 | @selectAll() 85 | else 86 | @selectAll() 87 | return false 88 | 89 | renderOrder: (title, orderby) => 90 | h("a.title.orderby", { 91 | href: "##{orderby}", 92 | orderby: orderby, 93 | onclick: @handleOrderbyClick, 94 | classes: {selected: @orderby == orderby, desc: @orderby_desc} 95 | }, [ 96 | title, 97 | h("div.icon.icon-arrow-down") 98 | ]) 99 | 100 | renderOrderRight: (title, orderby) => 101 | h("a.title.orderby", { 102 | href: "##{orderby}", 103 | orderby: orderby, 104 | onclick: @handleOrderbyClick, 105 | classes: {selected: @orderby == orderby, desc: @orderby_desc} 106 | }, [ 107 | h("div.icon.icon-arrow-down"), 108 | title 109 | ]) 110 | 111 | isSelectedAll: => 112 | return not @has_more and Object.keys(@selected).length == @items.length 113 | 114 | render: => 115 | if not @items?.length 116 | return [] 117 | [ 118 | h("div.files.files-#{@mode}", exitAnimation: Animation.slideUpInout, [ 119 | h("div.tr.thead", [ 120 | h("div.td.pre", 121 | h("a.checkbox-outer", { 122 | href: "#Select+all", onclick: @handleSelectAllClick, classes: {selected: @isSelectedAll()} 123 | }, h("span.checkbox")) 124 | ), 125 | if @mode == "bigfiles" or @mode == "result" 126 | h("div.td.site", @renderOrder("Site", "address")) 127 | h("div.td.inner_path", @renderOrder("Optional file", "is_pinned DESC, inner_path")), 128 | if @mode == "bigfiles" 129 | h("div.td.status", "Status") 130 | h("div.td.size", @renderOrderRight("Size", "size")), 131 | h("div.td.peer", @renderOrder("Peers", "peer")), 132 | h("div.td.uploaded", @renderOrder("Uploaded", "uploaded")), 133 | h("div.td.added", @renderOrder("Finished", "time_downloaded")) 134 | #h("th.access", "Access") 135 | ]), 136 | h("div.tbody", @items.map (file) => 137 | site = file.site or @site 138 | if file.peer >= 10 139 | profile_color = "#47d094" 140 | else if file.peer > 0 141 | profile_color = "#f5b800" 142 | else 143 | profile_color = "#d1d1d1" 144 | if @mode == "bigfiles" 145 | file.pieces ?= 0 146 | file.pieces_downloaded ?= 0 147 | if file.pieces == 0 or file.pieces_downloaded == 0 148 | percent = 0 149 | else 150 | percent = parseInt((file.pieces_downloaded / file.pieces) * 100) 151 | 152 | if file.is_downloading or percent == 100 153 | status = "" 154 | percent_bg = "#9ef5cf" 155 | else 156 | status = "paused" 157 | percent_bg = "#f5f49e" 158 | 159 | percent_title = "#{percent}% #{status}" 160 | 161 | classes = {selected: @selected[file.inner_path], pinned: file.is_pinned} 162 | 163 | h("div.tr", {key: file.inner_path, inner_path: file.inner_path, exitAnimation: Animation.slideUpInout, enterAnimation: Animation.slideDown, classes: classes, onmouseenter: @handleRowMouseenter}, [ 164 | h("div.td.pre", 165 | h("a.checkbox-outer", { 166 | href: "#Select", 167 | onmousedown: @handleSelectMousedown, 168 | onclick: @handleSelectClick, 169 | inner_path: file.inner_path 170 | }, h("span.checkbox")) 171 | ), 172 | if @mode == "bigfiles" or @mode == "result" 173 | h("div.td.site", h("a.link", {href: site.getHref()}, site.row.content.title)) 174 | h("div.td.inner_path", 175 | h("a.title.link", {href: site.getHref(file), target: "_blank", title: file.inner_path.replace(/.*\//, "")}, file.inner_path.replace(/.*\//, "")) 176 | if file.is_pinned 177 | h("span.pinned", {exitAnimation: Animation.slideUpInout, enterAnimation: Animation.slideDown}, "Pinned") 178 | ), 179 | if @mode == "bigfiles" 180 | h("div.td.status", {classes: {"downloading": file.is_downloading}} 181 | h("span.percent", {title: "#{file.pieces_downloaded} of #{file.pieces} pieces downloaded", style: "box-shadow: inset #{percent * 0.8}px 0px 0px #{percent_bg};"}, percent_title) 182 | ) 183 | h("div.td.size", Text.formatSize(file.size)), 184 | h("div.td.peer", [ 185 | h("div.icon.icon-profile", {style: "color: #{profile_color}"}), 186 | h("span.num", file.peer) 187 | ]), 188 | h("div.td.uploaded", 189 | h("div.uploaded-text", Text.formatSize(file.uploaded)), 190 | h("div.dots-container", [ 191 | h("span.dots.dots-bg", {title: "Ratio: #{(file.uploaded/file.size).toFixed(1)}"}, "\u2022\u2022\u2022\u2022\u2022"), 192 | h("span.dots.dots-fg", {title: "Ratio: #{(file.uploaded/file.size).toFixed(1)}", style: "width: #{Math.min(5, file.uploaded/file.size) * 9}px"}, "\u2022\u2022\u2022\u2022\u2022") 193 | ]) 194 | ), 195 | h("div.td.added", if file.time_downloaded then Time.since(file.time_downloaded) else "n/a"), 196 | #h("td.access", if file.time_accessed then Time.since(file.time_accessed) else "n/a") 197 | ]) 198 | ) 199 | ]), 200 | if @has_more 201 | h("div.more-container", h("a.more", {href: "#More", onclick: @handleMoreClick}, "More files...")) 202 | ] 203 | 204 | update: (cb) => 205 | orderby = @orderby + (if @orderby_desc then " DESC" else "") 206 | Page.cmd "optionalFileList", {address: @site.row.address, limit: @limit+1, orderby: orderby}, (res) => 207 | @items = res[0..@limit-1] 208 | @loaded = true 209 | @has_more = res.length > @limit 210 | Page.projector.scheduleRender() 211 | cb?() 212 | 213 | window.SiteFiles = SiteFiles 214 | -------------------------------------------------------------------------------- /js/PageSites/MuteList.coffee: -------------------------------------------------------------------------------- 1 | class MuteList extends Class 2 | constructor: -> 3 | @mutes = null 4 | @includes = null 5 | @visible = false 6 | @max_height = 0 7 | @updated = false 8 | @siteblocks_serving = [] 9 | Page.site_list.on_loaded.then => 10 | @updateFilterIncludes() 11 | @ 12 | 13 | update: => 14 | @need_update = false 15 | Page.cmd "MuteList", [], (res) => 16 | @mutes = [] 17 | for auth_address, mute of res 18 | mute.auth_address = auth_address 19 | mute.site = Page.site_list.sites_byaddress[mute.source] 20 | @mutes.push(mute) 21 | 22 | @mutes.sort (a, b) -> 23 | return b.date_added - a.date_added 24 | 25 | if not @max_height 26 | @max_height = 100 27 | 28 | @updated = true 29 | Page.projector.scheduleRender() 30 | 31 | @updateFilterIncludes() 32 | 33 | updateFilterIncludes: => 34 | Page.cmd "filterIncludeList", {all_sites: true, filters: true}, (res) => 35 | @siteblocks_serving = [] 36 | @includes = [] 37 | @siteblocks = {} 38 | for include in res 39 | include.site = Page.site_list.sites_byaddress[include.address] 40 | 41 | mutes = [] 42 | if include.mutes? 43 | for auth_address, mute of include.mutes 44 | mute.auth_address = auth_address 45 | mutes.push(mute) 46 | include.mutes = mutes 47 | 48 | siteblocks = [] 49 | if include.siteblocks? 50 | for address, siteblock of include.siteblocks 51 | siteblock.address = address 52 | siteblock.include = include 53 | siteblocks.push(siteblock) 54 | @siteblocks[address] = siteblock 55 | include.siteblocks = siteblocks 56 | 57 | @includes.push(include) 58 | 59 | @includes.sort (a, b) -> 60 | return b.date_added - a.date_added 61 | 62 | for site in Page.site_list.sites 63 | address = site.row.address 64 | if @siteblocks[address] and not Page.settings.siteblocks_ignore[address] 65 | @siteblocks[address].site = site 66 | @siteblocks_serving.push(@siteblocks[address]) 67 | 68 | address_hash = "0x" + site.row.address_hash 69 | if @siteblocks[address_hash] and not Page.settings.siteblocks_ignore[address_hash] 70 | @siteblocks[address_hash].site = site 71 | @siteblocks_serving.push(@siteblocks[address_hash]) 72 | 73 | @updated = true 74 | Page.projector.scheduleRender() 75 | 76 | handleHideClick: => 77 | @visible = false 78 | setTimeout (=> 79 | @updateFilterIncludes() 80 | ), 1000 81 | @max_height = 0 82 | 83 | handleMuteRemoveClick: (e) => 84 | mute = e.target.mute 85 | if mute.removed 86 | # Re-add 87 | Page.cmd("muteAdd", [mute.auth_address, mute.cert_user_id, mute.reason]) 88 | else 89 | # Remove 90 | Page.cmd("muteRemove", mute.auth_address) 91 | mute.removed = not mute.removed 92 | return false 93 | 94 | handleIncludeRemoveClick: (e) => 95 | include = e.currentTarget.include 96 | if include.removed 97 | # Re-add 98 | Page.cmd("filterIncludeAdd", [include.inner_path, include.description, include.address]) 99 | else 100 | # Remove 101 | Page.cmd("filterIncludeRemove", {inner_path: include.inner_path, address: include.address}) 102 | include.removed = not include.removed 103 | return false 104 | 105 | afterUpdate: => 106 | @updated = false 107 | if @node and @visible 108 | @max_height = @node.offsetHeight + 100 109 | Page.projector.scheduleRender() 110 | 111 | storeNode: (node) => 112 | @node = node 113 | 114 | renderMutes: (mutes, mode="mutes") => 115 | h("div.mutes", [ 116 | h("div.mute.mute-head", [ 117 | h("div.mute-col", "Muted user"), 118 | h("div.mute-col", {style: "width: 66%"}, "Why?") 119 | ]), 120 | mutes.map (mute) => 121 | h("div.mute", {key: mute.auth_address, classes: {removed: mute.removed}}, [ 122 | h("div.mute-col", [ 123 | h("div.cert_user_id", mute.cert_user_id), 124 | h("div.auth_address", mute.auth_address), 125 | ]), 126 | h("div.mute-col", {style: "width: 66%"}, [ 127 | h("div.source", if mute.site? then mute.site.row.content.title else mute.source), 128 | h("div.reason", {innerHTML: Text.renderMarked(mute.reason)}), 129 | h("div.date_added", " \u2500 " + Time.since(mute.date_added)) 130 | ]) 131 | if mode == "mutes" 132 | h("a.action", {href: "#Unmute", onclick: @handleMuteRemoveClick, mute: mute}, "×") 133 | ]) 134 | ]) 135 | 136 | renderSiteblocks: (siteblocks) => 137 | h("div.siteblocks", [ 138 | h("div.mute.mute-head", [ 139 | h("div.mute-col", "Blocked site"), 140 | h("div.mute-col", {style: "width: 66%"}, "Why?") 141 | ]), 142 | siteblocks.map (siteblock) => 143 | h("div.mute", {key: siteblock.address, classes: {removed: siteblock.removed}}, [ 144 | h("div.mute-col", [ 145 | h("div.cert_user_id", siteblock.name), 146 | h("div.auth_address", siteblock.address), 147 | ]), 148 | h("div.mute-col", {style: "width: 66%"}, [ 149 | h("div.reason", {innerHTML: Text.renderMarked(siteblock.reason)}), 150 | h("div.date_added", " \u2500 " + Time.since(siteblock.date_added)) 151 | ]) 152 | ]) 153 | ]) 154 | 155 | 156 | renderIncludes: => 157 | h("div.includes", [ 158 | @includes.map (include) => 159 | h("div.include", {key: include.address + include.inner_path, classes: {removed: include.removed}}, [ 160 | h("h2", h("a.site", {href: include.site.getHref()}, include.site.row.content.title), " \u203A ", h("a.inner_path", {href: "#"}, include.inner_path)) 161 | h("a.action", {href: "#Remove+include", onclick: @handleIncludeRemoveClick, include: include}, 162 | [h("span.closer", "×"), "deactivate this blocklist"] 163 | ) 164 | if include.mutes.length 165 | @renderMutes(include.mutes, "includes") 166 | if include.siteblocks.length 167 | @renderSiteblocks(include.siteblocks) 168 | 169 | ]) 170 | ]) 171 | 172 | render: => 173 | if @need_update 174 | @update() 175 | if not @mutes 176 | return h("div#MuteList", {classes: {visible: false}}, "Muted") 177 | if @updated 178 | @updated = false 179 | setTimeout @afterUpdate 180 | 181 | h("div#MuteList", {classes: {visible: @visible}, style: "max-height: #{@max_height}px"}, [ 182 | h("a.mute-hide", {href: "#Hide", onclick: @handleHideClick}, "\u2039 Back to feed"), 183 | 184 | if @mutes?.length == 0 and @includes?.length == 0 185 | h("div.mute-empty", "Your mute list is empty! :)") 186 | else 187 | h("div", {afterCreate: @storeNode}, [ 188 | if @mutes.length > 0 189 | @renderMutes(@mutes) 190 | if @includes 191 | @renderIncludes() 192 | ]) 193 | ]) 194 | 195 | show: => 196 | @visible = true 197 | Page.site_list.on_loaded.then => 198 | @need_update = true 199 | Page.projector.scheduleRender() 200 | 201 | window.MuteList = MuteList 202 | -------------------------------------------------------------------------------- /js/PageSites/SiteList.coffee: -------------------------------------------------------------------------------- 1 | class SiteList extends Class 2 | constructor: -> 3 | @item_list = new ItemList(Site, "address") 4 | @sites = @item_list.items 5 | @sites_byaddress = @item_list.items_bykey 6 | @inactive_demo_sites = null 7 | @loaded = false 8 | @on_loaded = new Promise() 9 | @schedule_reorder = false 10 | @merged_db = {} 11 | @filtering = "" 12 | setInterval(@reorderTimer, 10000) 13 | @limit = 100 14 | @should_animate = false 15 | 16 | Page.on_settings.then => 17 | Page.on_server_info.then => 18 | @update() 19 | Page.cmd "channelJoinAllsite", {"channel": "siteChanged"} 20 | 21 | reorderTimer: => 22 | if not @schedule_reorder 23 | return 24 | 25 | # Don't reorder if user if over site list or any of the sites are updating 26 | if not document.querySelector('.left:hover') and not document.querySelector(".working") and not Page.mode == "Files" 27 | @reorder() 28 | @schedule_reorder = false 29 | 30 | sortRows: (rows) => 31 | if Page.settings.sites_orderby == "modified" 32 | rows.sort (a, b) -> 33 | return b.row.settings.modified - a.row.settings.modified 34 | else if Page.settings.sites_orderby == "addtime" 35 | rows.sort (a, b) -> 36 | return b.row.settings.added - a.row.settings.added 37 | else if Page.settings.sites_orderby == "size" 38 | rows.sort (a, b) -> 39 | return b.row.settings.size - a.row.settings.size 40 | else 41 | rows.sort (a, b) -> 42 | return Math.max(b.row.peers, b.row.settings.peers) - Math.max(a.row.peers, a.row.settings.peers) 43 | return rows 44 | 45 | reorder: => 46 | @sortRows(@item_list.items) 47 | Page.projector.scheduleRender() 48 | 49 | update: -> 50 | if Page.server_info.rev >= 3660 51 | args = {connecting_sites: true} 52 | else 53 | args = {} 54 | Page.cmd "siteList", args, (site_rows) => 55 | favorite_sites = Page.settings.favorite_sites 56 | 57 | @item_list.sync(site_rows) 58 | 59 | @sortRows(@item_list.items) 60 | 61 | if @inactive_demo_sites == null 62 | @updateInactiveDemoSites() 63 | Page.projector.scheduleRender() 64 | @loaded = true 65 | @log "loaded" 66 | @on_loaded.resolve() 67 | @ 68 | 69 | updateInactiveDemoSites: -> 70 | demo_site_rows = [ 71 | {address: "1TaLkFrMwvbNsooF4ioKAY9EuxTBTjipT", demo: true, content: {title: "ZeroTalk", domain: "Talk.ZeroNetwork.bit"}, settings: {}} 72 | {address: "1BLogC9LN4oPDcruNz3qo1ysa133E9AGg8", demo: true, content: {title: "ZeroBlog", domain: "Blog.ZeroNetwork.bit"}, settings: {}} 73 | {address: "1MaiL5gfBM1cyb4a8e3iiL8L5gXmoAJu27", demo: true, content: {title: "ZeroMail", domain: "Mail.ZeroNetwork.bit"}, settings: {}} 74 | {address: "1uPLoaDwKzP6MCGoVzw48r4pxawRBdmQc", demo: true, content: {title: "ZeroUp"}, settings: {}} 75 | {address: "1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h", demo: true, content: {title: "GIF Time"}, settings: {}} 76 | {address: "1SiTEs2D3rCBxeMoLHXei2UYqFcxctdwB", demo: true, content: {title: "More @ ZeroSites", domain: "Sites.ZeroNetwork.bit"}, settings: {}} 77 | ] 78 | if Page.server_info.rev >= 1400 79 | demo_site_rows.push {address: "1MeFqFfFFGQfa1J3gJyYYUvb5Lksczq7nH", demo: true, content: {title: "ZeroMe", domain: "Me.ZeroNetwork.bit"}, settings: {}} 80 | 81 | @inactive_demo_sites = [] 82 | for site_row in demo_site_rows 83 | if @filtering and site.row.content.title.toLowerCase().indexOf(@filtering.toLowerCase()) == -1 84 | continue 85 | if not @sites_byaddress[site_row.address] 86 | @inactive_demo_sites.push(new Site(site_row)) 87 | 88 | renderMergedSites: => 89 | merged_db = {} 90 | for site in @sites_merged 91 | if not site.row.content.merged_type 92 | continue 93 | merged_db[site.row.content.merged_type] ?= [] 94 | merged_db[site.row.content.merged_type].push site 95 | 96 | back = [] 97 | for merged_type, merged_sites of merged_db 98 | back.push @renderSection(".merged.merged-#{merged_type}", "Merged: #{merged_type}", merged_sites), 99 | return back 100 | 101 | handleFilterInput: (e) => 102 | @filtering = e.target.value 103 | 104 | handleFilterKeyup: (e) => 105 | if e.keyCode == 27 # Esc 106 | e.target.value = "" 107 | @handleFilterInput(e) 108 | return false 109 | 110 | handleFilterClear: (e) => 111 | e.target.value = "" 112 | @handleFilterInput(e) 113 | return false 114 | 115 | handleSiteListMoreClick: (e) => 116 | @limit += 1000 117 | Page.projector.scheduleRender() 118 | return false 119 | 120 | handleSectionClick: (e) => 121 | @should_animate = true 122 | class_name = e.currentTarget.getAttribute("class_name") 123 | if Page.settings.sites_section_hide[class_name] 124 | delete Page.settings.sites_section_hide[class_name] 125 | else 126 | Page.settings.sites_section_hide[class_name] = true 127 | Page.saveSettings() 128 | Page.projector.scheduleRender() 129 | return false 130 | 131 | renderSection: (class_name, title, items, limit=null) => 132 | if items.length == 0 133 | return null 134 | 135 | classes = {"hidden": Page.settings.sites_section_hide[class_name]} 136 | 137 | if limit and items.length > limit 138 | items_limited = items[0..limit] 139 | else 140 | items_limited = items 141 | 142 | return [ 143 | h("h2.SiteSection.section#{class_name}", {classes: classes}, 144 | h("a.section-title", { href: "#Show", onclick: @handleSectionClick, class_name: class_name }, [ 145 | title, 146 | h("span.hide-title", "[Show #{items.length} sites]") 147 | ]) 148 | ), 149 | if not classes.hidden 150 | h("div.SiteList#{class_name}", {classes: classes, exitAnimation: Animation.slideUp, enterAnimation: Animation.slideDown, animate_disable: !@should_animate}, 151 | [ 152 | [ items_limited.map (item) -> item.render() ], 153 | if limit and items.length > limit 154 | h("a.site-list-more", {href: "#Show+more+connected+sites", onclick: @handleSiteListMoreClick}, "Show more") 155 | ] 156 | ) 157 | ] 158 | 159 | render: => 160 | if not @loaded 161 | return h("div#SiteList") 162 | 163 | @sites_needaction = [] 164 | @sites_favorited = [] 165 | @sites_owned = [] 166 | @sites_recent = [] 167 | @sites_connected = [] 168 | @sites_connecting = [] 169 | @sites_merged = [] 170 | num_found = 0 171 | 172 | for site in @sites 173 | if @filtering 174 | filter_base = site.row.content.title + site.row.content.merged_type + site.row.address 175 | if filter_base.toLowerCase().indexOf(@filtering.toLowerCase()) == -1 176 | continue 177 | 178 | if site.row.settings.size * 1.2 > site.row.size_limit * 1024 * 1024 179 | site.row.need_limit = site.row.size_limit * 2 180 | @sites_needaction.push site 181 | else if site.favorite 182 | @sites_favorited.push site 183 | else if site.row.content.merged_type 184 | @sites_merged.push site 185 | else if site.row.settings?.own 186 | @sites_owned.push site 187 | else if site.row.settings?.downloaded > Time.timestamp() - 60 * 60 * 24 188 | @sites_recent.push site 189 | else if site.row.content.title 190 | @sites_connected.push site 191 | else 192 | @sites_connecting.push site 193 | num_found += 1 194 | 195 | h("div#SiteList", [ 196 | if @sites.length > 10 197 | h("input.site-filter", {placeholder: "Filter: Site name", spellcheck: false, oninput: @handleFilterInput, onkeyup: @handleFilterKeyup, value: @filtering}) 198 | if @filtering 199 | [ 200 | h("span.filter-num", {updateAnimation: Animation.show, enterAnimation: Animation.show, exitAnimation: Animation.hide}, "(found #{num_found} of #{@sites.length} sites)") 201 | h("a.filter-clear", {href: "#clear", onclick: @handleFilterClear}, "\u00D7") 202 | ] 203 | @renderSection(".recent", "Recently downloaded:", @sites_recent), 204 | @renderSection(".needaction", "Running out of size limit:", @sites_needaction), 205 | @renderSection(".favorited", "Favorited sites:", @sites_favorited), 206 | @renderSection(".owned", "Owned sites:", @sites_owned), 207 | @renderSection(".connecting", "Connecting sites:", @sites_connecting), 208 | @renderSection(".connected", "Connected sites:", @sites_connected, @limit), 209 | 210 | @renderMergedSites() 211 | if @inactive_demo_sites != null and @inactive_demo_sites.length > 0 212 | @renderSection(".more", "More sites:", @inactive_demo_sites) 213 | ]) 214 | 215 | 216 | onSiteInfo: (site_info) => 217 | @item_list.items_bykey[site_info.address]?.setRow(site_info) 218 | @schedule_reorder = true 219 | Page.projector.scheduleRender() 220 | 221 | 222 | window.SiteList = SiteList 223 | -------------------------------------------------------------------------------- /js/PageSites/Trigger.coffee: -------------------------------------------------------------------------------- 1 | class Trigger extends Class 2 | constructor: -> 3 | @active = false 4 | 5 | handleTitleClick: => 6 | @active = not @active 7 | if @active 8 | document.getElementById("left").classList.add("trigger-on") 9 | else 10 | document.getElementById("left").classList.remove("trigger-on") 11 | 12 | return false 13 | 14 | render: => 15 | h("div.Trigger", {classes: { "active": @active }}, [ 16 | h("a.icon", {"href": "#Trigger", onclick: @handleTitleClick, ontouchend: ""}, h("div.arrow-right")) 17 | ]) 18 | window.Trigger = Trigger 19 | -------------------------------------------------------------------------------- /js/PageStats/Chart.coffee: -------------------------------------------------------------------------------- 1 | class Chart extends Class 2 | constructor: () -> 3 | @query = "" 4 | @title = "" 5 | @value = "" 6 | @line_data = [] 7 | @details = [] 8 | @colorize = "cc00ff0a" 9 | @chart_ctx = null 10 | @chart_type_name = null 11 | @need_update = false 12 | 13 | initChart: (node) => 14 | @chart_canvas = node 15 | @chart_ctx = node.getContext("2d") 16 | 17 | getTitle: => 18 | @title 19 | 20 | update: => 21 | Page.cmd "chartDbQuery", @getChartQuery(), (res) => 22 | @line_data = [] 23 | for row in res 24 | @line_data.push(row.value) 25 | @line_data.reverse() 26 | @updateChart() 27 | 28 | query_type_data = """ 29 | SELECT * FROM data 30 | WHERE 31 | type_id IN :type_ids AND 32 | date_added = (SELECT date_added FROM data ORDER BY data_id DESC LIMIT 1) 33 | """ 34 | Page.cmd "chartDbQuery", [query_type_data, {type_ids: Page.page_stats.type_id_db[type_name] for type_name in @type_names}], (res) => 35 | type_data = {} 36 | for row in res 37 | type_data[Page.page_stats.type_name_db[row.type_id]] = row.value 38 | @details = @formatDetails?(type_data) 39 | @value = @formatValue?(type_data) 40 | Page.projector.scheduleRender() 41 | 42 | updateChart: => 43 | @chart_ctx.clearRect(0, 0, @chart_canvas.width, @chart_canvas.height) 44 | stroke = @chart_ctx.createLinearGradient(0, 0, 900, 0) 45 | stroke.addColorStop(0, @chart_stroke[0]) 46 | stroke.addColorStop(1, @chart_stroke[1]) 47 | 48 | #@chart_ctx.shadowColor = 'rgba(0, 0, 0, 0.7)' 49 | #@chart_ctx.shadowBlur = 3 50 | #@chart_ctx.shadowOffsetY = 0 51 | 52 | @chart_ctx.lineWidth = 4 53 | @chart_ctx.strokeStyle = stroke 54 | @chart_ctx.fillStyle = '#66666611' 55 | gradient = @chart_ctx.createLinearGradient(0, 200, 0, 400) 56 | gradient.addColorStop(0, "#42324599") 57 | gradient.addColorStop(1, "#2C2E3700") 58 | @chart_ctx.fillStyle = gradient 59 | 60 | @chart_ctx.beginPath() 61 | 62 | @chart_ctx.moveTo(-10,0) 63 | step = 900 / (@line_data.length - 2) 64 | data_max = Math.max.apply(null, @line_data) 65 | data_min = Math.min.apply(null, @line_data) 66 | for data, i in @line_data 67 | line_y = 250 - ((data - data_min) / (data_max - data_min)) * 120 68 | @chart_ctx.lineTo((i - 1) * step, line_y) 69 | @chart_ctx.lineTo((i + 1) * step, line_y) 70 | @chart_ctx.lineTo(i * step, 450) 71 | @chart_ctx.lineTo(0, 450) 72 | 73 | @chart_ctx.fill() 74 | @chart_ctx.stroke() 75 | @chart_ctx.shadowBlur = 0 76 | 77 | render: => 78 | if @need_update 79 | @update() 80 | @need_update = false 81 | 82 | h("div.Chart", {style: "background-image: radial-gradient(at 29% top, #eaaeda05, #{@colorize})"}, [ 83 | h("div.titles", [ 84 | h("div.title", @getTitle()) 85 | h("div.value", @value) 86 | h("div.details", @details.map (detail) => 87 | [detail, h("br", key: detail)] 88 | ) 89 | ]), 90 | h("canvas.canvas", {afterCreate: @initChart, width: 900, height: 400}) 91 | ]) 92 | 93 | window.Chart = Chart 94 | -------------------------------------------------------------------------------- /js/PageStats/ChartLegend.coffee: -------------------------------------------------------------------------------- 1 | class ChartLegend extends Class 2 | constructor: -> 3 | @items_left = [] 4 | @items_right = [] 5 | 6 | renderItem: (item) => 7 | @i += 1 8 | item.dot ?= "\u25CF" 9 | value = item.getValue() 10 | hidden = not value 11 | if item.post 12 | value += " #{item.post}" 13 | 14 | if item.type == "ratio" 15 | h("div.legend-item", {classes: {hidden: hidden}}, [ 16 | h("div.title", item.title), 17 | h("div.value", [ 18 | h("span", {updateAnimation: Animation.show, delay: @i * 0.1}, Math.round(value * 10) / 10), 19 | h("div.dots-container", [ 20 | h("span.dots.dots-fg", {style: "width: #{Math.min(value, 5) * 11.5}px; color: #{item.color}"}, item.dot.repeat(5)), 21 | h("span.dots.dots-bg", item.dot.repeat(5)) 22 | ]) 23 | ]) 24 | ]) 25 | else 26 | h("div.legend-item", {classes: {hidden: hidden}}, [ 27 | h("div.title", [h("span.dot", {style: "color: #{item.color}"}, "#{item.dot} "), item.title]), 28 | h("div.value", {updateAnimation: Animation.show, delay: @i * 0.1}, value) 29 | ]) 30 | 31 | 32 | render: -> 33 | @i = 0 34 | h("div.ChartLegend", 35 | h("div.legend-items.align-left", @items_left.map(@renderItem)) 36 | h("div.legend-items.align-right", @items_right.map(@renderItem)) 37 | ) 38 | 39 | window.ChartLegend = ChartLegend -------------------------------------------------------------------------------- /js/PageStats/ChartRadar.coffee: -------------------------------------------------------------------------------- 1 | class ChartRadar extends Class 2 | constructor: (@id) -> 3 | @configuration = {} 4 | @site_stats = [] 5 | @need_update = false 6 | @order_by = "site_bw" 7 | @legends = [ 8 | {id: "site_bw", title: "Transferred data (last 7 days)", color: "#608DECDD"}, 9 | {id: "site_size", title: "Site size", color: "#9C27B0DD"} 10 | ] 11 | 12 | update: => 13 | query = """ 14 | SELECT type_id, site_id, SUM(value) AS sum, value 15 | FROM data 16 | WHERE type_id IN :type_ids AND date_added > #{Time.timestamp() - 60 * 60 * 24 * 7} 17 | GROUP BY type_id, site_id 18 | """ 19 | type_ids = (Page.page_stats.type_id_db[type_name] for type_name in ["site_bytes_sent", "site_bytes_recv", "site_size"]) 20 | Page.cmd "chartDbQuery", [query, {type_ids: type_ids}], (res) => 21 | @logStart "Parse result" 22 | 23 | # Aggregate data from result 24 | data = {} 25 | for row in res 26 | address = Page.page_stats.site_address_db[row.site_id] 27 | type_name = Page.page_stats.type_name_db[row.type_id] 28 | site = Page.site_list.sites_byaddress[address] 29 | if not site 30 | continue 31 | data[address] ?= {address: address, site: site} 32 | if type_name == "site_size" 33 | data[address][type_name] = row.value 34 | else 35 | data[address][type_name] = row.sum or 0 36 | 37 | # Sort by bytes_sent 38 | @site_stats = [] 39 | for address, stat of data 40 | stat.site_bw = stat.site_bytes_sent + stat.site_bytes_recv 41 | @site_stats.push(stat) 42 | 43 | @site_stats.sort (a, b) => 44 | return b[@order_by] - a[@order_by] 45 | 46 | if @site_stats.length > 8 47 | @site_stats.length = 8 48 | 49 | max_site_bw = Math.max.apply(null, stat.site_bw for stat in @site_stats) 50 | max_site_size = Math.max.apply(null, stat.site_size for stat in @site_stats) 51 | for stat, i in @site_stats 52 | @configuration.data.labels[i] = stat.site.row.content.title 53 | @configuration.data.datasets[0].data[i] = Math.log(1 + (stat.site_bw / max_site_bw) * 100) 54 | @configuration.data.datasets[1].data[i] = Math.log(1 + (stat.site_size / max_site_size) * 100) 55 | 56 | @logEnd "Parse result", "sites: #{@site_stats.length}" 57 | Page.projector.scheduleRender() 58 | 59 | if @chart 60 | @chart.update() 61 | else 62 | @initChart() 63 | 64 | initCanvas: (node) => 65 | if @chart 66 | @chart.clear() 67 | @chart.destroy() 68 | @chart = null 69 | 70 | @ctx = node.getContext("2d") 71 | @configuration = @getChartConfiguration() 72 | #setTimeout @initChart, 100 # Delay chart to add animation 73 | 74 | getChartConfiguration: => 75 | fill = @ctx.createLinearGradient(0, 0, 900, 0) 76 | fill.addColorStop(0, "#608DECCC") 77 | fill.addColorStop(1, "#9C27B0CC") 78 | 79 | fill2 = @ctx.createLinearGradient(0, 0, 900, 0) 80 | fill2.addColorStop(0, "#9C27B0DD") 81 | fill2.addColorStop(1, "#608DECDD") 82 | 83 | shadowed = { 84 | beforeDatasetsDraw: (chart, options) -> 85 | chart.ctx.shadowColor = 'rgba(0, 0, 0, 0.5)' 86 | chart.ctx.shadowBlur = 40 87 | afterDatasetsDraw: (chart, options) -> 88 | chart.ctx.shadowColor = 'rgba(0, 0, 0, 0)' 89 | chart.ctx.shadowBlur = 0 90 | } 91 | 92 | return { 93 | type: 'radar', 94 | data: { 95 | labels: [], 96 | datasets: [{ 97 | label: "Transferred data", 98 | backgroundColor: fill, 99 | borderColor: "transparent", 100 | borderWidth: 0, 101 | pointBorderWidth: 0, 102 | pointRadius: 0 103 | data: [] 104 | },{ 105 | label: "Site size", 106 | backgroundColor: fill2, 107 | borderColor: "transparent", 108 | borderWidth: 0, 109 | pointBorderWidth: 0, 110 | pointRadius: 0, 111 | data: [] 112 | }] 113 | }, 114 | options: { 115 | legend: { 116 | display: false, 117 | position: "bottom", 118 | labels: { padding: 5 } 119 | }, 120 | scale: { 121 | ticks: { 122 | display: false, 123 | maxTicksLimit: 5, 124 | beginAtZero: true 125 | }, 126 | angleLines: { 127 | color: "#99999911" 128 | }, 129 | gridLines: { 130 | color: "#99999911", 131 | tickMarkLength: 1 132 | }, 133 | tooltips: { enabled: true }, 134 | pointLabels: { 135 | fontColor: "rgba(200,210,232,1)", 136 | fontSize: 14, 137 | fontFamily: "Roboto" 138 | fontStyle: "lighter", 139 | padding: 10, 140 | callback: @formatLabel 141 | } 142 | } 143 | }, 144 | plugins: [shadowed] 145 | } 146 | 147 | formatLabel: => 148 | return [""] 149 | 150 | initChart: => 151 | @chart = new Chart(@ctx, @configuration) 152 | timer_resize = null 153 | window.addEventListener "resize", => 154 | @log "resize" 155 | clearInterval(timer_resize) 156 | setTimeout ( => @chart.resize()), 300 157 | 158 | handleLegendClick: (e) => 159 | @order_by = e.currentTarget.getAttribute("href").replace("#", "") 160 | @update() 161 | return false 162 | 163 | renderLabel: (stat, i) => 164 | if i % (@site_stats.length / 2) == 0 165 | r = 37 166 | else 167 | r = 40 168 | left = 50 + r * Math.sin(2 * Math.PI * i / @site_stats.length) 169 | top = 50 - r * Math.cos(2 * Math.PI * i / @site_stats.length) 170 | h("div.radar-label", {key: stat.address + i, style: "left: #{left}%; top: #{top}%", enterAnimation: Animation.show, exitAnimation: Animation.hide, delay: i*0.05}, 171 | h("a.title", {href: stat.site.getHref()}, stat.site.row.content.title) 172 | " " 173 | h("span.value", " (#{Text.formatSize(stat[@order_by]) or "No data yet"})") 174 | ) 175 | 176 | render: => 177 | if @need_update 178 | @update() 179 | @need_update = false 180 | label_i = 0 181 | 182 | h("div.ChartRadar", [ 183 | h("div.radar-container", [ 184 | h("div.radar-labels", @site_stats.map (stat) => 185 | label = @renderLabel(stat, label_i) 186 | label_i += 1 187 | return label 188 | ) 189 | h("div.canvas-container", 190 | h("canvas", {width: 600, height: 600, afterCreate: @initCanvas}) 191 | ) 192 | ]) 193 | h("div.radar-legends", @legends.map (legend) => 194 | h("a.radar-legend", {id: legend.id, classes: {active: @order_by == legend.id}, onclick: @handleLegendClick, href: "#" + legend.id}, [ 195 | h("div.legend-box", {style: "background-color: #{legend.color}"}), 196 | h("span.title", legend.title) 197 | ]) 198 | ) 199 | 200 | 201 | ]) 202 | 203 | window.ChartRadar = ChartRadar 204 | -------------------------------------------------------------------------------- /js/PageStats/ChartTimeline.coffee: -------------------------------------------------------------------------------- 1 | class ChartTimeline extends Class 2 | constructor: -> 3 | @items = [] 4 | for i in [6..0] 5 | @items.push {id: i, title: "\u200B", data: "\u200B", value: i, active: false} 6 | @active_id = 0 7 | @chart_ctx = null 8 | @need_update = false 9 | @line_data = null 10 | 11 | initChart: (node) => 12 | @chart_canvas = node 13 | @chart_ctx = node.getContext("2d") 14 | 15 | if @line_data 16 | @updateChart() 17 | 18 | updateChart: => 19 | @chart_ctx.clearRect(0, 0, @chart_canvas.width, @chart_canvas.height) 20 | 21 | @chart_ctx.lineWidth = 0 22 | @chart_ctx.fillStyle = '#EDC54B' 23 | 24 | @chart_ctx.beginPath() 25 | 26 | @chart_ctx.moveTo(-10,0) 27 | data_max = Math.max.apply(null, @line_data) 28 | data_last_i = (i for val, i in @line_data when val > 0).pop() 29 | line_width = 1400 / @line_data.length 30 | 31 | if not data_last_i? 32 | return # No data yet 33 | 34 | for data, i in @line_data 35 | line_x = i * line_width 36 | line_y = parseInt(101 - (data / data_max) * 100) 37 | @chart_ctx.lineTo(line_x, line_y) 38 | if i == data_last_i 39 | break 40 | @chart_ctx.lineTo(line_x, 120) 41 | @chart_ctx.lineTo(0, 120) 42 | 43 | @chart_ctx.fill() 44 | 45 | # Dashed line at the end 46 | if data_last_i > 36 47 | @chart_ctx.beginPath() 48 | @chart_ctx.lineWidth = 0 49 | @chart_ctx.strokeStyle = '#EDC54B' 50 | @chart_ctx.setLineDash [0, 1, 1] 51 | @chart_ctx.moveTo(line_x, line_y) 52 | @chart_ctx.lineTo(1500, line_y) 53 | @chart_ctx.stroke() 54 | 55 | update: => 56 | query = """ 57 | SELECT 58 | MAX(date_added) AS date_added, AVG(value) AS avg, SUM(value) AS sum 59 | FROM data 60 | WHERE type_id = :type_id AND date_added >= :date_added_from AND date_added <= :date_added_to 61 | GROUP BY strftime('%Y-%m-%d %H', date_added, 'unixepoch', 'localtime') 62 | ORDER BY date_added DESC 63 | """ 64 | 65 | if Page.params.interval == "1w" 66 | c = new Date() 67 | c.setDate(c.getDate() - (c.getDay() or 7) + 7) 68 | date_added_to = c.setHours(23,59,59,0) / 1000 69 | interval_step = 60 * 60 * 24 * 7 70 | date_added_from = date_added_to - interval_step * 7 71 | group_steps = 6 72 | 73 | else if Page.params.interval == "1m" 74 | c = new Date() 75 | c.setDate(30) 76 | date_added_to = c.setHours(23,59,59,0) / 1000 77 | interval_step = 60 * 60 * 24 * 30 78 | date_added_from = date_added_to - interval_step * 30 79 | group_steps = 24 * 3 80 | else 81 | date_added_to = (new Date()).setHours(23,59,59,0) / 1000 82 | interval_step = 60 * 60 * 24 83 | date_added_from = date_added_to - interval_step * 7 84 | group_steps = 2 85 | 86 | step = 60 * 60 87 | type_id = Page.page_stats.type_id_db["file_bytes_sent"] 88 | data = {} 89 | day_total = {} 90 | Page.cmd "chartDbQuery", [query, {type_id: type_id, date_added_from: date_added_from, date_added_to: date_added_to}], (res) => 91 | @logStart "Parse result", res.length 92 | 93 | @line_data = [] 94 | for row in res 95 | data[Math.ceil(row.date_added / step) * step] = row.sum 96 | day_string = Time.dateIso(row.date_added * 1000) 97 | day_total[day_string] ?= 0 98 | day_total[day_string] += row.sum 99 | 100 | data_date_added = Math.ceil(date_added_from / step) * step 101 | while data_date_added <= date_added_to 102 | group_step_data = 0 103 | for i in [0..group_steps] 104 | group_step_data += data[data_date_added] or 0 105 | data_date_added += step 106 | @line_data.push(group_step_data) 107 | 108 | # Update links 109 | @items = [] 110 | for i in [7..1] 111 | data_from = date_added_to - i * interval_step + 1 112 | data_to = data_from + interval_step - 1 113 | 114 | if Page.params.interval == "1w" 115 | day_data = 0 116 | for x in [0..6] 117 | day_data += day_total[Time.dateIso(data_from + (60 * 60 * 24 * x))] or 0 118 | day_from = Time.date(data_from, "day") 119 | day_to = Time.date(data_from + interval_step - 1, "day") 120 | day_to = day_to.replace(day_from.split(" ")[0], "") # Only display month once if it's the same 121 | day_name = "#{day_from} - #{day_to}" 122 | else if Page.params.interval == "1m" 123 | day_data = 0 124 | for x in [0..30] 125 | day_data += day_total[Time.dateIso(data_from + (60 * 60 * 24 * x))] or 0 126 | day_name = Time.date(data_from, "month") 127 | else 128 | day_data = day_total[Time.dateIso(data_from)] 129 | day_name = Time.weekDay(data_from) 130 | @items.push {id: i, title: day_name, data: day_data, value: data_to} 131 | 132 | @logEnd "Parse result", "data: #{@line_data.length}" 133 | Page.projector.scheduleRender() 134 | @updateChart() 135 | 136 | renderItem: (item) -> 137 | date_added_to = Time.dateIso(item.value) 138 | if item.value >= Time.timestamp() 139 | date_added_to = "" 140 | classes = {active: (Page.params.date_added_to or "") == date_added_to} 141 | h("a.timeline-item", {key: item.title, enterAnimation: Animation.show, delay: item.id * 0.05, href: Page.createUrl("date_added_to", date_added_to), onclick: Page.handleLinkClick, classes: classes}, 142 | h("span.title", item.title), 143 | h("span.data", Text.formatSize(item.data) or "0 MB") 144 | ) 145 | 146 | render: => 147 | if @need_update 148 | @update() 149 | @need_update = false 150 | 151 | h("div.ChartTimeline", [ 152 | h("div.timeline-borders", @items.map (item) => 153 | date_added_to = Time.dateIso(item.value) 154 | if item.value >= Time.timestamp() 155 | date_added_to = "" 156 | h("div.timeline-border", {key: item.id, classes: {active: (Page.params.date_added_to or "") == date_added_to }}) 157 | ), 158 | h("canvas.chart", {afterCreate: @initChart, width: 1400, height: 100, data: @line_data?.length, delay: 0.3, updateAnimation: Animation.show}), 159 | h("div.timeline-items", @items.map (item) => 160 | @renderItem(item) 161 | ) 162 | ]) 163 | 164 | window.ChartTimeline = ChartTimeline 165 | -------------------------------------------------------------------------------- /js/PageStats/ChartWorld.coffee: -------------------------------------------------------------------------------- 1 | class ChartWorld extends Class 2 | constructor: -> 3 | @points = [] 4 | @need_update = false 5 | 6 | update: => 7 | ### 8 | @points = [ 9 | {lat: 0, lon: 0}, 10 | {lat: 30, lon: 30}, 11 | {lat: 33.137551, lon: 129.902344}, 12 | {lat: 0.351560, lon: 115.136719}, 13 | {lat: 40.178873, lon: -8.261719}, 14 | {lat: 52.482780, lon: -0.878906}, 15 | {lat: 47.040182, lon: 19.511719}, 16 | {lat: 38.548165, lon: -76.113281}, 17 | {lat: 40.446947, lon: -122.871094} 18 | {lat: -16.972741, lon: 46.582031} 19 | {lat: -35.173808, lon: 19.511719} 20 | {lat: -33.431441, lon: 116.542969} 21 | {lat: -45.336702, lon: 168.222656} 22 | {lat: -54.977614, lon: -67.412109} 23 | {lat: 8.928487, lon: -62.314453} 24 | {lat: 65.20515, lon: -14.670696} 25 | {lat: 64.90863, lon: -21.70194625} 26 | ] 27 | return false 28 | ### 29 | 30 | Page.cmd "chartGetPeerLocations", [], (res) => 31 | @points = res 32 | 33 | # Calculate country stat 34 | country_db = {} 35 | items = Page.page_stats.country_list.items 36 | items.length = 0 37 | for point in @points 38 | country_db[point.country] ?= 0 39 | country_db[point.country] += 1 40 | 41 | for country, num of country_db 42 | items.push({title: country, value: num}) 43 | 44 | items.sort (a, b) => 45 | return b.value - a.value 46 | 47 | if items.length > 15 48 | num_others = 0 49 | (num_others += item.value for item in items[14..]) 50 | items.length = 14 51 | items.push({title: "Other", value: num_others, type: "other"}) 52 | 53 | @drawPoints() 54 | Page.projector.scheduleRender() 55 | 56 | drawPoints: => 57 | @ctx.clearRect(0, 0, @canvas.width, @canvas.height) 58 | for point in @points 59 | left = (47 + (point.lon / 3.65)) * @canvas.width / 100 60 | top = (59 - (point.lat / 1.52)) * @canvas.height / 100 61 | @ctx.fillRect(left, top, 2, 2) 62 | 63 | initCanvas: (node) => 64 | @canvas = node 65 | @ctx = node.getContext("2d") 66 | @ctx.globalCompositeOperation = 'screen' 67 | @ctx.fillStyle = '#30758e'; 68 | @drawPoints() 69 | 70 | render: => 71 | if @need_update 72 | @update() 73 | @need_update = false 74 | 75 | h("div.ChartWorld", [ 76 | h("canvas.map-points", {width: 878, height: 371, afterCreate: @initCanvas}) 77 | h("img.map", {src: "img/world.png"}) 78 | ]) 79 | 80 | window.ChartWorld = ChartWorld -------------------------------------------------------------------------------- /js/PageStats/PageStats.coffee: -------------------------------------------------------------------------------- 1 | class PageStats extends Class 2 | constructor: -> 3 | @need_update = false 4 | @need_load_chartjs = true 5 | @chartjs_loaded = false 6 | @chart_timeline = new ChartTimeline() 7 | 8 | @chart_big = new ChartBig() 9 | @chart_big.types = [ 10 | {name: "file_bytes_sent", dataset_id: 0} 11 | {name: "file_bytes_recv", dataset_id: 1, negative: true} 12 | {name: "request_num_sent", dataset_id: 2, negative: true} 13 | {name: "request_num_recv", dataset_id: 3} 14 | ] 15 | 16 | @chart_legend = new ChartLegend() 17 | @chart_legend.items_left = [ 18 | {title: "Upload", getValue: ( => Text.formatSize(@chart_big.data_total.file_bytes_sent) ), color: "#1dfc59"}, 19 | {title: "Download", getValue: ( => Text.formatSize(@chart_big.data_total.file_bytes_recv) ), color: "#c94d47"}, 20 | {title: "Ratio", getValue: ( => @chart_big.data_total.file_bytes_sent / @chart_big.data_total.file_bytes_recv), type: "ratio", color: "#16ffe9"} 21 | ] 22 | @chart_legend.items_right = [ 23 | {title: "Sent", getValue: ( => @chart_big.data_total.request_num_sent ), post: "requests", dot: "\u2500", color: "#2da3b3"}, 24 | {title: "Received", getValue: ( => @chart_big.data_total.request_num_recv ), post: "requests", dot: "\u2500", color: "#80623f"} 25 | ] 26 | 27 | @chart_radar = new ChartRadar() 28 | 29 | # Chart connections 30 | @chart_connections = new Chart() 31 | @chart_connections.title = "Connections" 32 | @chart_connections.type_names = ["peer", "peer_onion", "connection", "connection_onion", "connection_in", "connection_ping_avg", "connection_ping_min"] 33 | @chart_connections.formatValue = (type_data) -> 34 | return "#{type_data.connection} of #{type_data.peer} peers" 35 | @chart_connections.formatDetails = (type_data) -> 36 | back = [] 37 | back.push "Onion: #{type_data.peer_onion} peers (#{type_data.connection_onion or 0} connections)" 38 | back.push "Incoming: #{Math.round(type_data.connection_in / type_data.connection * 100)}%" 39 | back.push "Ping avg: #{type_data.connection_ping_avg}ms (min: #{type_data.connection_ping_min}ms)" 40 | return back 41 | @chart_connections.chart_stroke = ["#608DECAA", "#D74C58FF"] 42 | @chart_connections.getChartQuery = -> 43 | "SELECT * FROM data WHERE type_id = #{Page.page_stats.type_id_db['connection']} ORDER BY date_added DESC LIMIT 50" 44 | 45 | # Chart size 46 | @chart_size = new Chart() 47 | @chart_size.getTitle = -> 48 | h("a", {href: Page.createUrl("bigchart", "size"), onclick: Page.handleLinkClick}, "Total size") 49 | @chart_size.chart_stroke = ["#F99739AA", "#51B8F2"] 50 | @chart_size.type_names = ["size", "size_optional", "optional_limit", "optional_used", "content"] 51 | @chart_size.formatValue = (type_data) -> 52 | return Text.formatSize(type_data.size) + " in #{Page.site_list.sites.length} sites" 53 | @chart_size.formatDetails = (type_data) -> 54 | back = [] 55 | back.push "Content sources: #{type_data.content} files" 56 | back.push "Optional downloaded: #{Text.formatSize(type_data.optional_used) or '0 MB'} of #{Text.formatSize(type_data.size_optional) or '0 MB'} (limit: #{Text.formatSize(type_data.optional_limit)})" 57 | return back 58 | @chart_size.getChartQuery = -> 59 | "SELECT CAST(value AS FLOAT) / 1024 / 1024 AS value FROM data WHERE type_id = #{Page.page_stats.type_id_db['size']} GROUP BY ROUND(date_added / 10000) ORDER BY date_added DESC LIMIT 50" 60 | 61 | @chart_world = new ChartWorld() 62 | @country_list = new StatList() 63 | 64 | @type_name_db = {} 65 | @type_id_db = {} 66 | 67 | @site_address_db = {} 68 | @site_id_db = {} 69 | 70 | setInterval ( => 71 | @need_update = true 72 | Page.projector.scheduleRender() 73 | ), 5 * 60 * 1000 74 | 75 | handleChartjsLoad: => 76 | @chartjs_loaded = true 77 | Page.projector.scheduleRender() 78 | 79 | update: => 80 | Page.cmd "chartDbQuery", "SELECT * FROM type", (res) => 81 | @type_id_db = {} 82 | @type_name_db = {} 83 | for row in res 84 | @type_id_db[row.name] = row.type_id 85 | @type_name_db[row.type_id] = row.name 86 | 87 | Page.cmd "chartDbQuery", "SELECT * FROM site", (res) => 88 | sites = {} 89 | @sites_by_id = {} 90 | for row in res 91 | @site_id_db[row.address] = row.site_id 92 | @site_address_db[row.site_id] = row.address 93 | 94 | @chart_big.need_update = true 95 | @chart_timeline.need_update = true 96 | @chart_connections.need_update = true 97 | @chart_size.need_update = true 98 | @chart_radar.need_update = true 99 | @chart_world.need_update = true 100 | Page.projector.scheduleRender() 101 | 102 | loadChartjs: => 103 | e = document.createElement("script") 104 | e.type = "text/javascript" 105 | e.src = "chartjs/chart.bundle.min.js" 106 | e.onload = @handleChartjsLoad 107 | document.body.appendChild e 108 | 109 | render: => 110 | if Page.server_info?.rev < 3220 111 | return h("div#PageStats", 112 | h("div.empty", [ 113 | h("h4", "Need update"), 114 | h("small", "You have to update to ZeroNet version 0.6.1 to use this feature.") 115 | ]) 116 | ) 117 | 118 | if @need_update 119 | @update() 120 | @need_update = false 121 | 122 | if not @need_update and document.body.className != "loaded" 123 | setTimeout ( -> document.body.classList.add("loaded") ), 1000 124 | 125 | if @need_load_chartjs 126 | setTimeout @loadChartjs, 500 127 | @need_load_chartjs = false 128 | 129 | intervals = ["1w", "1d"] 130 | 131 | h("div#PageStats", [ 132 | h("div.intervals", intervals.map (interval) => 133 | if interval == "1d" 134 | interval_param = undefined 135 | else 136 | interval_param = interval 137 | h("a.interval", {href: Page.createUrl("interval", interval_param), onclick: Page.handleLinkClick, classes: {active: interval_param == Page.params.interval}}, interval) 138 | ) 139 | @chart_timeline.render(), 140 | if @chartjs_loaded then [ 141 | @chart_big.render() 142 | @chart_legend.render() 143 | @chart_radar.render() 144 | h("div.Charts", [ 145 | @chart_connections.render(), 146 | @chart_size.render() 147 | ]) 148 | @chart_world.render() 149 | @country_list.render() 150 | ] 151 | ]) 152 | 153 | 154 | window.PageStats = PageStats 155 | -------------------------------------------------------------------------------- /js/PageStats/StatList.coffee: -------------------------------------------------------------------------------- 1 | class StatList extends Class 2 | constructor: -> 3 | @items = [] 4 | 5 | renderItem: (item) => 6 | h("div.stat-list-item", {key: item.title, classes: {other: item.type == "other"}}, 7 | h("div.title", item.title), 8 | h("div.value", item.value + " peers") 9 | ) 10 | 11 | render: => 12 | h("div.StatList", [ 13 | h("h4", "Top country") 14 | h("div.stat-list-items", @items.map(@renderItem)), 15 | ]) 16 | 17 | window.StatList = StatList -------------------------------------------------------------------------------- /js/lib/Class.coffee: -------------------------------------------------------------------------------- 1 | class Class 2 | trace: true 3 | 4 | log: (args...) -> 5 | return unless @trace 6 | return if typeof console is 'undefined' 7 | args.unshift("[#{@.constructor.name}]") 8 | console.log(args...) 9 | @ 10 | 11 | logStart: (name, args...) -> 12 | return unless @trace 13 | @logtimers or= {} 14 | @logtimers[name] = +(new Date) 15 | @log "#{name}", args..., "(started)" if args.length > 0 16 | @ 17 | 18 | logEnd: (name, args...) -> 19 | ms = +(new Date)-@logtimers[name] 20 | @log "#{name}", args..., "(Done in #{ms}ms)" 21 | @ 22 | 23 | window.Class = Class -------------------------------------------------------------------------------- /js/lib/Promise.coffee: -------------------------------------------------------------------------------- 1 | # From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html 2 | 3 | class Promise 4 | @when: (tasks...) -> 5 | num_uncompleted = tasks.length 6 | args = new Array(num_uncompleted) 7 | promise = new Promise() 8 | 9 | for task, task_id in tasks 10 | ((task_id) -> 11 | task.then(() -> 12 | args[task_id] = Array.prototype.slice.call(arguments) 13 | num_uncompleted-- 14 | promise.complete.apply(promise, args) if num_uncompleted == 0 15 | ) 16 | )(task_id) 17 | 18 | return promise 19 | 20 | constructor: -> 21 | @resolved = false 22 | @end_promise = null 23 | @result = null 24 | @callbacks = [] 25 | 26 | resolve: -> 27 | if @resolved 28 | return false 29 | @resolved = true 30 | @data = arguments 31 | if not arguments.length 32 | @data = [true] 33 | @result = @data[0] 34 | for callback in @callbacks 35 | back = callback.apply callback, @data 36 | if @end_promise 37 | @end_promise.resolve(back) 38 | 39 | fail: -> 40 | @resolve(false) 41 | 42 | then: (callback) -> 43 | if @resolved == true 44 | callback.apply callback, @data 45 | return 46 | 47 | @callbacks.push callback 48 | 49 | @end_promise = new Promise() 50 | 51 | window.Promise = Promise 52 | 53 | ### 54 | s = Date.now() 55 | log = (text) -> 56 | console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ") 57 | 58 | log "Started" 59 | 60 | cmd = (query) -> 61 | p = new Promise() 62 | setTimeout ( -> 63 | p.resolve query+" Result" 64 | ), 100 65 | return p 66 | 67 | back = cmd("SELECT * FROM message").then (res) -> 68 | log res 69 | return "Return from query" 70 | .then (res) -> 71 | log "Back then", res 72 | 73 | log "Query started", back 74 | ### -------------------------------------------------------------------------------- /js/lib/Property.coffee: -------------------------------------------------------------------------------- 1 | Function::property = (prop, desc) -> 2 | Object.defineProperty @prototype, prop, desc 3 | -------------------------------------------------------------------------------- /js/lib/Prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::startsWith = (s) -> @[...s.length] is s 2 | String::endsWith = (s) -> s is '' or @[-s.length..] is s 3 | String::repeat = (count) -> new Array( count + 1 ).join(@) 4 | 5 | window.isEmpty = (obj) -> 6 | for key of obj 7 | return false 8 | return true 9 | -------------------------------------------------------------------------------- /js/utils/Animation.coffee: -------------------------------------------------------------------------------- 1 | class Animation 2 | slideDown: (elem, props) -> 3 | if (elem.offsetTop > 1000 and elem.getBoundingClientRect().top > 1000) or props.animate_disable 4 | return 5 | 6 | h = elem.offsetHeight 7 | cstyle = window.getComputedStyle(elem) 8 | margin_top = cstyle.marginTop 9 | margin_bottom = cstyle.marginBottom 10 | padding_top = cstyle.paddingTop 11 | padding_bottom = cstyle.paddingBottom 12 | transition = cstyle.transition 13 | 14 | elem.style.boxSizing = "border-box" 15 | elem.style.overflow = "hidden" 16 | elem.style.transform = "scale(0.6)" 17 | elem.style.opacity = "0" 18 | elem.style.height = "0px" 19 | elem.style.marginTop = "0px" 20 | elem.style.marginBottom = "0px" 21 | elem.style.paddingTop = "0px" 22 | elem.style.paddingBottom = "0px" 23 | elem.style.transition = "none" 24 | 25 | setTimeout (-> 26 | elem.className += " animate-inout" 27 | elem.style.height = h+"px" 28 | elem.style.transform = "scale(1)" 29 | elem.style.opacity = "1" 30 | elem.style.marginTop = margin_top 31 | elem.style.marginBottom = margin_bottom 32 | elem.style.paddingTop = padding_top 33 | elem.style.paddingBottom = padding_bottom 34 | ), 1 35 | 36 | elem.addEventListener "transitionend", -> 37 | elem.classList.remove("animate-inout") 38 | elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null 39 | elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null 40 | elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null 41 | elem.removeEventListener "transitionend", arguments.callee, false 42 | 43 | 44 | slideUp: (elem, remove_func, props) -> 45 | if elem.offsetTop > 1000 and elem.getBoundingClientRect().top > 1000 46 | return remove_func() 47 | 48 | elem.className += " animate-back" 49 | elem.style.boxSizing = "border-box" 50 | elem.style.height = elem.offsetHeight+"px" 51 | elem.style.overflow = "hidden" 52 | elem.style.transform = "scale(1)" 53 | elem.style.opacity = "1" 54 | elem.style.pointerEvents = "none" 55 | setTimeout (-> 56 | elem.style.height = "0px" 57 | elem.style.marginTop = "0px" 58 | elem.style.marginBottom = "0px" 59 | elem.style.paddingTop = "0px" 60 | elem.style.paddingBottom = "0px" 61 | elem.style.transform = "scale(0.8)" 62 | elem.style.borderTopWidth = "0px" 63 | elem.style.borderBottomWidth = "0px" 64 | elem.style.opacity = "0" 65 | ), 1 66 | elem.addEventListener "transitionend", (e) -> 67 | if e.propertyName == "opacity" or e.elapsedTime >= 0.6 68 | elem.removeEventListener "transitionend", arguments.callee, false 69 | remove_func() 70 | 71 | 72 | slideUpInout: (elem, remove_func, props) -> 73 | elem.className += " animate-inout" 74 | elem.style.boxSizing = "border-box" 75 | elem.style.height = elem.offsetHeight+"px" 76 | elem.style.overflow = "hidden" 77 | elem.style.transform = "scale(1)" 78 | elem.style.opacity = "1" 79 | elem.style.pointerEvents = "none" 80 | setTimeout (-> 81 | elem.style.height = "0px" 82 | elem.style.marginTop = "0px" 83 | elem.style.marginBottom = "0px" 84 | elem.style.paddingTop = "0px" 85 | elem.style.paddingBottom = "0px" 86 | elem.style.transform = "scale(0.8)" 87 | elem.style.borderTopWidth = "0px" 88 | elem.style.borderBottomWidth = "0px" 89 | elem.style.opacity = "0" 90 | ), 1 91 | elem.addEventListener "transitionend", (e) -> 92 | if e.propertyName == "opacity" or e.elapsedTime >= 0.6 93 | elem.removeEventListener "transitionend", arguments.callee, false 94 | remove_func() 95 | 96 | 97 | showRight: (elem, props) -> 98 | elem.className += " animate" 99 | elem.style.opacity = 0 100 | elem.style.transform = "TranslateX(-20px) Scale(1.01)" 101 | setTimeout (-> 102 | elem.style.opacity = 1 103 | elem.style.transform = "TranslateX(0px) Scale(1)" 104 | ), 1 105 | elem.addEventListener "transitionend", -> 106 | elem.classList.remove("animate") 107 | elem.style.transform = elem.style.opacity = null 108 | 109 | 110 | show: (elem, props) -> 111 | delay = arguments[arguments.length-2]?.delay*1000 or 1 112 | elem.style.opacity = 0 113 | setTimeout (-> 114 | elem.className += " animate" 115 | ), 1 116 | setTimeout (-> 117 | elem.style.opacity = 1 118 | ), delay 119 | elem.addEventListener "transitionend", -> 120 | elem.classList.remove("animate") 121 | elem.style.opacity = null 122 | elem.removeEventListener "transitionend", arguments.callee, false 123 | 124 | hide: (elem, remove_func, props) -> 125 | delay = arguments[arguments.length-2]?.delay*1000 or 1 126 | elem.className += " animate" 127 | setTimeout (-> 128 | elem.style.opacity = 0 129 | ), delay 130 | elem.addEventListener "transitionend", (e) -> 131 | if e.propertyName == "opacity" 132 | remove_func() 133 | 134 | addVisibleClass: (elem, props) -> 135 | setTimeout -> 136 | elem.classList.add("visible") 137 | 138 | window.Animation = new Animation() -------------------------------------------------------------------------------- /js/utils/Dollar.coffee: -------------------------------------------------------------------------------- 1 | window.$ = (selector) -> 2 | if selector.startsWith("#") 3 | return document.getElementById(selector.replace("#", "")) 4 | -------------------------------------------------------------------------------- /js/utils/ItemList.coffee: -------------------------------------------------------------------------------- 1 | class ItemList 2 | constructor: (@item_class, @key) -> 3 | @items = [] 4 | @items_bykey = {} 5 | 6 | sync: (rows, item_class, key) -> 7 | @items.splice(0, @items.length) # Empty items 8 | for row in rows 9 | current_obj = @items_bykey[row[@key]] 10 | if current_obj 11 | current_obj.row = row 12 | @items.push current_obj 13 | else 14 | item = new @item_class(row, @) 15 | @items_bykey[row[@key]] = item 16 | @items.push item 17 | 18 | deleteItem: (item) -> 19 | index = @items.indexOf(item) 20 | if index > -1 21 | @items.splice(index, 1) 22 | else 23 | console.log "Can't delete item", item 24 | delete @items_bykey[item.row[@key]] 25 | 26 | window.ItemList = ItemList -------------------------------------------------------------------------------- /js/utils/Menu.coffee: -------------------------------------------------------------------------------- 1 | class Menu 2 | constructor: -> 3 | @visible = false 4 | @items = [] 5 | @node = null 6 | @height = 0 7 | @direction = "bottom" 8 | 9 | show: => 10 | window.visible_menu?.hide() 11 | @visible = true 12 | window.visible_menu = @ 13 | @direction = @getDirection() 14 | 15 | hide: => 16 | @visible = false 17 | 18 | toggle: => 19 | if @visible 20 | @hide() 21 | else 22 | @show() 23 | Page.projector.scheduleRender() 24 | 25 | 26 | addItem: (title, cb, selected=false) -> 27 | @items.push([title, cb, selected]) 28 | 29 | 30 | storeNode: (node) => 31 | @node = node 32 | # Animate visible 33 | if @visible 34 | node.className = node.className.replace("visible", "") 35 | setTimeout (=> 36 | node.className += " visible" 37 | node.attributes.style.value = @getStyle() 38 | ), 20 39 | node.style.maxHeight = "none" 40 | @height = node.offsetHeight 41 | node.style.maxHeight = "0px" 42 | @direction = @getDirection() 43 | 44 | getDirection: => 45 | if @node and @node.parentNode.getBoundingClientRect().top + @height + 60 > document.body.clientHeight and @node.parentNode.getBoundingClientRect().top - @height > 0 46 | return "top" 47 | else 48 | return "bottom" 49 | 50 | handleClick: (e) => 51 | keep_menu = false 52 | for item in @items 53 | [title, cb, selected] = item 54 | if title == e.currentTarget.textContent or e.currentTarget["data-title"] == title 55 | keep_menu = cb?(item) 56 | break 57 | if keep_menu != true and cb != null 58 | @hide() 59 | return false 60 | 61 | renderItem: (item) => 62 | [title, cb, selected] = item 63 | if typeof(selected) == "function" 64 | selected = selected() 65 | 66 | if title == "---" 67 | return h("div.menu-item-separator", {key: Time.timestamp()}) 68 | else 69 | if cb == null 70 | href = undefined 71 | onclick = @handleClick 72 | else if typeof(cb) == "string" # Url 73 | href = cb 74 | onclick = true 75 | else # Callback 76 | href = "#"+title 77 | onclick = @handleClick 78 | classes = { 79 | "selected": selected, 80 | "noaction": (cb == null) 81 | } 82 | return h("a.menu-item", {href: href, onclick: onclick, "data-title": title, key: title, classes: classes}, title) 83 | 84 | getStyle: => 85 | if @visible 86 | max_height = @height 87 | else 88 | max_height = 0 89 | style = "max-height: #{max_height}px" 90 | if @direction == "top" 91 | style += ";margin-top: #{0 - @height - 50}px" 92 | else 93 | style += ";margin-top: 0px" 94 | return style 95 | 96 | render: (class_name="") => 97 | if @visible or @node 98 | h("div.menu#{class_name}", {classes: {"visible": @visible}, style: @getStyle(), afterCreate: @storeNode}, @items.map(@renderItem)) 99 | 100 | window.Menu = Menu 101 | 102 | # Hide menu on outside click 103 | document.body.addEventListener "mouseup", (e) -> 104 | if not window.visible_menu or not window.visible_menu.node 105 | return false 106 | menu_node = window.visible_menu.node 107 | menu_parents = [menu_node, menu_node.parentNode] 108 | if e.target.parentNode not in menu_parents and e.target.parentNode.parentNode not in menu_parents 109 | window.visible_menu.hide() 110 | Page.projector.scheduleRender() 111 | -------------------------------------------------------------------------------- /js/utils/Prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::startsWith = (s) -> @[...s.length] is s 2 | String::endsWith = (s) -> s is '' or @[-s.length..] is s 3 | String::capitalize = -> if @.length then @[0].toUpperCase() + @.slice(1) else "" 4 | String::repeat = (count) -> new Array( count + 1 ).join(@) 5 | 6 | window.isEmpty = (obj) -> 7 | for key of obj 8 | return false 9 | return true 10 | -------------------------------------------------------------------------------- /js/utils/RateLimit.coffee: -------------------------------------------------------------------------------- 1 | limits = {} 2 | call_after_interval = {} 3 | window.RateLimit = (interval, fn) -> 4 | if not limits[fn] 5 | call_after_interval[fn] = false 6 | fn() # First call is not delayed 7 | limits[fn] = setTimeout (-> 8 | if call_after_interval[fn] 9 | fn() 10 | delete limits[fn] 11 | delete call_after_interval[fn] 12 | ), interval 13 | else # Called within iterval, delay the call 14 | call_after_interval[fn] = true 15 | -------------------------------------------------------------------------------- /js/utils/RateLimitCb.coffee: -------------------------------------------------------------------------------- 1 | last_time = {} 2 | calling = {} 3 | calling_iterval = {} 4 | call_after_interval = {} 5 | 6 | # Rate limit function call and don't allow to run in parallel (until callback is called) 7 | window.RateLimitCb = (interval, fn, args=[]) -> 8 | cb = -> # Callback when function finished 9 | left = interval - (Date.now() - last_time[fn]) # Time life until next call 10 | # console.log "CB, left", left, "Calling:", calling[fn] 11 | if left <= 0 # No time left from rate limit interval 12 | delete last_time[fn] 13 | if calling[fn] # Function called within interval 14 | RateLimitCb(interval, fn, calling[fn]) 15 | delete calling[fn] 16 | else # Time left from rate limit interval 17 | setTimeout (-> 18 | delete last_time[fn] 19 | if calling[fn] # Function called within interval 20 | RateLimitCb(interval, fn, calling[fn]) 21 | delete calling[fn] 22 | ), left 23 | if last_time[fn] # Function called within interval 24 | calling[fn] = args # Schedule call and update arguments 25 | else # Not called within interval, call instantly 26 | last_time[fn] = Date.now() 27 | fn.apply(this, [cb, args...]) 28 | 29 | 30 | window.RateLimit = (interval, fn) -> 31 | if calling_iterval[fn] > interval 32 | clearInterval calling[fn] 33 | delete calling[fn] 34 | 35 | if not calling[fn] 36 | call_after_interval[fn] = false 37 | fn() # First call is not delayed 38 | calling_iterval[fn] = interval 39 | calling[fn] = setTimeout (-> 40 | if call_after_interval[fn] 41 | fn() 42 | delete calling[fn] 43 | delete call_after_interval[fn] 44 | ), interval 45 | else # Called within iterval, delay the call 46 | call_after_interval[fn] = true 47 | 48 | 49 | ### 50 | window.s = Date.now() 51 | window.load = (done, num) -> 52 | console.log "Loading #{num}...", Date.now()-window.s 53 | setTimeout (-> done()), 1000 54 | 55 | RateLimit 500, window.load, [0] # Called instantly 56 | RateLimit 500, window.load, [1] 57 | setTimeout (-> RateLimit 500, window.load, [300]), 300 58 | setTimeout (-> RateLimit 500, window.load, [600]), 600 # Called after 1000ms 59 | setTimeout (-> RateLimit 500, window.load, [1000]), 1000 60 | setTimeout (-> RateLimit 500, window.load, [1200]), 1200 # Called after 2000ms 61 | setTimeout (-> RateLimit 500, window.load, [3000]), 3000 # Called after 3000ms 62 | ### -------------------------------------------------------------------------------- /js/utils/Text.coffee: -------------------------------------------------------------------------------- 1 | class MarkedRenderer extends marked.Renderer 2 | image: (href, title, text) -> 3 | return ("![#{text}](#{href})") 4 | 5 | class Text 6 | toColor: (text, saturation=30, lightness=50) -> 7 | hash = 0 8 | for i in [0..text.length-1] 9 | hash += text.charCodeAt(i)*i 10 | hash = hash % 1777 11 | return "hsl(" + (hash % 360) + ",#{saturation}%,#{lightness}%)"; 12 | 13 | 14 | renderMarked: (text, options={}) -> 15 | options["gfm"] = true 16 | options["breaks"] = true 17 | options["sanitize"] = true 18 | options["renderer"] = marked_renderer 19 | text = marked(text, options) 20 | return @fixHtmlLinks text 21 | 22 | emailLinks: (text) -> 23 | return text.replace(/([a-zA-Z0-9]+)@zeroid.bit/g, "$1@zeroid.bit") 24 | 25 | # Convert zeronet html links to relaitve 26 | fixHtmlLinks: (text) -> 27 | if window.is_proxy 28 | return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="http://zero') 29 | else 30 | return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="') 31 | 32 | # Convert a single link to relative 33 | fixLink: (link) -> 34 | if window.is_proxy 35 | back = link.replace(/http:\/\/(127.0.0.1|localhost):43110/, 'http://zero') 36 | return back.replace(/http:\/\/zero\/([^\/]+\.bit)/, "http://$1") # Domain links 37 | else 38 | return link.replace(/http:\/\/(127.0.0.1|localhost):43110/, '') 39 | 40 | toUrl: (text) -> 41 | return text.replace(/[^A-Za-z0-9]/g, "+").replace(/[+]+/g, "+").replace(/[+]+$/, "") 42 | 43 | getSiteUrl: (address) -> 44 | if window.is_proxy 45 | if "." in address # Domain 46 | return "http://"+address+"/" 47 | else 48 | return "http://zero/"+address+"/" 49 | else 50 | return "/"+address+"/" 51 | 52 | 53 | fixReply: (text) -> 54 | return text.replace(/(>.*\n)([^\n>])/gm, "$1\n$2") 55 | 56 | toBitcoinAddress: (text) -> 57 | return text.replace(/[^A-Za-z0-9]/g, "") 58 | 59 | 60 | jsonEncode: (obj) -> 61 | return unescape(encodeURIComponent(JSON.stringify(obj))) 62 | 63 | jsonDecode: (obj) -> 64 | return JSON.parse(decodeURIComponent(escape(obj))) 65 | 66 | fileEncode: (obj) -> 67 | if typeof(obj) == "string" 68 | return btoa(unescape(encodeURIComponent(obj))) 69 | else 70 | return btoa(unescape(encodeURIComponent(JSON.stringify(obj, undefined, '\t')))) 71 | 72 | utf8Encode: (s) -> 73 | return unescape(encodeURIComponent(s)) 74 | 75 | utf8Decode: (s) -> 76 | return decodeURIComponent(escape(s)) 77 | 78 | 79 | distance: (s1, s2) -> 80 | s1 = s1.toLocaleLowerCase() 81 | s2 = s2.toLocaleLowerCase() 82 | next_find_i = 0 83 | next_find = s2[0] 84 | match = true 85 | extra_parts = {} 86 | for char in s1 87 | if char != next_find 88 | if extra_parts[next_find_i] 89 | extra_parts[next_find_i] += char 90 | else 91 | extra_parts[next_find_i] = char 92 | else 93 | next_find_i++ 94 | next_find = s2[next_find_i] 95 | 96 | if extra_parts[next_find_i] 97 | extra_parts[next_find_i] = "" # Extra chars on the end doesnt matter 98 | extra_parts = (val for key, val of extra_parts) 99 | if next_find_i >= s2.length 100 | return extra_parts.length + extra_parts.join("").length 101 | else 102 | return false 103 | 104 | 105 | parseQuery: (query) -> 106 | params = {} 107 | parts = query.split('&') 108 | for part in parts 109 | [key, val] = part.split("=") 110 | if val 111 | params[decodeURIComponent(key)] = decodeURIComponent(val) 112 | else 113 | params["url"] = decodeURIComponent(key) 114 | return params 115 | 116 | encodeQuery: (params) -> 117 | back = [] 118 | if params.url 119 | back.push(params.url) 120 | for key, val of params 121 | if not val or key == "url" 122 | continue 123 | back.push("#{encodeURIComponent(key)}=#{encodeURIComponent(val)}") 124 | return back.join("&") 125 | 126 | highlight: (text, search) -> 127 | if not text 128 | return [""] 129 | parts = text.split(RegExp(search, "i")) 130 | back = [] 131 | for part, i in parts 132 | back.push(part) 133 | if i < parts.length-1 134 | back.push(h("span.highlight", {key: i}, search)) 135 | return back 136 | 137 | formatSize: (size) -> 138 | if isNaN(parseInt(size)) 139 | return "" 140 | size_mb = size/1024/1024 141 | if size_mb >= 1000 142 | return (size_mb/1024).toFixed(1)+" GB" 143 | else if size_mb >= 100 144 | return size_mb.toFixed(0)+" MB" 145 | else if size/1024 >= 1000 146 | return size_mb.toFixed(2)+" MB" 147 | else 148 | return (parseInt(size)/1024).toFixed(2)+" KB" 149 | 150 | window.marked_renderer = new MarkedRenderer() 151 | window.is_proxy = (document.location.host == "zero" or window.location.pathname == "/") 152 | window.Text = new Text() 153 | -------------------------------------------------------------------------------- /js/utils/Time.coffee: -------------------------------------------------------------------------------- 1 | class Time 2 | since: (timestamp) -> 3 | now = +(new Date)/1000 4 | if timestamp > 1000000000000 # In ms 5 | timestamp = timestamp/1000 6 | secs = now - timestamp 7 | if secs < 60 8 | back = "Just now" 9 | else if secs < 60*60 10 | minutes = Math.round(secs/60) 11 | back = "" + minutes + " minutes ago" 12 | else if secs < 60*60*24 13 | back = "#{Math.round(secs/60/60)} hours ago" 14 | else if secs < 60*60*24*3 15 | back = "#{Math.round(secs/60/60/24)} days ago" 16 | else 17 | back = "on "+@date(timestamp) 18 | back = back.replace(/^1 ([a-z]+)s/, "1 $1") # 1 days ago fix 19 | return back 20 | 21 | dateIso: (timestamp=null) -> 22 | if not timestamp 23 | timestamp = window.Time.timestamp() 24 | 25 | if timestamp > 1000000000000 # In ms 26 | timestamp = timestamp/1000 27 | tzoffset = (new Date()).getTimezoneOffset() * 60 28 | return (new Date((timestamp - tzoffset) * 1000)).toISOString().split("T")[0] 29 | 30 | date: (timestamp=null, format="short") -> 31 | if not timestamp 32 | timestamp = window.Time.timestamp() 33 | 34 | if timestamp > 1000000000000 # In ms 35 | timestamp = timestamp/1000 36 | parts = (new Date(timestamp * 1000)).toString().split(" ") 37 | if format == "short" 38 | display = parts.slice(1, 4) 39 | else if format == "day" 40 | display = parts.slice(1, 3) 41 | else if format == "month" 42 | display = [parts[1], parts[3]] 43 | else if format == "long" 44 | display = parts.slice(1, 5) 45 | return display.join(" ").replace(/( [0-9]{4})/, ",$1") 46 | 47 | weekDay: (timestamp) -> 48 | if timestamp > 1000000000000 # In ms 49 | timestamp = timestamp/1000 50 | return ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][ (new Date(timestamp * 1000)).getDay() ] 51 | 52 | timestamp: (date="") -> 53 | if date == "now" or date == "" 54 | return parseInt(+(new Date)/1000) 55 | else 56 | return parseInt(Date.parse(date)/1000) 57 | 58 | 59 | window.Time = new Time -------------------------------------------------------------------------------- /js/utils/Translate.coffee: -------------------------------------------------------------------------------- 1 | window._ = (s) -> return s -------------------------------------------------------------------------------- /js/utils/ZeroFrame.coffee: -------------------------------------------------------------------------------- 1 | class ZeroFrame extends Class 2 | constructor: (url) -> 3 | @url = url 4 | @waiting_cb = {} 5 | @wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1") 6 | @connect() 7 | @next_message_id = 1 8 | @history_state = {} 9 | @init() 10 | 11 | 12 | init: -> 13 | @ 14 | 15 | 16 | connect: -> 17 | @target = window.parent 18 | window.addEventListener("message", @onMessage, false) 19 | @cmd("innerReady") 20 | 21 | # Save scrollTop 22 | window.addEventListener "beforeunload", (e) => 23 | @log "save scrollTop", window.pageYOffset 24 | @history_state["scrollTop"] = window.pageYOffset 25 | @cmd "wrapperReplaceState", [@history_state, null] 26 | 27 | # Restore scrollTop 28 | @cmd "wrapperGetState", [], (state) => 29 | @history_state = state if state? 30 | @log "restore scrollTop", state, window.pageYOffset 31 | if window.pageYOffset == 0 and state 32 | window.scroll(window.pageXOffset, state.scrollTop) 33 | 34 | 35 | onMessage: (e) => 36 | message = e.data 37 | cmd = message.cmd 38 | if cmd == "response" 39 | if @waiting_cb[message.to]? 40 | @waiting_cb[message.to](message.result) 41 | delete @waiting_cb[message.to] 42 | else 43 | @log "Websocket callback not found:", message 44 | else if cmd == "wrapperReady" # Wrapper inited later 45 | @cmd("innerReady") 46 | else if cmd == "ping" 47 | @response message.id, "pong" 48 | else if cmd == "wrapperOpenedWebsocket" 49 | @onOpenWebsocket() 50 | else if cmd == "wrapperClosedWebsocket" 51 | @onCloseWebsocket() 52 | else 53 | @onRequest cmd, message.params 54 | 55 | 56 | onRequest: (cmd, message) => 57 | @log "Unknown request", message 58 | 59 | 60 | response: (to, result) -> 61 | @send {"cmd": "response", "to": to, "result": result} 62 | 63 | 64 | cmd: (cmd, params={}, cb=null) -> 65 | @send {"cmd": cmd, "params": params}, cb 66 | 67 | 68 | send: (message, cb=null) -> 69 | message.wrapper_nonce = @wrapper_nonce 70 | message.id = @next_message_id 71 | @next_message_id += 1 72 | @target.postMessage(message, "*") 73 | if cb 74 | @waiting_cb[message.id] = cb 75 | 76 | 77 | onOpenWebsocket: => 78 | @log "Websocket open" 79 | 80 | 81 | onCloseWebsocket: => 82 | @log "Websocket close" 83 | 84 | 85 | 86 | window.ZeroFrame = ZeroFrame 87 | -------------------------------------------------------------------------------- /languages/da.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Åben", 3 | "Closed": "Lukket", 4 | "_(Disabled)": "Slået fra", 5 | "_(Error)": "Fejl", 6 | "Available": "Tilgængelig", 7 | "Always": "Altid", 8 | "Status: ": "Status: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "OK. Din \" + Page.server_info.fileserver_port + \" port er åben.", 10 | "Re-check opened port": "Check port igen", 11 | "How to make Tor connection work?": "Hvordan bruges TOR i ZeroNet?", 12 | "How to use ZeroNet in Tor Browser?": "Hvordan bruges ZeroNet i en TOR browser?", 13 | "Disable always Tor mode": "Brug ikke TOR altid", 14 | "Enable Tor for every connection (slower)": "Brug altid TOR (langsommere)", 15 | "Help to keep this project alive": "Hjælp med dette projekt", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Opdater til senest version...
Genstart venligst ZeroNet manuelt hvis ZeroNet ikke starter automatisk i løbet af et par minutter.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer er ikke fuldt supporteret i ZeroNet. Overvej evt. at bruge Chrome eller Firefox", 18 | 19 | "Welcome to ZeroNet": "Velkommen til ZeroNet", 20 | "Let's build a decentralized Internet together!": "Lad os bygge et decentralt internet sammen!", 21 | "This site currently served by ": "Denne side deles af ", 22 | " peers, without any central server.": " klienter uden en central server.", 23 | "Some sites we created:": "Nogle sider vi har oprettet:", 24 | "Simple messaging board": "Simpel besked side", 25 | "Reddit-like, decentralized forum": "Reddit lignende decentralt forum", 26 | "Activate \\u2501": "Aktiver \\u2501", 27 | "Microblogging platform": "ZeroNet mini blog", 28 | "End-to-end encrypted mailing": "ZeroNet krypteret mail", 29 | "P2P social network": "ZeroNet socialt network", 30 | 31 | "Update all sites": "Opdater alle sider", 32 | "Order sites by peers": "Sorter sider efter antal klienter", 33 | "Order sites by update time": "Sorter sider efter sidst opdateret", 34 | "Order sites by add time": "Sorter sider efter oprettelses tidspunkt", 35 | "Order sites by size": "Sorter sider efter størrelse", 36 | "Version ": "Version: ", 37 | "New version avalible!": "Ny version klar!", 38 | "Up to date!": "Opdateret!!", 39 | "Shut down ZeroNet": "Luk ZeroNet", 40 | 41 | "_(Sites)": "Sider", 42 | "_(Files)": "Filer", 43 | "Favorited sites:": "Favorit sider:", 44 | "Connected sites:": "Nuværende sider:", 45 | "More sites:": "Flere sider:", 46 | " file update failed": " fil opdatering fejlede", 47 | "No peers": "Ingen klienter", 48 | "Update failed": "Opdatering fejlede", 49 | "Updating...": "Opdaterer...", 50 | "Updated!": "Opdateret!", 51 | "Updating: ": "Opdaterer: ", 52 | " left": " fájl", 53 | "Are you sure?": "Er du sikker?", 54 | "Activate \\u00BB": "Aktiver \\u00BB", 55 | "Unfavorite": "Fjern favorit", 56 | "Favorite": "Favorit", 57 | "Update": "Opdater", 58 | "Clone": "Klon", 59 | "Resume": "Aktiv", 60 | "Pause": "Pause", 61 | "Check files": "Tjek filer", 62 | "Delete": "Slet", 63 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Du kan desværre ikke slette din egen side her.", 64 | 65 | "Search in connected sites": "Søg i nuværende sider", 66 | " results from ": " resultat fra ", 67 | " sites in ": " sider i ", 68 | "s": "er", 69 | "Comment on": "Kommenterede:", 70 | "You got mentioned in": "Du blev nævnt i:", 71 | "You got mentioned": "Du blev nævnt:", 72 | 73 | "Used: ": "Brugt: ", 74 | "Edit optional files limit": "Opdater grænse for valgfri data", 75 | "Optional files limit:": "Grænse:", 76 | "Cancel": "Fortryd", 77 | "Set": "Sæt", 78 | 79 | "Selected:": "Valgt:", 80 | " files": " filer", 81 | "Pin": "Bevar", 82 | "UnPin": "Frigør", 83 | 84 | "Optional": "Valgfri", 85 | "Help distribute all new files": "Hjælp med at dele alle nye filer", 86 | 87 | "Optional file": "Valgfri fil", 88 | "Size": "Størrelse", 89 | "Peers": "Klienter", 90 | "Uploaded": "Uploadet", 91 | "Downloaded": "Downloadet", 92 | "Finished": "Klar", 93 | "Pinned": "Fastgjort", 94 | 95 | "More files...": "Flere filer...", 96 | 97 | " minutes ago": " minutte(r) siden", 98 | " hours ago": " time(r) siden", 99 | " days ago": " dag(e) siden", 100 | "on ": "", 101 | "Just now": "Lige nu" 102 | } 103 | -------------------------------------------------------------------------------- /languages/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Abierto", 3 | "Closed": "Cerrado", 4 | "_(Disabled)": "Desactivado", 5 | "_(Error)": "Error", 6 | "Status: ": "Estado: ", 7 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "¡Bien! Tu puerto \" + Page.server_info.fileserver_port + \" está abierto.", 8 | "Re-check opened port": "Volver a comprobar puerto abierto", 9 | "How to make Tor connection work?": "¿Cómo hacer funcionar una conexión Tor?", 10 | "How to use ZeroNet in Tor Browser?": "¿Cómo usar ZeroNet en un navegador Tor?", 11 | "Disable always Tor mode": "Desactivar siempre el modo Tor", 12 | "Enable Tor for every connection (slower)": "Activar Tor para cada conexión (más lento)", 13 | "Help to keep this project alive": "Ayuda a mantener vivo este proyecto", 14 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Actualizando a la última versión...
Por favor, reinicia ZeroNet manualmente si no responde en los próximos minutos.", 15 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "El navegador Internet Explorer no está completamente soportado por ZeroNet, por favor considera cambiar a Chrome o Firefox.", 16 | 17 | "Welcome to ZeroNet": "Bienvenido a ZeroNet", 18 | "Let's build a decentralized Internet together!": "¡Construyamos juntos un Internet descentralizado!", 19 | "This site currently served by ": "Este sitio está actualmente proporcionado por ", 20 | " peers, without any central server.": " entre pares, sin un servidor central.", 21 | "Some sites we created:": "Algunos sitios que hemos creado:", 22 | "Simple messaging board": "Panel de mensajes simples", 23 | "Reddit-like, decentralized forum": "Foro descentralizado, estilo Reddit", 24 | "Activate \\u2501": "Activar \\u2501", 25 | "Microblogging platform": "Plataforma de microblogging", 26 | "End-to-end encrypted mailing": "Emails cifrados extremo a extremo", 27 | "P2P social network": "Red social P2P", 28 | 29 | "Update all sites": "Actualizar todos los sitios", 30 | "Order sites by peers": "Ordenar sitios por pares", 31 | "Order sites by update time": "Ordenar sitios por fecha de actualización", 32 | "Order sites by add time": "Ordenar sitios por fecha de creación", 33 | "Order sites by size": "Ordenar sitios por tamaño", 34 | "Version ": "Versión: ", 35 | "New version avalible!": "¡Nueva versión disponible!", 36 | "Up to date!": "¡Actualizado!", 37 | "Shut down ZeroNet": "Desconectar ZeroNet", 38 | 39 | "_(Sites)": "Sitios", 40 | "_(Files)": "Ficheros", 41 | "Favorited sites:": "Sitios favoritos", 42 | "Connected sites:": "Sitios conectados", 43 | "More sites:": "Más sitios:", 44 | " file update failed": " actualización de fichero fallida", 45 | "No peers": "No se han encontrado pares", 46 | "Update failed": "Actualización fallido", 47 | "Updating...": "Actualizando...", 48 | "Updated!": "¡Actualizado!", 49 | "Updating: ": "Actualizando: ", 50 | " left": " restantes", 51 | "Are you sure?": "¿Estás seguro?", 52 | "Activate \\u00BB": "Activar \\u00BB", 53 | "Unfavorite": "Eliminar de favoritos", 54 | "Favorite": "Añadir a favoritos", 55 | "Update": "Actualizar", 56 | "Clone": "Clonar", 57 | "Resume": "Continuar", 58 | "Pause": "Pausar", 59 | "Check files": "Comprobar ficheros", 60 | "Delete": "Eliminar", 61 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Lo siento, no puedes eliminar tu propio sitio.
Por favor, elimina el directorio manualmente.", 62 | 63 | "Search in connected sites": "Buscar en sitios conectados", 64 | " results from ": " resultados de ", 65 | " sites in ": " sitios en ", 66 | "s": "s", 67 | "Comment on": "Comentar:", 68 | "You got mentioned in": "Se te mencionó en:", 69 | "You got mentioned": "Se te mencionó:", 70 | 71 | "Used: ": "Usado: ", 72 | "Edit optional files limit": "Editar el límite de ficheros opcionales", 73 | "Optional files limit:": "Límite:", 74 | "Cancel": "Cancelar", 75 | "Set": "Establecer", 76 | 77 | "Selected:": "Seleccionados:", 78 | " files": "ficheros", 79 | "Pin": "Fijar con alfiler", 80 | "UnPin": "Quitar alfiler", 81 | 82 | "Optional": "Opcional", 83 | "Help distribute all new files": "Ayuda a distribuir todos los nuevos ficheros", 84 | 85 | "Optional file": "Fichero opcional", 86 | "Size": "Tamaño", 87 | "Peers": "Pares", 88 | "Uploaded": "Subido", 89 | "Downloaded": "Descargado", 90 | "Finished": "Terminado", 91 | "Pinned": "Fijado", 92 | 93 | "More files...": "Más ficheros...", 94 | 95 | " minutes ago": " minutos antes", 96 | " hours ago": " horas antes", 97 | " days ago": " dias antes", 98 | "on ": "en ", 99 | "Just now": "Ahora mismo" 100 | } 101 | -------------------------------------------------------------------------------- /languages/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Ouvert", 3 | "Closed": "Fermé", 4 | "_(Disabled)": "Désactivé", 5 | "_(Error)": "Erreur", 6 | "Available": "Disponible", 7 | "Always": "Toujours", 8 | "Status: ": "Statut: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "OK ! Le port\" + Page.server_info.fileserver_port + \" est ouvert", 10 | "Re-check opened port": "Vérifier le port", 11 | "How to make Tor connection work?": "Comment se connecter via Tor ?", 12 | "How to use ZeroNet in Tor Browser?": "Comment utiliser ZeroNet avec le navigateur Tor ?", 13 | "Disable always Tor mode": "Désactiver le mode Tor", 14 | "Enable Tor for every connection (slower)": "Activer Tor sur toutes les connections (plus lent)", 15 | "Help to keep this project alive": "Soutenir ZeroNet", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Installation de la mise à jour...
Redémarrez ZeroNet manuellement si la connection n'est pas rétablie dans quelques minutes", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer n'est pas correctement supporté par ZeroNet, utilisez plutôt Chrome ou Firefox", 18 | 19 | "Welcome to ZeroNet": "Bienvenue sur ZeroNet", 20 | "Let's build a decentralized Internet together!": "Construisons un internet décentralisé ensemble !", 21 | "This site currently served by ": "Cette page est présentement servie par ", 22 | " peers, without any central server.": " pairs, sans aucun serveur central.", 23 | "Some sites we created:": "Quelques sites que nous avons créés:", 24 | "Simple messaging board": "Babillard minimaliste", 25 | "Reddit-like, decentralized forum": "Forum décentralisé à la Reddit", 26 | "Activate \\u2501": "Activer \\u2501", 27 | "Microblogging platform": "Plate-forme de microblogue", 28 | "End-to-end encrypted mailing": "Messagerie chiffrée de bout en bout", 29 | "P2P social network": "Réseau social pair-à-pair", 30 | 31 | "Update all sites": "Mettre à jour les sites", 32 | "Order sites by peers": "Classer par nombre de pairs", 33 | "Order sites by update time": "Classer par date de mise à jour", 34 | "Order sites by add time": "Classer par date de création", 35 | "Order sites by size": "Classer par taille", 36 | "Version ": "Version ", 37 | "New version avalible!": "Nouvelle version disponible !", 38 | "Up to date!": "Dernière version !", 39 | "Shut down ZeroNet": "Éteindre ZeroNet", 40 | 41 | "_(Sites)": "Sites", 42 | "_(Files)": "Fichiers", 43 | "Favorited sites:": "Favoris:", 44 | "Connected sites:": "Connectés:", 45 | "More sites:": "Suggestions:", 46 | " file update failed": " mises à jour échouées", 47 | "No peers": "Aucun pair", 48 | "Update failed": "Échec de la mise à jour", 49 | "Updating...": "Mise à jour...", 50 | "Updated!": "À jour!", 51 | "Updating: ": "", 52 | " left": " fichier(s)", 53 | "Are you sure?": "Êtes-vous certain ?", 54 | "Activate \\u00BB": "Activer \\u00BB", 55 | "Unfavorite": "Retirer des favoris", 56 | "Favorite": "Ajouter aux favoris", 57 | "Update": "Mettre à jour", 58 | "Clone": "Cloner", 59 | "Resume": "Reprendre", 60 | "Pause": "Suspendre", 61 | "Check files": "Vérifier les fichiers", 62 | "Delete": "Supprimer", 63 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Vous ne pouvez pas supprimer votre propre site.
Veuillez supprimer le dossier manuellement.", 64 | 65 | "Search in connected sites": "Rechercher dans les sites connectés", 66 | " results from ": " résultats de ", 67 | " sites in ": " sites en ", 68 | "s": "s", 69 | "Comment on": "Commentaire sur", 70 | "You got mentioned in": "Vous avez été mentionné dans", 71 | "You got mentioned": "Vous avez été mentionné", 72 | 73 | "Used: ": "Utilisé : ", 74 | "Edit optional files limit": "Modifier la limite de fichiers optionnels", 75 | "Optional files limit:": "Limite :", 76 | "Cancel": "Annuler", 77 | "Set": "Modifier", 78 | 79 | "Selected:": "Sélectionné :", 80 | " files": " fichiers", 81 | "Pin": "Épingler", 82 | "UnPin": "Détacher", 83 | 84 | "Optional": "Optionnel", 85 | "Help distribute all new files": "Participez à la distribution de tous les nouveaux fichiers", 86 | 87 | "Optional file": "Fichier optionnel", 88 | "Size": "Taille", 89 | "Peers": "Pairs", 90 | "Uploaded": "Envoyé", 91 | "Downloaded": "Téléchargé", 92 | "Finished": "Terminé", 93 | "Pinned": "Épinglé", 94 | 95 | "More files...": "Plus de fichiers...", 96 | 97 | "\" + minutes + \" minutes ago": "il y a \" + minutes + \" minutes", 98 | " hours ago": " heures", 99 | " days ago": " jours", 100 | "on ": "le ", 101 | "Just now": "À l'instant" 102 | } 103 | -------------------------------------------------------------------------------- /languages/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Nyitva", 3 | "Closed": "Bezárva", 4 | "_(Disabled)": "Kikapcsolva", 5 | "_(Error)": "Hiba", 6 | "Available": "Elérhető", 7 | "Always": "Mindig", 8 | "Status: ": "Állapot: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Király! A \" + Page.server_info.fileserver_port + \" portod nyitva van.", 10 | "Re-check opened port": "Port újraellenörzése", 11 | "How to make Tor connection work?": "Hogyan lehet bekapcsolni a Tor kapcsolatot?", 12 | "How to use ZeroNet in Tor Browser?": "Hogyan használjam a ZeroNet-et a Tor böngészőben?", 13 | "Disable always Tor mode": "Tor mindig mód kikapcsolása", 14 | "Enable Tor for every connection (slower)": "Tor használata minden kapcsolatra (lassabb)", 15 | "Help to keep this project alive": "Segíts életben tartani a projectet", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Frissítés a legújabb verzióra...
Amennyiben automatikusan nem indulna újra a program kérjük, indítsd újra manuálisan.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Az Internet Explorer nem teljesen támogatott böngésző, kérjuk válts Chrome-ra vagy Firefox-ra", 18 | 19 | "Welcome to ZeroNet": "Üdv a ZeroNet-en", 20 | "Let's build a decentralized Internet together!": "Építsünk egy decentralizált Internetet közösen!", 21 | "This site currently served by ": "Ez azt az oldalt jelenleg ", 22 | " peers, without any central server.": " számítógép szolgálja ki bármilyen központi szerver nélkül.", 23 | "Some sites we created:": "Pár oldal, amit mi csináltunk:", 24 | "Simple messaging board": "Egyszerű üzenő fal", 25 | "Reddit-like, decentralized forum": "Reddit-szerű, decentralizált fórum", 26 | "Activate \\u2501": "Aktiválás \\u2501", 27 | "Microblogging platform": "Mini blog-motor", 28 | "End-to-end encrypted mailing": "Ponttól-ponting titkosított üzenetküldő", 29 | "P2P social network": "P2P szociális hálózat", 30 | 31 | "Update all sites": "Minden oldal frissítése", 32 | "Order sites by peers": "Rendezés csatlakozási pontok szerint", 33 | "Order sites by update time": "Rendezés frissesség szerint", 34 | "Order sites by add time": "Rendezés hozzáadás ideje szerint", 35 | "Order sites by size": "Rendezés méret szerint", 36 | "Version ": "Verzió: ", 37 | "New version avalible!": "Új verzió elérhető", 38 | "Up to date!": "Naprakész!", 39 | "Shut down ZeroNet": "ZeroNet leállítása", 40 | 41 | "_(Sites)": "Oldalak", 42 | "_(Files)": "Fájlok", 43 | "Needs your interaction:": "Megerősítés szükséges:", 44 | "Favorited sites:": "Kedvenc oldalak:", 45 | "Connected sites:": "Elérhető oldalak:", 46 | "More sites:": "Több oldal:", 47 | " file update failed": " hiányzó fájl", 48 | "No peers": "Nincs kapcsolat", 49 | "Update failed": "Sikertelen frissítés", 50 | "Updating...": "Frissítés...", 51 | "Updated!": "Frissítve!", 52 | "Updating: ": "Frissítés: ", 53 | " left": " fájl", 54 | "Are you sure?": "Biztos vagy benne?", 55 | "Activate \\u00BB": "Aktiválás \\u00BB", 56 | "Unfavorite": "Eltávolítás a kedvencek közül", 57 | "Favorite": "Kedvencekhez adás", 58 | "Update": "Frissítés", 59 | "Clone": "Klónozás", 60 | "Resume": "Frissítés folytatása", 61 | "Pause": "Frissítés szünteltetése", 62 | "Check files": "Fájlok ellenőrzése", 63 | "Delete": "Törlés", 64 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "A saját oldaladat nem tudod törölni
Kérjük, távolítsd el kézzel.", 65 | 66 | "Search in connected sites": "Keresés az elérhető oldalakban", 67 | " results from ": " találat ", 68 | " sites in ": " oldalban ", 69 | "s": "mp", 70 | "Comment on": "Hozzászólás:", 71 | "You got mentioned in": "Megemlítés:", 72 | "You got mentioned": "Megemlítés:", 73 | 74 | "Used: ": "Felhasznált: ", 75 | "Edit optional files limit": "Opcionális fájlok korlátjának megadása", 76 | "Optional files limit:": "Hely korlát:", 77 | "Cancel": "Mégse", 78 | "Set": "Alkalmaz", 79 | 80 | "Selected:": "Kijelölve:", 81 | " files": " fájl", 82 | "Pin": "Rögzít", 83 | "UnPin": "Rögzítés feloldása", 84 | 85 | "Optional": "Opcionális", 86 | "Help distribute all new files": "Segítség az összes új fájl terjesztésében", 87 | 88 | "Optional file": "Opcionális fájl", 89 | "Size": "Méret", 90 | "Peers": "Peerek", 91 | "Uploaded": "Feltöltés", 92 | "Downloaded": "Letöltés", 93 | "Finished": "Letöltve", 94 | "Pinned": "Rögzítve", 95 | 96 | "More files...": "Több fájl...", 97 | 98 | " minutes ago": " perce", 99 | " hours ago": " órája", 100 | " days ago": " napja", 101 | "on ": "", 102 | "Just now": "Épp most" 103 | } -------------------------------------------------------------------------------- /languages/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Aperta", 3 | "Closed": "Chiusa", 4 | "_(Disabled)": "Disabilitato", 5 | "_(Error)": "Errore", 6 | "Available": "Attivo", 7 | "Always": "Sempre", 8 | "Status: ": "Stato: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Bene! La tua porta \" + Page.server_info.fileserver_port + \" è aperta.", 10 | "Re-check opened port": "Ricontrolla porta aperta", 11 | "How to make Tor connection work?": "Come far funzionare la connessione Tor?", 12 | "How to use ZeroNet in Tor Browser?": "Come usare ZeroNet con Tor Browser?", 13 | "Disable always Tor mode": "Disabilita modalità sempre-Tor", 14 | "Enable Tor for every connection (slower)": "Abilita Tor per ogni connessione (più lento)", 15 | "Help to keep this project alive": "Aiuta a mantenere vivo questo progetto", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Aggiornamento all'ultima versione...
Si prega di riavviare ZeroNet se non riappare nei prossimi minuti.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer non è browser completamente supportato da ZeroNet, si prega di considerare di passare a Chrome o Firefox", 18 | 19 | "Welcome to ZeroNet": "Benvenuto su ZeroNet", 20 | "Let's build a decentralized Internet together!": "Costruiamo insieme un'Internet decentralizzata!", 21 | "This site currently served by ": "Questo sito è attualmente fornito da ", 22 | " peers, without any central server.": " peers, senza alcun server centrale.", 23 | "Some sites we created:": "Alcuni siti che abbiamo creato:", 24 | "Simple messaging board": "Semplice spazio di messaggistica", 25 | "Reddit-like, decentralized forum": "Forum decentralizzato, stile Reddit", 26 | "Activate \\u2501": "Attiva \\u2501", 27 | "Microblogging platform": "ZeroNet mini blog", 28 | "End-to-end encrypted mailing": "Email con crittografia end-to-end", 29 | "P2P social network": "P2P social network", 30 | 31 | "Update all sites": "Aggiorna tutti i siti", 32 | "Order sites by peers": "Ordina i siti per N° di peers", 33 | "Order sites by update time": "Ordina i siti per ultimo aggiornamento", 34 | "Order sites by add time": "Ordina siti per data di aggiunta", 35 | "Order sites by size": "Ordina siti per dimensione", 36 | "Create new, empty site": "Crea nuovo sito vuoto", 37 | "Version ": "Versione: ", 38 | "New version avalible!": "Nuova versione disponibile!", 39 | "Up to date!": "Aggiornato!", 40 | "Shut down ZeroNet": "Spegni ZeroNet", 41 | "You need ZeroNet 0.5.2 to use this feature.": "Hai bisogno di aggiornare alla versione 0.5.2 di ZeroNet per usare questa funzione", 42 | "Update to latest development version?": "Vuoi aggiornare all'ultima versione disponibile ?", 43 | 44 | "Muted": "Utenti bloccati", 45 | "\u2039 Back to feed": "\u2039 Indietro", 46 | "Your mute list is empty! :)": "La tua lista utenti bloccati è vuota! :)", 47 | "Why?": "Perche?", 48 | 49 | 50 | "_(Sites)": "Siti", 51 | "_(Files)": "File", 52 | "Favorited sites:": "Siti preferiti:", 53 | "Connected sites:": "Siti connessi:", 54 | "More sites:": "Altri siti:", 55 | " file update failed": " aggiornamento file fallito", 56 | "No peers": "Nessun peer", 57 | "Update failed": "Aggiornamento fallito!", 58 | "Updating...": "Aggiornamento...", 59 | "Updated!": "Aggiornato!", 60 | "Updating: ": "Aggiornamento: ", 61 | " left": " mancanti", 62 | "Are you sure?": "Sei sicuro?", 63 | "Activate \\u00BB": "Attiva \\u00BB", 64 | "Unfavorite": "Non preferito", 65 | "Favorite": "Preferito", 66 | "Update": "Aggiorna", 67 | "Clone": "Clona", 68 | "Resume": "Riprendi", 69 | "Pause": "Ferma", 70 | "Check files": "Controlla file", 71 | "Delete": "Cancella", 72 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Mi spiace, non puoi cancellare il tuo sito.
Si prega di rimuovere la cartella manualmente.", 73 | 74 | "Search in connected sites": "Cerca nei siti connessi", 75 | " results from ": " risultati da ", 76 | " sites in ": " siti in ", 77 | "s": "s", 78 | "Comment on": "Commento su:", 79 | "You got mentioned in": "Sei stato menzionato in:", 80 | "You got mentioned": "Sei stato menzionato:", 81 | 82 | "Used: ": "Usato: ", 83 | "Edit optional files limit": "Modifica limite file facoltativi", 84 | "Optional files limit:": "Limite:", 85 | "Cancel": "Cancella", 86 | "Set": "Imposta", 87 | 88 | "Selected:": "Selezionati:", 89 | " files": " file", 90 | "Pin": "Appunta", 91 | "UnPin": "Spunta", 92 | 93 | "Optional": "Facoltativo", 94 | "Help distribute all new files": "Aiuta a distribuire nuovi file", 95 | 96 | "Optional file": "File facoltativo", 97 | "Size": "Dimensione", 98 | "Peers": "Peers", 99 | "Uploaded": "Caricato", 100 | "Downloaded": "Scaricato", 101 | "Finished": "Finito", 102 | "Pinned": "Appuntato", 103 | 104 | "More files...": "Altri file...", 105 | 106 | " minutes ago": " minuti fa", 107 | " hours ago": " ore fa", 108 | " days ago": " giorni fa", 109 | "on ": "a", 110 | "Just now": "Proprio ora" 111 | } 112 | -------------------------------------------------------------------------------- /languages/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Geopend", 3 | "Closed": "Gesloten", 4 | "_(Disabled)": "Uitgeschakeld", 5 | "_(Error)": "Fout", 6 | "Available": "Beschikbaar", 7 | "Always": "Altijd", 8 | "Status: ": "Status", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Geweldig! Je poort \" + Page.server_info.fileserver_port + \" is geopend.", 10 | "Re-check opened port": "Hercontroleer geopende poort", 11 | "How to make Tor connection work?": "Hoe zorg ik dat mijn Tor verbinding werkt?", 12 | "How to use ZeroNet in Tor Browser?": "Hoe gebruik ik ZeroNet binnen Tor Browser?", 13 | "Disable always Tor mode": "Altijd Tor modus uitschakelen", 14 | "Enable Tor for every connection (slower)": "Schakel Tor in voor elke verbinding (langzamer)", 15 | "Help to keep this project alive": "Help dit project in stand te houden", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Bijwerken naar de laatste versie...
Herstart Zeronet alsjeblieft met de hand als hij niet automatisch start in de komende minuten.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer wordt niet volledig ondersteund door ZeroNet, overweeg om naar Chrome of Firefox over te schakelen", 18 | 19 | "Welcome to ZeroNet": "Welkom bij ZeroNet", 20 | "Let's build a decentralized Internet together!": "Laten we samen een gedecentraliseerde Internet maken!", 21 | "This site currently served by ": "Deze site word geserveerd door ", 22 | " peers, without any central server.": " peers, zonder een enkele centrale server", 23 | "Some sites we created:": "Enkele van onze sites:", 24 | "Simple messaging board": "Simpel berichtenbord", 25 | "Reddit-like, decentralized forum": "Reddit-achtig, gedecentraliseerd forum", 26 | "Activate \\u2501": "Activeer \\u2501", 27 | "Microblogging platform": "Platform voor microblogs", 28 | "End-to-end encrypted mailing": "End-to-end geencrypt mailen", 29 | "P2P social network": "P2P sociaal netwerk", 30 | 31 | "Update all sites": "Alle sites bijwerken", 32 | "Order sites by peers": "Sorteer sites op peers", 33 | "Order sites by update time": "Sorteer sites op laatste update", 34 | "Order sites by add time": "Sorteer sites op laatste toevoeging", 35 | "Order sites by size": "Sorteer sites op grootte", 36 | "Version ": "Versie", 37 | "New version avalible!": "Nieuwe versie beschikbaar!", 38 | "Up to date!": "Up to date!", 39 | "Shut down ZeroNet": "ZeroNet afsluiten", 40 | 41 | "_(Sites)": "Sites", 42 | "_(Files)": "Bestanden", 43 | "Favorited sites:": "Favoriete sites:", 44 | "Connected sites:": "Verbonden sites:", 45 | "More sites:": "Meer sites:", 46 | " file update failed": " bestandsupdate mislukt", 47 | "No peers": "Geen peers", 48 | "Update failed": "Bijwerken mislukt", 49 | "Updating...": "Bijwerken...", 50 | "Updated!": "Bijgewerkt!", 51 | "Updating: ": "Bijwerken: ", 52 | " left": " te doen", 53 | "Are you sure?": "Zeker weten?", 54 | "Activate \\u00BB": "Activeer \\u00BB", 55 | "Unfavorite": "Niet meer favoriet", 56 | "Favorite": "Favoriet", 57 | "Update": "Bijwerken", 58 | "Clone": "Clonen", 59 | "Resume": "Hervatten", 60 | "Pause": "Pauzeren", 61 | "Check files": "Controleer bestanden", 62 | "Delete": "Verwijderen", 63 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Sorry, je kan je eigen site niet verwijderen.
Verwijder alsjeblieft de map met de hand.", 64 | 65 | "Search in connected sites": "Zoeken in verbonden sites", 66 | " results from ": " resultaten van", 67 | " sites in ": " sites in ", 68 | "s": "s", 69 | "Comment on": "Antwoord op", 70 | "You got mentioned in": "Je bent genoemd in", 71 | "You got mentioned": "Je bent genoemd", 72 | 73 | "Used: ": "Gebruikt: ", 74 | "Edit optional files limit": "Bewerk optionele bestandslimiet", 75 | "Optional files limit:": "Optionele bestandslimiet", 76 | "Cancel": "Annuleren", 77 | "Set": "Stel in", 78 | 79 | "Selected:": "Geselecteerd:", 80 | " files": " bestanden", 81 | "Pin": "Pinnen", 82 | "UnPin": "Ontpinnen", 83 | 84 | "Optional": "Optioneel", 85 | "Help distribute all new files": "Help met het verspreiden van alle nieuwe bestanden", 86 | 87 | "Optional file": "Optioneel bestand", 88 | "Size": "Grootte", 89 | "Peers": "Peers", 90 | "Uploaded": "Geupload", 91 | "Downloaded": "Gedownload", 92 | "Finished": "Voltooid", 93 | "Pinned": "Gepint", 94 | 95 | "More files...": "Meer bestanden...", 96 | 97 | " minutes ago": " minuten geleden", 98 | " hours ago": " uren geleden", 99 | " days ago": " dagen geleden", 100 | "on ": "op ", 101 | "Just now": "Zojuist" 102 | } 103 | -------------------------------------------------------------------------------- /languages/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Otwarty", 3 | "Closed": "Zamknięty", 4 | "_(Disabled)": "Zablokowany", 5 | "_(Error)": "Błąd", 6 | "Available": "Dostępny", 7 | "Always": "Zawsze", 8 | "Status: ": "Status: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Dobrze! Twój port \" + Page.server_info.fileserver_port + \" jest otwarty.", 10 | "Re-check opened port": "Sprawdź ponownie czy port jest otwarty", 11 | "How to make Tor connection work?": "Jak sprawić by działało połączenie z siecią Tor?", 12 | "How to use ZeroNet in Tor Browser?": "Jak używać ZeroNet w przeglądarce Tor Browser?", 13 | "Disable always Tor mode": "Wyłącz tryb 'zawsze Tor'", 14 | "Enable Tor for every connection (slower)": "Włącz Tora dla każdego połączenia (wolniej)", 15 | "Help to keep this project alive": "Pomóż utrzymać ten projekt żywym", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Aktualizowanie do najnowszej wersji...
Proszę uruchomić ponownie ZeroNet ręcznie, jeśli nie wróci w ciągu kilku minut.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer nie jest w pełni wspieraną przeglądarką przez ZeroNet, proszę skorzystać z innej przeglądarki jak Firefox bądź Chrome", 18 | 19 | "Welcome to ZeroNet": "Witamy w ZeroNet", 20 | "Let's build a decentralized Internet together!": "Zbudujmy zdecentralizowany Internet razem!", 21 | "This site currently served by ": "Ta strona jest aktualnie obsługiwana przez ", 22 | " peers, without any central server.": " użytkowników równorzędnych, bez żadnego centralnego serwera.", 23 | "Some sites we created:": "Niektóre strony które stworzyliśmy:", 24 | "Simple messaging board": "Proste forum dyskusyjne", 25 | "Reddit-like, decentralized forum": "Zdecentralizowane forum, w stylu Reddita", 26 | "Activate \\u2501": "Aktywny \\u2501", 27 | "Microblogging platform": "Platforma mikroblogów", 28 | "End-to-end encrypted mailing": "Zaszyfrowana poczta od końca do końca", 29 | "P2P social network": "Sieć społecznościowa P2P", 30 | 31 | "Update all sites": "Aktualizuj wszystkie strony", 32 | "Order sites by peers": "Sortuj według ilości udostępniających", 33 | "Order sites by update time": "Sortuj według czasu aktualizacji", 34 | "Order sites by add time": "Sortuj według czasu dodania", 35 | "Order sites by size": "Sortuj według rozmiaru stron", 36 | "Version ": "Wersja: ", 37 | "New version avalible!": "Nowa wersja dostępna", 38 | "Up to date!": "Aktualna!", 39 | "Shut down ZeroNet": "Wyłącz ZeroNet", 40 | 41 | "_(Sites)": "Strony", 42 | "_(Files)": "Pliki", 43 | "Needs your interaction:": "Potrzebna twoja interakcja:", 44 | "Favorited sites:": "Ulubione strony:", 45 | "Connected sites:": "Udostępniane strony:", 46 | "More sites:": "Więcej strony:", 47 | " file update failed": " aktualizacja pliku nie powiodła się", 48 | "No peers": "Brak użytkowników równorzędnych", 49 | "Update failed": "Aktualizacja nie powiodła się", 50 | "Updating...": "Aktualizowanie...", 51 | "Updated!": "Zaktualizowane!", 52 | "Updating: ": "Aktualizowanie: ", 53 | " left": " zostało", 54 | "Are you sure?": "Jesteś pewny?", 55 | "Activate \\u00BB": "Aktywny \\u00BB", 56 | "Unfavorite": "Usuń z ulubionych", 57 | "Favorite": "Ulubione", 58 | "Update": "Aktualizacja", 59 | "Clone": "Sklonuj", 60 | "Resume": "Wznów", 61 | "Pause": "Wstrzymaj", 62 | "Check files": "Sprawdź pliki", 63 | "Delete": "Usuń", 64 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Wybacz, niestety nie możesz usunąć swojej własnej strony
Proszę usunąć folder ze stroną ręcznie.", 65 | 66 | "Search in connected sites": "Poszukaj w połączonych stronach", 67 | " results from ": " rezultat z ", 68 | " sites in ": " strony w ", 69 | "s": " sek", 70 | "Comment on": "Skomentowane w:", 71 | "You got mentioned in": "Wspomniano o tobie w:", 72 | "You got mentioned": "Wspomniano o tobie:", 73 | 74 | "Used: ": "Użyte: ", 75 | "Edit optional files limit": "Edytuj limit plików opcjonalnych", 76 | "Optional files limit:": "Limit:", 77 | "Cancel": "Anuluj", 78 | "Set": "Ustaw", 79 | 80 | "Selected:": "Wybrane:", 81 | " files": " pliki", 82 | "Pin": "Przypnij", 83 | "UnPin": "Odepnij", 84 | 85 | "Optional": "Opcjonalny", 86 | "Help distribute all new files": "Pomóż udostępniać wszystkie nowe pliki", 87 | 88 | "Optional file": "Pliki opcjonalne", 89 | "Size": "Rozmiar", 90 | "Peers": "Użytkownicy", 91 | "Uploaded": "Wysłane", 92 | "Downloaded": "Pobrane", 93 | "Finished": "Skończone", 94 | "Pinned": "Przypięte", 95 | 96 | "More files...": "Więcej plików...", 97 | 98 | " minutes ago": " minut temu", 99 | " hours ago": " godzin temu", 100 | " days ago": " dni temu", 101 | "on ": "", 102 | "Just now": "Właśnie teraz", 103 | 104 | "_(Stats)": "Statystyki", 105 | "Recently downloaded:": "Ostatnio pobrane:", 106 | "Owned sites:": "Posiadane strony:", 107 | 108 | "Unsupported": "Niewspierany", 109 | 110 | "Theme: ": "Wygląd: ", 111 | "_(system)": "systemowy", 112 | "_(light)": "jasny", 113 | "_(dark)": "ciemny", 114 | 115 | "Language: ": "Język: ", 116 | "Create new, empty site": "Utwórz nową, pustą stronę", 117 | "Manage blocked users and sites": "Zarządzaj blokadą użytkowników i stron", 118 | "Configuration": "Konfiguracja", 119 | "Plugins": "Wtyczki", 120 | "Show data directory": "Pokaż folder z danymi", 121 | 122 | "Filter: Site name": "Filtr: Nazwa strony", 123 | "Filter: File name": "Filtr: Nazwa pliku", 124 | "Space allowed to used by optional files": "Przestrzeń dozwolona dla plików opcjonalnych", 125 | "Space current used by optional files": "Przestrzeń użyta przez pliki opcjonalne", 126 | "Total free space on your storage": "Całkowita wolna przestrzeń na urządzeniu", 127 | "Optional files on site: ": "Pliki opcjonalne na stronie: ", 128 | "Upload/Download ratio": "Stosunek wysłane/pobrane", 129 | 130 | "Top country": "Top państwa", 131 | "Other": "Inne", 132 | "Transferred data (last 7 days)": "Dane przesłane (ostatnie 7 dni)", 133 | "Site size": "Rozmiar strony", 134 | "Optional downloaded: ": "Pliki opcjonalne: ", 135 | " of ": " z ", 136 | " in ": " na ", 137 | " sites": " stronach", 138 | 139 | "Total size": "Łączny rozmiar", 140 | "Upload": "Wysłano", 141 | "Download": "Pobrano", 142 | "Sent": "Wysłano", 143 | "Received":"Odebrano", 144 | "requests": "żądań", 145 | " requests": " żądań", 146 | "ratio": "stosunek", 147 | "Ratio": "stosunek", 148 | "Connections": "Połączenia", 149 | " connections)": " połączenia)", 150 | " peers": " użytkowników", 151 | " peers (": " użytkowników (", 152 | "Incoming: ": "Przychodzące: ", 153 | "Ping avg: ": "Śr. ping: ", 154 | 155 | "Sunday": "Niedziela", 156 | "Monday": "Poniedziałek", 157 | "Tuesday": "Wtorek", 158 | "Wednesday": "Środa", 159 | "Thursday": "Czwartek", 160 | "Friday": "Piątek", 161 | "Saturday": "Sobota", 162 | 163 | "Site size limit: ": "Limit rozmiaru strony: ", 164 | 165 | "Save as .zip": "Zapisz jako .zip", 166 | "Checking": "Sprawdzanie", 167 | "Checking...": "Sprawdzanie...", 168 | "Good, your port is always closed when using ZeroNet in Tor always mode.": "Dobrze, twój port jest zamknięty kiedy używasz ZeroNet z Tor w trybie 'zawsze'.", 169 | "Warnings: ": "Ostrzeżenia: ", 170 | "Your browser is not safe": "Twoja przeglądarka nie jest bezpieczna", 171 | "To protect your anonymity you should use ZeroNet in the Tor browser.": "W celu ochrony Twojej anonimowości powinieneś korzystać w ZeroNet z przeglądarki Tor", 172 | "Tor always mode disabled, please restart your ZeroNet.": "Tryb 'zawsze Tor' został wyłączony, proszę uruchomić ponownie ZeroNet.", 173 | "Restart ZeroNet client": "Uruchom ponownie klienta ZeroNet", 174 | 175 | "Site": "Strona", 176 | "Taken": "Zajęło", 177 | "Feed": "Śledzenie", 178 | "from ": "z ", 179 | "All": "Wszystko", 180 | 181 | "anything site:SiteName": "cokolwiek site:SiteName", 182 | "Tip: Search in specific site using ": "Podpowiedź: Wyszukaj na konkretnej stronie korzystając z " 183 | } 184 | -------------------------------------------------------------------------------- /languages/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Aberta", 3 | "Closed": "Fechada", 4 | "_(Disabled)": "Desactivada", 5 | "_(Error)": "Erro", 6 | "Status: ": "Estado: ", 7 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Boa! A tua porta \" + Page.server_info.fileserver_port + \" está aberta.", 8 | "Re-check opened port": "Re-verificar porta aberta", 9 | "How to make Tor connection work?": "Como estabelecer ligação à rede Tor?", 10 | "How to use ZeroNet in Tor Browser?": "Como aceder à ZeroNet no Tor Browser?", 11 | "Disable always Tor mode": "Manter modo Tor sempre desactivado", 12 | "Enable Tor for every connection (slower)": "Activar Tpr para cada ligação (lento)", 13 | "Help to keep this project alive": "Ajuda a manter este projecto", 14 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "A actualizar para a última versão...
Por favor reinicia a ZeroNet manualmente se esta não o fizer automaticamente nos próximos minutos.", 15 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "O Internet Explorer não é completamente suportado pela ZeroNet, por favor considera utilizar Chrome ou Firefox.", 16 | 17 | "Welcome to ZeroNet": "Bem-vindo à ZeroNet", 18 | "Let's build a decentralized Internet together!": "Vamos construir uma Internet descentralizada unidos!", 19 | "This site currently served by ": "Este site está a ser disponibilizado por ", 20 | " peers, without any central server.": " peers, sem qualquer servidor central.", 21 | "Some sites we created:": "Alguns sites que já criámos:", 22 | "Simple messaging board": "Painel de mensagens simples", 23 | "Reddit-like, decentralized forum": "Fórum descentralizado, estilo Reddit", 24 | "Activate \\u2501": "Activar \\u2501", 25 | "Microblogging platform": "Plataforma de microblogging", 26 | "End-to-end encrypted mailing": "Emails cifrados ponta-a-ponta", 27 | "P2P social network": "Rede social P2P", 28 | 29 | "Update all sites": "Actualizar todos os sites", 30 | "Order sites by peers": "Ordenar sites por número de peers", 31 | "Order sites by update time": "Ordenar sites por data de actualização", 32 | "Order sites by add time": "Ordenar sites por data de criação", 33 | "Order sites by size": "Ordenar sites por tamanho", 34 | "Version ": "Versão: ", 35 | "New version avalible!": "Nova versão disponível", 36 | "Up to date!": "Actualizada!", 37 | "Shut down ZeroNet": "Desligar a minha ZeroNet", 38 | 39 | "_(Sites)": "Sites", 40 | "_(Files)": "Ficheiros", 41 | "Favorited sites:": "Sites favoritos", 42 | "Connected sites:": "Sites ligados", 43 | "More sites:": "Mais sites:", 44 | " file update failed": " actualização de ficheiro falhou", 45 | "No peers": "Nenhum peer encontrado", 46 | "Update failed": "Actualização falhou", 47 | "Updating...": "A actualizar...", 48 | "Updated!": "Actualizado!", 49 | "Updating: ": "A actualizar: ", 50 | " left": " restantes", 51 | "Are you sure?": "Tens a certeza?", 52 | "Activate \\u00BB": "Activar \\u00BB", 53 | "Unfavorite": "Remover dos favoritos", 54 | "Favorite": "Adicionar aos favoritos", 55 | "Update": "Actualizar", 56 | "Clone": "Clonar", 57 | "Resume": "Continuar", 58 | "Pause": "Suspender", 59 | "Check files": "Verificar ficheiros", 60 | "Delete": "Remover", 61 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Lamentamos, mas não podes remover o teu próprio site.
Por favor remove a directoria manualmente.", 62 | 63 | "Search in connected sites": "Pesquisar nos sites ligados", 64 | " results from ": " resultados de ", 65 | " sites in ": " sites em ", 66 | "s": "s", 67 | "Comment on": "Comentar:", 68 | "You got mentioned in": "Foste mencionado em:", 69 | "You got mentioned": "Foste mencionado:", 70 | 71 | "Used: ": "Usado: ", 72 | "Edit optional files limit": "Editar limite de ficheiros adicionais", 73 | "Optional files limit:": "Limite:", 74 | "Cancel": "Cancelar", 75 | "Set": "Definir", 76 | 77 | "Selected:": "Seleccionados:", 78 | " files": "ficheiros", 79 | "Pin": "Afixar", 80 | "UnPin": "Desafixar", 81 | 82 | "Optional": "Opcional", 83 | "Help distribute all new files": "Ajuda a distribuir todos os novos ficheiros", 84 | 85 | "Optional file": "Ficheiro opcional", 86 | "Size": "Tamanho", 87 | "Peers": "Peers", 88 | "Uploaded": "Submetido", 89 | "Downloaded": "Descarregado", 90 | "Finished": "Terminado", 91 | "Pinned": "Afixado", 92 | 93 | "More files...": "Mais ficheiros...", 94 | 95 | " minutes ago": " minutos atrás", 96 | " hours ago": " horas atrás", 97 | " days ago": " dias atrás", 98 | "on ": "a ", 99 | "Just now": "Agora mesmo" 100 | } 101 | -------------------------------------------------------------------------------- /languages/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Открыт", 3 | "Closed": "Закрыт", 4 | "_(Disabled)": "Отключен", 5 | "_(Error)": "Ошибка", 6 | "Available": "Доступно", 7 | "Always": "Всегда", 8 | "Status: ": "Статус: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Отлично. Ваш порт\" + Page.server_info.fileserver_port + \" открыт", 10 | "Re-check opened port": "Перепроверить статус порта", 11 | "How to make Tor connection work?": "Как подключится через Tor?", 12 | "How to use ZeroNet in Tor Browser?": "Как использовать ZeroNet в Tor Browser?", 13 | "Disable always Tor mode": "Отключить режим постоянного использования Tor", 14 | "Enable Tor for every connection (slower)": "Включить режим работы всех соединений через Tor (Медленно)", 15 | "Help to keep this project alive": "Поддержите проект ZeroNet", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Обновление на последнюю версию...
Если спустя пару минут ZeroNet недоступен - перезапустите приложение.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Работа ZeroNet в Internet Explorer не гарантируется, попробуйте Chrome или Firefox", 18 | 19 | "Welcome to ZeroNet": "Добро пожаловать в ZeroNet", 20 | "Let's build a decentralized Internet together!": "Построим распределенный Интернет вместе!", 21 | "This site currently served by ": "Этот сайт раздаётся ", 22 | " peers, without any central server.": " пирами, без какого бы то ни было центрального сервера.", 23 | "Some sites we created:": "Некоторые сайты, которые мы создали:", 24 | "Simple messaging board": "Простой чат", 25 | "Reddit-like, decentralized forum": "Форум", 26 | "Activate \\u2501": "Активировать \\u2501", 27 | "Microblogging platform": "Блог о разработке", 28 | "End-to-end encrypted mailing": "Почтовая система с E2E шифрованием", 29 | "P2P social network": "P2P Социальная сеть", 30 | 31 | "Update all sites": "Обновить все сайты", 32 | "Order sites by peers": "Сортировать сайты по пирам", 33 | "Order sites by update time": "Сортировать сайты по времени обновления", 34 | "Order sites by add time": "Сортировать сайты по добавлению", 35 | "Order sites by size": "Сортировать сайты по размеру", 36 | "Version ": "Версия ", 37 | "New version avalible!": "Доступна новая версия!", 38 | "Up to date!": "Актуальная версия!", 39 | "Shut down ZeroNet": "Выключить ZeroNet", 40 | 41 | "_(Sites)": "Сайты", 42 | "_(Files)": "Файлы", 43 | "Favorited sites:": "Избранное:", 44 | "Connected sites:": "Подписки:", 45 | "More sites:": "Больше сайтов:", 46 | " file update failed": " контент отсутствует", 47 | "No peers": "Нет пиров", 48 | "Update failed": "Ошибка при обновлении", 49 | "Updating...": "Обновление...", 50 | "Updated!": "Обновлено!", 51 | "Updating: ": "Ожидание: ", 52 | " left": " файла(ов)", 53 | "Are you sure?": "Вы уверены?", 54 | "Activate \\u00BB": "Подписаться \\u00BB", 55 | "Unfavorite": "Из избранного", 56 | "Favorite": "В избранное", 57 | "Update": "Обновить", 58 | "Clone": "Клонировать", 59 | "Resume": "Продолжить", 60 | "Pause": "Пауза", 61 | "Check files": "Проверить файлы", 62 | "Delete": "Удалить", 63 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Вы не можете удалить свой собственный сайт таким образом.
Пожалуйста удалить папку с сайтом в ручную.", 64 | 65 | "Search in connected sites": "Поиск в подписках", 66 | " results from ": " результаты с ", 67 | " sites in ": " сайтов за ", 68 | "s": " секунд", 69 | "Comment on": " Прокомментировал", 70 | "You got mentioned in": "Вас упомянули в", 71 | "You got mentioned": "Вас упомянули", 72 | 73 | "Used: ": "Использовано: ", 74 | "Edit optional files limit": "Редактировать размер опциональных файлов", 75 | "Optional files limit:": "Лимит:", 76 | "Cancel": "Отменить", 77 | "Set": "Изменить", 78 | 79 | "Selected:": "Выбрано:", 80 | " files": " файлов", 81 | "Pin": "Прикрепить", 82 | "UnPin": "Открепить", 83 | 84 | "Optional": "Опционально", 85 | "Help distribute all new files": "Помогите сайту распространять новый контент", 86 | 87 | "Optional file": "Опциональный файл", 88 | "Size": "Размер", 89 | "Peers": "Пиры", 90 | "Uploaded": "Отдано", 91 | "Downloaded": "Скачано", 92 | "Finished": "Закончено", 93 | "Pinned": "Прикрепленно", 94 | 95 | "More files...": "Больше файлов...", 96 | 97 | " minutes ago": " м. назад", 98 | " hours ago": " ч. назад", 99 | " days ago": " д. назад", 100 | "on ": " ", 101 | "Just now": "Cейчас" 102 | } 103 | -------------------------------------------------------------------------------- /languages/sk.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Otvorený", 3 | "Closed": "Zatvorený", 4 | "_(Disabled)": "Zrušené", 5 | "_(Error)": "Chyba", 6 | "Available": "Dostupné", 7 | "Always": "Vždy", 8 | "Status: ": "Status: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Výborne! tvoj port \" + Page.server_info.fileserver_port + \" je otvorený.", 10 | "Re-check opened port": "Znovu skontrolovať otvorenie portu", 11 | "How to make Tor connection work?": "Ako nastaviť a spustiť Tor?", 12 | "How to use ZeroNet in Tor Browser?": "Ako používať ZeroNet v Tor prehliadavači?", 13 | "Disable always Tor mode": "Vždy zakázať Tor mód", 14 | "Enable Tor for every connection (slower)": "Spustiť Tor pre každé spojenie (pomalšie)", 15 | "Help to keep this project alive": "Pomôžte udržať tento projekt pri živote", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Aktualizácia na najnovšiu verziu...
Prosím manuálne reštartujte ZeroNet ak sa za chvíľu sám znovu nespustí.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer nieje plne podporovaný ZeroNet-om, prosím zvážte použitie Chrom-u alebo Firefox-u.", 18 | 19 | "Welcome to ZeroNet": "Vitajte na ZeroNet-e", 20 | "Let's build a decentralized Internet together!": "Poďme spoločne vybudovať decentralizovaný internet!", 21 | "This site currently served by ": "Táto stránke je práve doručovaná ", 22 | " peers, without any central server.": " peer-mi, bez centrálneho serveru.", 23 | "Some sites we created:": "Niektoré stránky ktoré sme vytvorili:", 24 | "Simple messaging board": "Jednoduchý panel pre správy", 25 | "Reddit-like, decentralized forum": "Redditu podobné, decentralizované fórum", 26 | "Activate \\u2501": "Aktivovať \\u2501", 27 | "Microblogging platform": "Platforma pre Microblogging", 28 | "End-to-end encrypted mailing": "End-to-end šifrovaný mailing", 29 | "P2P social network": "P2P sociálna sieť", 30 | 31 | "Update all sites": "Aktualizovať všetky stránky", 32 | "Order sites by peers": "Zoradiť stránky podľa peer-ov", 33 | "Order sites by update time": "Zoradiť stránky podľa dátumu poslednej aktualizácie", 34 | "Order sites by add time": "Zoradiť stránky podľa dátumu pridania", 35 | "Order sites by size": "Zoradiť stránky podľa veľkosti", 36 | "Version ": "Verzia: ", 37 | "New version avalible!": "Dostupná nová verzia!", 38 | "Up to date!": "Verzia aktuálna!", 39 | "Shut down ZeroNet": "Vypnúť ZeroNet", 40 | 41 | "_(Sites)": "Stránky", 42 | "_(Files)": "Súbory", 43 | "Needs your interaction:": "Potrebný zásah:", 44 | "Favorited sites:": "Obľúbené stránky:", 45 | "Connected sites:": "Pripojené stránky:", 46 | "More sites:": "Viac stránok:", 47 | " file update failed": " aktualizácia súboru zlyhala", 48 | "No peers": "Žiadny peer-ovia", 49 | "Update failed": "Aktualizácia zlyhala", 50 | "Updating...": "Aktualizuje sa...", 51 | "Updated!": "Aktualizované!", 52 | "Updating: ": "Aktualizácia: ", 53 | " left": " zostáva", 54 | "Are you sure?": "Ste si istý?", 55 | "Activate \\u00BB": "Aktivovať \\u00BB", 56 | "Unfavorite": "Odstrániť z obľúbených", 57 | "Favorite": "Obľúbené", 58 | "Update": "Aktualizovať", 59 | "Clone": "Klonovať", 60 | "Resume": "Obnoviť", 61 | "Pause": "Pozastaviť", 62 | "Check files": "Kontrola súborov", 63 | "Delete": "Vymazať", 64 | "Save as .zip": "Uložiť ako .zip", 65 | 66 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Prepáčte, nieje možné vymazať vašu vlastnú stránku
Prosím vymažte zložku stránky manuálne.", 67 | 68 | "Search in connected sites": "Vyhľadávať v pripojených stránkach", 69 | " results from ": " výsledky z ", 70 | " sites in ": " stránky v ", 71 | "s": "", 72 | "from": "z", 73 | "Comment on": "Komentár na:", 74 | "You got mentioned in": "Niekto vás spomenul v:", 75 | "You got mentioned": "Niekto vás spomenul:", 76 | 77 | "Used: ": "Použité: ", 78 | "Edit optional files limit": "Upraviť limit pamäte pre voliteľné súbory", 79 | "Optional files limit:": "Limit pamäte voliteľných súborov:", 80 | "Cancel": "Zrušiť", 81 | "Set": "Nastaviť", 82 | 83 | "Selected:": "Zvolené:", 84 | " files": " súbory", 85 | "Pin": "Pripnúť", 86 | "UnPin": "Odopnúť", 87 | 88 | "Optional": "Voliteľné", 89 | "Help distribute all new files": "Pomôcť s distribúciou všetkých nových súborov", 90 | 91 | "Optional file": "Voliteľné súbory", 92 | "Size": "Veľkosť", 93 | "Peers": "Peer-ovia", 94 | "Uploaded": "Nahratých", 95 | "Downloaded": "Stiahnutých", 96 | "Finished": "Dokončené", 97 | "Pinned": "Pripnuté", 98 | 99 | "More files...": "Viac súborov...", 100 | 101 | " minutes ago": " minút dozadu", 102 | " hours ago": " hodín dozadu", 103 | " days ago": " dní dozadu", 104 | "on ": "", 105 | "Just now": "Teraz", 106 | 107 | "All": "Všetko", 108 | "comment": "Komentár", 109 | "topic": "Téma", 110 | "post": "Príspevok", 111 | "Owned sites:": "Vlastnené Stránky", 112 | "Merged:": "Zlúčené:", 113 | "Hello newcomer!": "Vitaj nováčik!", 114 | "You have not downloaded any optional files yet": "Zatiaľ si nestiahol žiadne voliteľné súbory", 115 | "Language: ": "Jazyk: ", 116 | "Create new, empty site": "Vytvoriť novú, prázdnu stránu", 117 | "Manage blocked users and sites": "Spravovať blokovaných užívateľov a stránky", 118 | "Show data directory": "Ukázať zložku s dátami", 119 | 120 | "Stats": "Štatistiky", 121 | "Monday": "Pondelok", 122 | "Tuesday": "Utorok", 123 | "Wednesday": "Streda", 124 | "Thursday": "Štvrtok", 125 | "Friday": "Piatok", 126 | "Saturday": "Sobota", 127 | "Sunday": "Nedeľa", 128 | 129 | "1w": "1t", 130 | "Incoming": "Príchodzie", 131 | "Connections": "Pripojenia", 132 | "Total size": "Ceľková Veľkosť", 133 | "of": "z", 134 | "Top country": "Top Krajina", 135 | "Transferred data (last 7 days)": "Prenesené dáta (posledných 7 dní)", 136 | "Site size": "Veľkosť Stránky", 137 | "Owned sites": "Vlastnené Stránky", 138 | "peers": "peer-ov", 139 | "Ratio": "Pomer", 140 | "Download": "Sťahovanie", 141 | "Upload": "Nahrávanie", 142 | "Sent": "Zaslaných", 143 | "Received": "Prijatých", 144 | "requests": "žiadostí", 145 | "Trackers: ": "Trackery: ", 146 | "Filter: Site name": "Filter: Meno stránky", 147 | "Discover more sites": "Objav ďalšie stránky", 148 | 149 | "United States": "USA", 150 | "China": "Čína", 151 | "France": "Francúzsko", 152 | "Portugal": "Portugalsko", 153 | "Spain": "Španielsko", 154 | "Russia": "Rusko", 155 | "United Kingdom": "Veľká Británia", 156 | "Germany": "Nemecko", 157 | "Canada": "Kanada", 158 | "Australia": "Austrália", 159 | "Netherlands": "Holandsko", 160 | "Japan": "Japonsko", 161 | "Brazil": "Brazília", 162 | "Sweden": "Švédsko", 163 | "Other": "Ostatné", 164 | 165 | "on": "", 166 | "Oct": "Okt", 167 | "May": "Máj", 168 | "Jun": "Jún", 169 | "Jul": "Júl" 170 | } 171 | -------------------------------------------------------------------------------- /languages/sl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Odprto", 3 | "Closed": "Zaprto", 4 | "_(Disabled)": "Onemogočeno", 5 | "_(Error)": "Napaka", 6 | "Available": "Na voljo", 7 | "Always": "Vedno", 8 | "Status: ": "Status: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Super! Vaša vrata \" + Page.server_info.fileserver_port + \" so odprta.", 10 | "Re-check opened port": "Ponovno preveri vrata", 11 | "How to make Tor connection work?": "Kako vzpostaviti Tor povezavo?", 12 | "How to use ZeroNet in Tor Browser?": "Kako uporabiti ZeroNet v Tor brskalniku?", 13 | "Disable always Tor mode": "Onemogoči Tor za vsako povezavo", 14 | "Enable Tor for every connection (slower)": "Uporabi Tor za vsako povezavo (počasneje)", 15 | "Help to keep this project alive": "Pomagajte ohraniti ta projekt aktiven", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Posodabljanje za zadnjo različico ...
Prosimo ponovno zaženite ZeroNet, če ne postane aktiven v nekaj minutah.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer ni popolnoma podpri brskalnik. Prosimo uporabite Chrome ali Firefox", 18 | 19 | "Welcome to ZeroNet": "Dobrodošli v ZeroNet", 20 | "Let's build a decentralized Internet together!": "Zgradimo decentraliziran internet skupaj!", 21 | "This site currently served by ": "Ta stran trenutno gostuje na ", 22 | " peers, without any central server.": " povezavah, brez centralnega strežnika.", 23 | "Some sites we created:": "Nekaj strani, ki smo jih naredili:", 24 | "Simple messaging board": "Preprosta stran s sporočili", 25 | "Reddit-like, decentralized forum": "Decentraliziran forum", 26 | "Activate \\u2501": "Omogoči \\u2501", 27 | "Microblogging platform": "Microblogging platforma", 28 | "End-to-end encrypted mailing": "Šifrirana decentralizirana e-pošta", 29 | "P2P social network": "P2P družabno omrežje", 30 | 31 | "Update all sites": "Posodobi vse strani", 32 | "Order sites by peers": "Razvrsti strani glede na povezave", 33 | "Order sites by update time": "Razvrsti strani glede na čas posodabljanja", 34 | "Order sites by add time": "Razvrsti strani glede na čas dodajanja", 35 | "Order sites by size": "Razvrsti strani glede na velikost", 36 | "Version ": "Različica: ", 37 | "New version avalible!": "Na voljo je nova različica!", 38 | "Up to date!": "Posodobljeno!", 39 | "Shut down ZeroNet": "Izklopi ZeroNet", 40 | 41 | "_(Sites)": "Strani", 42 | "_(Files)": "Datoteke", 43 | "Favorited sites:": "Priljubljene strani:", 44 | "Connected sites:": "Povezane strani:", 45 | "More sites:": "Več strani:", 46 | " file update failed": " datotek ni bilo posodobljeni", 47 | "No peers": "Ni povezav", 48 | "Update failed": "Posodobitev ni uspela", 49 | "Updating...": "Posodabljanje ...", 50 | "Updated!": "Posodobljeno!", 51 | "Updating: ": "Posodabljanje: ", 52 | " left": " preostalo", 53 | "Are you sure?": "Ali ste prepričani?", 54 | "Activate \\u00BB": "Aktiviraj \\u00BB", 55 | "Unfavorite": "Odstrani iz priljubljenih", 56 | "Favorite": "Dodaj v priljubljene", 57 | "Update": "Posodobi", 58 | "Clone": "Kloniraj", 59 | "Resume": "Nadaljuj", 60 | "Pause": "Čakaj", 61 | "Check files": "Preveri datoteke", 62 | "Delete": "Izbriši", 63 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Žal vašega spletnega mesta ne morete izbrisati.
Mapo odstranite ročno..", 64 | 65 | "Search in connected sites": "Iskanje po povezanih straneh", 66 | " results from ": " rezultatov iz ", 67 | " sites in ": " stravi v ", 68 | "s": "s", 69 | "Comment on": "Komentar na:", 70 | "You got mentioned in": "Omenili ste v", 71 | "You got mentioned": "Omenili ste", 72 | 73 | "Used: ": "Uporabljeno: ", 74 | "Edit optional files limit": "Uredi omejitev neobveznih datotek", 75 | "Optional files limit:": "Omejitev neobveznih datotek:", 76 | "Cancel": "Prekliči", 77 | "Set": "Nastavi", 78 | 79 | "Selected:": "Izbrano:", 80 | " files": " datotek", 81 | "Pin": "Pripni", 82 | "UnPin": "Odpni", 83 | 84 | "Optional": "Neobvezno", 85 | "Help distribute all new files": "Pomagajte distribuirati vse nove datoteke", 86 | 87 | "Optional file": "Neobvezne datoteke", 88 | "Size": "Velikost", 89 | "Peers": "Povezave", 90 | "Uploaded": "Naloženo", 91 | "Downloaded": "Preneseno", 92 | "Finished": "Končano", 93 | "Pinned": "Pripeto", 94 | 95 | "More files...": "Več datotek ...", 96 | 97 | " minutes ago": " minut nazaj", 98 | " hours ago": " ur nazaj", 99 | " days ago": " dni nazaj", 100 | "on ": "na ", 101 | "Just now": "Ravnokar" 102 | } 103 | -------------------------------------------------------------------------------- /languages/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Açık", 3 | "Closed": "Kapalı", 4 | "_(Disabled)": "Etkin değil", 5 | "_(Error)": "Hata", 6 | "Available": "Erişilebilir", 7 | "Always": "Her zaman", 8 | "Status: ": "Durum: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Güzel! \" + Page.server_info.fileserver_port + \" numaralı port açık.", 10 | "Re-check opened port": "Açık portları yeniden kontrol et", 11 | "How to make Tor connection work?": "Tor bağlantısı nasıl yapılır?", 12 | "How to use ZeroNet in Tor Browser?": "ZeroNet Tor Tarayıcısında nasıl kullanılır?", 13 | "Disable always Tor mode": "Tor modunu her zaman devre dışı bırak", 14 | "Enable Tor for every connection (slower)": "Tor'u her bağlantı için etkinleştir (daha yavaş)", 15 | "Help to keep this project alive": "Projeye destek ver", 16 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Son süreme güncelleniyor...
Bir kaç dakika içinde geri gelmez ise ZeroNet'i kendiniz tekrar başlatın.", 17 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer ZeroNet tarafından tam olarak desteklenmiyor, Chrome ya da Firefox kullanmayı tercih edebilirsiniz", 18 | 19 | "Welcome to ZeroNet": "ZeroNet'e Hoş Geldiniz", 20 | "Let's build a decentralized Internet together!": "Hadi hep beraber dağıtık bir Internet inşa edelim!", 21 | "This site currently served by ": "Bu site hali hazırda ", 22 | " peers, without any central server.": " eş tarafından herhangi bir merkezi sunucu olmadan yayımlanıyor.", 23 | "Some sites we created:": "Yaptığımız bazı siteler:", 24 | "Simple messaging board": "Basit bir mesaj tahtası", 25 | "Reddit-like, decentralized forum": "Reddit benzeri dağıtık forum", 26 | "Activate \\u2501": "Etkinleştir \\u2501", 27 | "Microblogging platform": "Microblogging platformu", 28 | "End-to-end encrypted mailing": "Uçtan uca şifreli mesajlaşma", 29 | "P2P social network": "P2P sosyal ağ", 30 | 31 | "Update all sites": "Tüm siteleri güncelle", 32 | "Order sites by peers": "Eş sayısına göre sırala", 33 | "Order sites by update time": "Güncelleme zamanına göre sırala", 34 | "Order sites by add time": "Eklenme zamanına göre sırala", 35 | "Order sites by size": "Boyuta göre sırala", 36 | "Create new, empty site": "Yeni site oluştur", 37 | "Version ": "Sürüm ", 38 | "New version avalible!": "Yeni sürüm var!", 39 | "Up to date!": "Güncel!", 40 | "Shut down ZeroNet": "ZeroNet'i durdur", 41 | 42 | "_(Sites)": "Siteler", 43 | "_(Files)": "Dosyalar", 44 | "Favorited sites:": "Beğenilen siteler:", 45 | "Connected sites:": "Başlatılan siteler:", 46 | "More sites:": "Daha fazlası:", 47 | " file update failed": " güncelleme başarısız oldu", 48 | "No peers": "Eş yok", 49 | "Update failed": "Güncelleme başarısız", 50 | "Updating...": "Güncelleniyor...", 51 | "Updated!": "Güncel!", 52 | "Updating: ": "Güncelleniyor: ", 53 | " left": " tane kaldı", 54 | "Are you sure?": "Emin misin?", 55 | "Activate \\u00BB": "Etkinleştir \\u00BB", 56 | "Unfavorite": "Beğenilenlerden çıkart", 57 | "Favorite": "Beğen", 58 | "Update": "Güncelle", 59 | "Clone": "Klonla", 60 | "Resume": "Sürdür", 61 | "Pause": "Duraklat", 62 | "Check files": "Dosyaları kontrol et", 63 | "Delete": "Sil", 64 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Maalesef, kendi sitenizi silemezsiniz
Lütfen dizini kendiniz silin", 65 | 66 | "Search in connected sites": "Bağlı sitelerde ara", 67 | " results from ": " sonuç ", 68 | " sites in ": " (", 69 | "s": ") içinde bulundu", 70 | "Comment on": "Yorum yap", 71 | "You got mentioned in": "Adınızın şurada anıldı", 72 | "You got mentioned": "Adınız anıldı", 73 | 74 | "Used: ": "Kulanılan: ", 75 | "Edit optional files limit": "Opsiyonel dosya sınırını düzenle", 76 | "Optional files limit:": "Opsiyonel dosya sınırı:", 77 | "Cancel": "İptal", 78 | "Set": "Ayarla", 79 | 80 | "Selected:": "Seçilen:", 81 | " files": " dosya", 82 | "Pin": "Sabitle", 83 | "UnPin": "Sabitlemeyi kaldır", 84 | 85 | "Optional": "Opsiyonel", 86 | "Help distribute all new files": "Tüm yeni dosyaların dağıtılmasına yardım et", 87 | 88 | "Optional file": "Opsiyonel dosya", 89 | "Size": "Boyut", 90 | "Peers": "Eş", 91 | "Uploaded": "Yüklendi", 92 | "Downloaded": "İndirildi", 93 | "Finished": "Tamamlandı", 94 | "Pinned": "Sabitlendi", 95 | 96 | "More files...": "Daha fazla dosya...", 97 | 98 | " minutes ago": " dakika önce", 99 | " hours ago": " saat önce", 100 | " days ago": " gün önce", 101 | "on ": "", 102 | "Just now": "Şu anda" 103 | } 104 | -------------------------------------------------------------------------------- /languages/uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "Opened": "Відчинений", 3 | "Closed": "Зачинений", 4 | "_(Disabled)": "Вимкнено", 5 | "_(Error)": "Помилка", 6 | "Available": "Доступно", 7 | "Always": "Завжди", 8 | "Status: ": "Стан: ", 9 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "Відмінно. Ваш порт\" + Page.server_info.fileserver_port + \" відкритий", 10 | "Your port \" + Page.server_info.fileserver_port + \" is closed. You are still fine, but for faster experience try open it.": "Порт \" + Page.server_info.fileserver_port + \" зачинений. Та для кращого ефекту можете його відкрити.", 11 | "Re-check opened port": "Перевірити статус порту", 12 | "Port: ": "Порт: ", 13 | "Checking": "Перевірка", 14 | "How to make Tor connection work?": "Як підключитися через Tor?", 15 | "How to use ZeroNet in Tor Browser?": "Як використовувати ZeroNet в Tor Browser?", 16 | "Disable always Tor mode": "Вимкнути режим постійного використання Tor", 17 | "Enable Tor for every connection (slower)": "Увімкнути режим роботи всіх з'єднань через Tor (повільно)", 18 | "Help to keep this project alive": "Підтримайте проект ZeroNet", 19 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "Оновлення на останню версію...
Якщо через кілька хвилин ZeroNet буде недоступний - перезапустіть додаток.", 20 | "Internet Explorer is not fully browser supported by ZeroNet, please consider switching to Chrome або Firefox": "ZeroNet в Internet Explorer не повністю підтримується, спробуйте Chrome або Firefox", 21 | 22 | "Welcome to ZeroNet": "Ласкаво просимо в ZeroNet", 23 | "Let's build a decentralized Internet together!": "Створюємо розподілений Інтернет разом!", 24 | "This site currently served by ": "Цей сайт поширюється ", 25 | " peers, without any central server.": " пірами, без жодного центрального сервера.", 26 | "Some sites we created:": "Деякі сайти які ми створили:", 27 | "Simple messaging board": "Проста дошка повідомень", 28 | "Reddit-like, decentralized forum": "Децентралізований форум на кшталт Reddit", 29 | "Activate \\u2501": "Активувати \\u2501", 30 | "Microblogging platform": "Мікроблоґова платформа", 31 | "End-to-end encrypted mailing": "Поштова система з E2E шифруванням", 32 | "P2P social network": "P2P Соціальна мережа", 33 | 34 | "Update all sites": "Оновити всі сайти", 35 | "Order sites by peers": "Сортувати сайти за пірами", 36 | "Order sites by update time": "Сортувати сайти за часом оновлення", 37 | "Order sites by add time": "Сортувати сайти за додаванням", 38 | "Order sites by size": "Сортувати сайти за розміром", 39 | "Language: ": "Мова: ", 40 | "Version ": "Версія ", 41 | "Create new, empty site": "Створити новий порожній сайт", 42 | "Manage muted users": "Чорний список користувачів", 43 | "Show data directory": "Відкрити директорію даних", 44 | "New version avalible!": "Доступна нова версія!", 45 | "Up to date!": "Актуальна версія!", 46 | "Shut down ZeroNet": "Вимкнути ZeroNet", 47 | 48 | "Muted": "Заблоковано", 49 | "\u2039 Back to feed": "\u2039 Повернутись до стрічки", 50 | "Your mute list is empty! :)": "Ваш чорний список порожній! :)", 51 | "Muted user": "Заблокований користувач", 52 | "Why?": "Чому?", 53 | 54 | "_(Sites)": "Сайти", 55 | "_(Files)": "Сховище", 56 | "_(Stats)": "Статистика", 57 | "Filter: Site name": "Пошук за назвою", 58 | "Running out of size limit:": "Перевищуть ліміт:", 59 | "Favorited sites:": "Улюблені сайти:", 60 | "Connected sites:": "Я поширюю ці сайти:", 61 | "Owned sites:": "Мої сайти:", 62 | "Merged: ": "Сполучені: ", 63 | "More sites:": "Більше сайтів:", 64 | " file update failed": " помилка(и) оновлення контенту", 65 | "No peers": "Немає пірів", 66 | "Update failed": "Помилка при оновленні", 67 | "Updating...": "Оновлюю ...", 68 | "Updated!": "Оновлено!", 69 | "Updating: ": "Очікування: ", 70 | " left": " файл(ів)", 71 | "Are you sure?": "Ви впевнені?", 72 | " Any modifications you made on
": " Будь-які зміни зроблені вами в js/css
файлах сайту ", 73 | " site's js/css files will be lost.": "
будуть перезаписані.", 74 | "Upgrade": "Оновити", 75 | "Activate \\u00BB": "Активувати \\u00BB", 76 | "Unfavorite": "Видалити із улюблених", 77 | "Favorite": "Додати в улюблені", 78 | "Update": "Оновнити", 79 | "Clone": "Клонувати", 80 | "Upgrade code": "Оновити код з джерела", 81 | "Resume": "Продовжити", 82 | "Pause": "Пауза", 83 | "Check files": "Перевірити файли", 84 | "Delete": "Видалити", 85 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "Ви не можете видалити свій власний сайт таким чином.
будь ласка видаліть папку з сайтом власноруч.", 86 | 87 | "Search in connected sites": "Пошук на поширюваних сайтах", 88 | " results ": " результатів ", 89 | "from ": " із ", 90 | " sites in ": " сайтів за ", 91 | "s": " с", 92 | "Comment on": "Прокоментував", 93 | "You got mentioned in": "Вас згадали у", 94 | "You got mentioned": "Вас згадали", 95 | 96 | "Used: ": "Використано: ", 97 | "Edit optional files limit": "Змінити ліміт дискового простору", 98 | "Optional files limit:": "Ліміт:", 99 | "Cancel": "Скасувати", 100 | "Set": "Змінити", 101 | 102 | "Selected:": "Вибрано:", 103 | " files": " файлів", 104 | "Pin": "Прикріпити", 105 | "UnPin": "Відкріпити", 106 | 107 | "Optional": "Додатково", 108 | "Help distribute all new files": "Допомогти сайту поширювати весь новий контент", 109 | 110 | "Optional file": "Додатковий файл", 111 | "Size": "Розмір", 112 | "Peers": "Піри", 113 | "Uploaded": "Віддано", 114 | "Downloaded": "Завантажено", 115 | "Finished": "Завершено", 116 | "Pinned": "Закріплено", 117 | 118 | "More files...": "Більше файлів...", 119 | 120 | " minutes ago": " хв. тому", 121 | " hours ago": " год. тому", 122 | " days ago": " дн. тому", 123 | "on ": " ", 124 | "Just now": "Щойно", 125 | 126 | "Monday": "Понеділок", 127 | "Tuesday": "Вівторок", 128 | "Wednesday": "Середа", 129 | "Thursday": "Четвер", 130 | "Friday": "П'ятниця", 131 | "Saturday": "Субота", 132 | "Sunday": "Неділя", 133 | 134 | "1w": "Тижні", 135 | "1d": "Дні", 136 | 137 | "Top country": "Топ країн", 138 | "peers": " пірів", 139 | "Other": "Інші", 140 | 141 | "Transferred data (last 7 days)": "Передано даних (за останіх 7 днів)", 142 | "Site size": "Розмір сайту", 143 | 144 | "Upload": "Відвантажено", 145 | "Download": "Завантажено", 146 | "Ratio": "Співвідношення", 147 | 148 | "Sent": "Надіслано", 149 | "Received": "Отримано" 150 | } 151 | -------------------------------------------------------------------------------- /languages/zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "Port: ": "埠: ", 3 | "Checking": "檢查中", 4 | "Opened": "開啟", 5 | "Closed": "關閉", 6 | "Always": "總是", 7 | "Available": "可用", 8 | "_(Disabled)": "禁用", 9 | "_(Error)": "錯誤", 10 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "不錯!你的 \" + Page.server_info.fileserver_port + \" 埠已經開啟。", 11 | "Good, your port is always closed when using ZeroNet in Tor always mode.": "好,當你使用 Tor Always 模式的 ZeroNet 時,你的埠總是關閉的。", 12 | "Re-check opened port": "重新檢查埠開啟狀態", 13 | "Status: ": "狀態:", 14 | "How to make Tor connection work?": "如何讓 Tor 連線工作?", 15 | "How to use ZeroNet in Tor Browser?": "如何在 Tor Browser 中使用 ZeroNet ?", 16 | "Disable always Tor mode": "禁用 Tor Always 模式", 17 | "Enable Tor for every connection (slower)": "為每個連線啟用 Tor (更慢)", 18 | "Tor always mode enabled, please restart your ZeroNet to make it work.
For your privacy switch to Tor browser and start a new profile by renaming the data directory.": "Tor Always 模式已啟用,請重新打開你的 ZeroNet 來使它工作。
為了你的隱私,請切換到 Tor Browser 並通過重新命名 data 目錄來新建一份用戶資料。", 19 | "Your port \" + Page.server_info.fileserver_port + \" is closed, but your Tor gateway is running well.": "你的埠 \" + Page.server_info.fileserver_port + \" 處於關閉狀態,但是你的 Tor 閘道器運行正常。", 20 | "Your port \" + Page.server_info.fileserver_port + \" is closed. You are still fine, but for faster experience try open it.": "你的埠 \" + Page.server_info.fileserver_port + \" 處於關閉狀態。你依然可以使用,但是為了更快的體驗,請嘗試打開它。", 21 | "Show your masterseed": "顯示你的種子金鑰", 22 | "Logout": "登出", 23 | "Enable Tor for every connection (slower)": "為每個連線啟用 Tor (更慢)", 24 | "Help to keep this project alive": "幫助保持這個項目存活", 25 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "正在更新到最新版本...
如果幾分鐘以後沒有運行,請手動重啟。", 26 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer 不被 ZeroNet 完整支持,請考慮切換到 Chrome 或 Firefox", 27 | "Unsupported browser": "不支援的瀏覽器", 28 | "New ZeroNet version: ": "新 ZeroNet 版本: ", 29 | "User: ": "使用者: ", 30 | 31 | "Welcome to ZeroNet": "歡迎使用 ZeroNet", 32 | "Let's build a decentralized Internet together!": "讓我們共同構建一個分散式的互聯網!", 33 | "This site currently served by ": "這個網站目前由 ", 34 | " peers, without any central server.": " 個節點驅動,沒有任何中心化的伺服器。", 35 | "Some sites we created:": "我們創建的一些網站:", 36 | "Simple messaging board": "简单的佈告欄", 37 | "Reddit-like, decentralized forum": "類似於 Reddit 的,分散式的論壇", 38 | "Activate \\u2501": "啟用 \\u2501", 39 | "Microblogging platform": "輕量化網誌平台", 40 | "End-to-end encrypted mailing": "端對端加密郵件", 41 | "P2P social network": "P2P 社交網路", 42 | 43 | "You need to update your ZeroNet client to use this feature": "你需要更新你的 ZeroNet 用戶端來使用這個特性", 44 | "Update all sites": "更新所有網站", 45 | "Order sites by peers": "根據網站節點數排序", 46 | "Order sites by update time": "根據網站更新時間排序", 47 | "Order sites by add time": "根據網站添加時間排序", 48 | "Order sites by size": "根據網站大小排序", 49 | "Create new, empty site": "新建空網站", 50 | "Manage muted users": "管理已屏蔽使用者", 51 | "Version ": "版本 ", 52 | "New version avalible!": "有新版本可用!", 53 | "Up to date!": "已是最新!", 54 | "Shut down ZeroNet": "關閉 ZeroNet", 55 | "You need ZeroNet 0.5.2 to use this feature.": "你需要 ZeroNet 0.5.2 來使用這個特性。", 56 | "Update to latest development version?": "更新到最新的開發版?", 57 | 58 | "Muted": "已屏蔽", 59 | "\\u2039 Back to feed": "\\u2039 返回數據源", 60 | "Your mute list is empty! :)": "你的屏蔽列表為空!:)", 61 | "Muted user": "已屏蔽的使用者", 62 | "Why?": "為何?", 63 | 64 | "_(Sites)": "網站", 65 | "_(Files)": "檔案", 66 | "Running out of size limit:": "超出網站大小限制:", 67 | "Favorited sites:": "收藏的網站", 68 | "Connected sites:": "已連線的網站", 69 | "Merged: ": "聚合在:", 70 | "More sites:": "更多網站:", 71 | " file update failed": " 個檔更新失敗", 72 | "No peers": "沒有節點", 73 | "Update failed": "更新失敗", 74 | "Updating...": "更新中...", 75 | "Updated!": "已更新!", 76 | "Updating: ": "更新中:", 77 | " left": " 剩下", 78 | "Are you sure?": "你确定?", 79 | "Activate \\u00BB": "啟用 \\u00BB", 80 | "Unfavorite": "取消收藏", 81 | "Favorite": "收藏", 82 | "Update": "更新", 83 | "Clone": "克隆", 84 | "Resume": "恢復", 85 | "Pause": "暫停", 86 | "Check files": "檢查檔", 87 | "Delete": "刪除", 88 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "抱歉,你不能刪除你自己擁有的網站。
請手動刪除網站目錄。", 89 | 90 | "Search in connected sites": "在已連線的網站中搜尋", 91 | " results from ": " 個結果 來自 ", 92 | "No results for ": "沒有結果對應 ", 93 | " sites in ": " 個網站 用時 ", 94 | "s": "秒", 95 | "Comment on": "評論於", 96 | "You got mentioned in": "提到了你", 97 | "You got mentioned": "提到了你", 98 | 99 | "Used: ": "已使用:", 100 | "Edit optional files limit": "編輯可選檔限制", 101 | "Optional files limit:": "可選檔限制:", 102 | "Cancel": "取消", 103 | "Set": "設定", 104 | 105 | "Space current used by optional files": "目前被可選檔使用的空間", 106 | "Space allowed to used by optional files": "允許被可選檔使用的空間", 107 | "Total free space on your storage": "你的記憶裝置的總剩餘空間", 108 | "Don't delete these files automatically": "不要自動刪除這些檔", 109 | "Hello newcomer!": "你好新人!", 110 | "You have not downloaded any optional files yet": "你還沒有下載任何可選檔", 111 | 112 | "Selected:": "已選中:", 113 | " files": " 個檔", 114 | "Pin": "固定", 115 | "UnPin": "解除固定", 116 | 117 | "Site size limit: ": "網站大小限制:", 118 | "Optional files on site: ": "這個網站的可選檔", 119 | "Optional": "可選的", 120 | "Help distribute all new files": "幫助分發所有新檔", 121 | "Upload/Download ratio": "上載/下載率", 122 | 123 | "Optional file": "可選檔", 124 | "Size": "大小", 125 | "Peers": "節點數", 126 | "Uploaded": "已上傳", 127 | "Downloaded": "已下載", 128 | "Finished": "已完成", 129 | "Pinned": "已固定", 130 | 131 | "More files...": "更多檔...", 132 | 133 | " minutes ago": " 分鐘前", 134 | " hours ago": " 小時前", 135 | " days ago": " 天前", 136 | "on ": "", 137 | "Just now": "剛才" 138 | } 139 | -------------------------------------------------------------------------------- /languages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Port: ": "端口: ", 3 | "Checking": "检查中", 4 | "Opened": "开放", 5 | "Closed": "关闭", 6 | "Unsupported": "不支持", 7 | "Checking...": "检查中...", 8 | "Offline mode": "离线模式", 9 | "Always": "总是", 10 | "Available": "可用", 11 | "_(Disabled)": "禁用", 12 | "_(Error)": "错误", 13 | "Offline mode, network communication disabled.": "离线模式,网络通讯已关闭。", 14 | "Nice! Your port \" + Page.server_info.fileserver_port + \" is opened.": "不错!您的 \" + Page.server_info.fileserver_port + \" 端口已经开放。", 15 | "Good, your port is always closed when using ZeroNet in Tor always mode.": "好,当您使用 Tor Always 模式的 ZeroNet 时,您的端口总是关闭的。", 16 | "Re-check opened port": "重新检查端口开放状态", 17 | "Status: ": "状态: ", 18 | "How to make Tor connection work?": "如何让 Tor 连接工作?", 19 | "How to use ZeroNet in Tor Browser?": "如何在 Tor Browser 中使用 ZeroNet ?", 20 | "Disable always Tor mode": "禁用 Tor Always 模式", 21 | "Enable Tor for every connection (slower)": "为每个连接启用Tor(超级慢)", 22 | "Tor always mode enabled, please restart your ZeroNet to make it work.
For your privacy switch to Tor browser and start a new profile by renaming the data directory.": "Tor Always 模式已启用,请重新打开您的 ZeroNet 来使它工作。
为了您的隐私,请切换到 Tor Browser 并通过重命名 data 目录来新建一份用户资料。", 23 | "Your port \" + Page.server_info.fileserver_port + \" is closed, but your Tor gateway is running well.": "您的端口 \" + Page.server_info.fileserver_port + \" 处于关闭状态,但是您的 Tor 网关运行正常。", 24 | "Your port \" + Page.server_info.fileserver_port + \" is closed. You are still fine, but for faster experience try open it.": "您的端口 \" + Page.server_info.fileserver_port + \" 处于关闭状态。您依然可以使用,但是为了更快的体验,请尝试打开它。", 25 | "Show your masterseed": "显示您的种子密钥", 26 | "Logout": "退出登录", 27 | "Tor always mode disabled, please restart your ZeroNet.": "Tor Always模式已禁用,请重新打开您的ZeroNet。", 28 | "Help to keep this project alive": "帮助这个项目", 29 | "Updating to latest version...
Please restart ZeroNet manually if it does not come back in the next few minutes.": "正在更新到最新版本...
如果几分钟以后没有运行,请手动重启。", 30 | "Internet Explorer is not fully supported browser by ZeroNet, please consider switching to Chrome or Firefox": "Internet Explorer 不被 ZeroNet 完整支持,请考虑切换到 Chrome 或 Firefox", 31 | "Unsupported browser": "不支持的浏览器", 32 | "New ZeroNet version: ": "新ZeroNet版本: ", 33 | "New important update: rev": "重要更新: 版本", 34 | "Update and restart ZeroNet": "更新并重启ZeroNet", 35 | "Details of the update": "本次更新细节", 36 | "User: ": "用户: ", 37 | 38 | "Welcome to ZeroNet": "欢迎加入 ZeroNet", 39 | "Let's build a decentralized Internet together!": "让我们共创一个分布式网络吧!", 40 | "This site currently served by ": "本站点目前可发现 ", 41 | " peers, without any central server.": " 个服务节点,没有任何集中式服务器。", 42 | "Some sites we created:": "我们创建的部分站点:", 43 | "Simple messaging board": "简单的消息版", 44 | "Reddit-like, decentralized forum": "类似于Reddit的分布式论坛", 45 | "Activate \\u2501": "激活 \\u2501", 46 | "Microblogging platform": "轻量化博客平台", 47 | "End-to-end encrypted mailing": "端对端加密邮件", 48 | "P2P social network": "P2P社交网络", 49 | "Discover more sites": "发现更多站点", 50 | 51 | "You need to update your ZeroNet client to use this feature": "您需要更新您的ZeroNet客户端来使用这个特性", 52 | "Update all sites": "更新所有站点", 53 | "Order sites by peers": "根据站点节点数排序", 54 | "Order sites by update time": "根据站点更新时间排序", 55 | "Order sites by add time": "根据站点添加时间排序", 56 | "Order sites by size": "根据站点大小排序", 57 | "You need ZeroNet 0.6.4 to change the interface's theme": "为使用主题特性, 您至少需要更新到ZeroNet 0.6.4", 58 | "Theme: ": "主题: ", 59 | "_(system)": "默认", 60 | "_(light)": "明亮", 61 | "_(dark)": "黑暗", 62 | "Language: ": "语言: ", 63 | "Create new, empty site": "新建空站点", 64 | "Manage muted users": "管理已屏蔽用户", 65 | "Manage blocked users and sites": "管理已屏蔽的用户和站点", 66 | "Configuration": "配置", 67 | "Plugins": "插件", 68 | "Show data directory": "打开数据文件夹", 69 | "Backup users.json file to keep your identity safe.": "备份 users.json 文件以确保妥善保管您的身份信息。", 70 | "Version ": "版本 ", 71 | "New version avalible!": "有新版本可用!", 72 | "Up to date!": "已是最新!", 73 | "Shut down ZeroNet": "关闭 ZeroNet", 74 | "You need ZeroNet 0.5.2 to use this feature.": "为使用这个特性, 您至少需要更新到ZeroNet 0.5.2.", 75 | "Update to latest development version?": "更新到最新的开发版?", 76 | 77 | "Muted": "已屏蔽", 78 | "\\u2039 Back to feed": "\\u2039 返回数据源", 79 | "Your mute list is empty! :)": "您的屏蔽列表为空!:)", 80 | "Muted user": "已屏蔽的用户", 81 | "Why?": "为什么?", 82 | 83 | "_(Sites)": "站点", 84 | "_(Files)": "文件", 85 | "_(Stats)": "统计", 86 | "Filter: Site name": "筛选器: 站点名", 87 | "(found ": "(找到 ", 88 | " sites)": " 站点)", 89 | "Recently downloaded:": "最近下载的站点:", 90 | "Running out of size limit:": "超出站点大小限制:", 91 | "Favorited sites:": "收藏的站点:", 92 | "Owned sites:": "拥有的站点:", 93 | "Connecting sites:": "连接中的站点", 94 | "Connected sites:": "连接的站点:", 95 | "Show more": "展示更多", 96 | "Merged: ": "聚合在:", 97 | "More sites:": "更多站点:", 98 | " file update failed": " 个文件更新失败", 99 | "No peers": "没有节点", 100 | "Update failed": "更新失败", 101 | "Updating...": "更新中...", 102 | "Updated!": "已更新!", 103 | "Updating: ": "更新中:", 104 | " left": " 剩余", 105 | "Are you sure?": "您确定吗?", 106 | "Activate \\u00BB": "激活 \\u00BB", 107 | "Unfavorite": "取消收藏", 108 | "Favorite": "收藏", 109 | "Update": "更新", 110 | "Clone": "克隆", 111 | "Upgrade": "升级", 112 | "Upgrade code": "升级代码", 113 | "Resume": "恢复", 114 | "Pause": "暂停", 115 | "Save as .zip": "打包成ZIP文件", 116 | "Check files": "检查文件", 117 | "Delete": "删除", 118 | "Blacklist": "黑名单", 119 | "Blacklist ": "拉黑 ", 120 | "Blacklist this site": "拉黑此站点", 121 | "Reason": "原因", 122 | "Delete and Blacklist": "删除并拉黑", 123 | "Sorry, you can't delete your own site.
Please remove the directory manually.": "抱歉,您不能删除您自己拥有的站点。
请手动删除站点文件夹。", 124 | 125 | "Search in connected sites": "在连接的站点中搜索", 126 | "Tip: Search in specific site using ": "小提示:在指定站点中搜索请使用格式 ", 127 | "anything site:SiteName": "搜索内容 site:站点名", 128 | "Feed": "馈送", 129 | "Taken": "耗时", 130 | " results from ": " 个结果 来自 ", 131 | "No results for ": "没有结果对应 ", 132 | " sites in ": " 个站点 用时 ", 133 | "s": "秒", 134 | "Comment on": "评论于", 135 | "You got mentioned in": "提到了您", 136 | "You got mentioned": "提到了您", 137 | 138 | "Used: ": "已使用:", 139 | "Edit optional files limit": "编辑可选文件限制", 140 | "Optional files limit:": "可选文件限制:", 141 | "Cancel": "取消", 142 | "Set": "设置", 143 | 144 | "Space current used by optional files": "目前被可选文件使用的空间", 145 | "Space allowed to used by optional files": "允许被可选文件使用的空间", 146 | "Total free space on your storage": "您的存储设备的总剩余空间", 147 | "Don't delete these files automatically": "不要自动删除这些文件", 148 | "Hello newcomer!": "您好 新人!", 149 | "You have not downloaded any optional files yet": "您还没有下载任何可选文件", 150 | 151 | "Selected:": "已选中:", 152 | " files": " 个文件", 153 | "Pin": "固定", 154 | "UnPin": "解除固定", 155 | 156 | "Site size limit: ": "站点大小限制: ", 157 | "Optional files on site: ": "这个站点的可选文件", 158 | "Optional": "可选的", 159 | "Help distribute all new files": "帮助分发所有新文件", 160 | "Upload/Download ratio": "上传/下载 比", 161 | 162 | "Bigfiles": "大文件", 163 | "Filter: File name": "筛选器: 文件名", 164 | "Site": "站点", 165 | "Optional file": "可选文件", 166 | "Status": "状态", 167 | "Size": "大小", 168 | "Peers": "节点数", 169 | "Uploaded": "已上传", 170 | "Downloaded": "已下载", 171 | "Finished": "已完成", 172 | "Pinned": "已固定", 173 | 174 | "Sunday": "周天", 175 | "Monday": "周一", 176 | "Tuesday": "周二", 177 | "Wednesday": "周三", 178 | "Thursday": "周四", 179 | "Friday": "周五", 180 | "Saturday": "周六", 181 | "Upload": "上传", 182 | "Download": "下载", 183 | "Ratio": "分享率", 184 | "Sent": "已发送", 185 | "Received": "已接受", 186 | "requests": "请求", 187 | " requests": " 请求", 188 | "Transferred data (last 7 days)": "传输的数据(最近7天)", 189 | "Site size": "站点大小", 190 | "Connections": "连接情况", 191 | " peers": " 节点", 192 | "Onion: ": "洋葱点: ", 193 | " peers (": " 节点 (", 194 | " connections)": " 连接)", 195 | "Incoming: ": "传入: ", 196 | "Ping avg: ": "平均Ping: ", 197 | "ms (min: ": "ms (最小: ", 198 | "Total size": "总大小", 199 | " sites": " 站点", 200 | "Content sources: ": "数据文件: ", 201 | "Optional downloaded: ": "已下载可选文件: ", 202 | " (limit: ": " (限制: ", 203 | "Top country": "热门地区", 204 | 205 | "More files...": "更多文件...", 206 | 207 | " minutes ago": " 分钟前", 208 | " hours ago": " 小时前", 209 | " days ago": " 天前", 210 | "on ": "", 211 | "Just now": "刚才" 212 | } 213 | -------------------------------------------------------------------------------- /template-new/content.json-default: -------------------------------------------------------------------------------- 1 | { 2 | "address": "17uxAEdnfTm5AFezpbV1aSSt25UFmQXRD4", 3 | "background-color": "#FFF", 4 | "clone_root": "template-new", 5 | "cloned_from": "1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D", 6 | "description": "", 7 | "files": { 8 | }, 9 | "ignore": "", 10 | "inner_path": "content.json", 11 | "modified": 1481393762.773, 12 | "postmessage_nonce_security": true, 13 | "sign": [ 14 | ], 15 | "signers_sign": "", 16 | "signs": { 17 | }, 18 | "signs_required": 1, 19 | "title": " new site", 20 | "translate": [ 21 | "js/all.js" 22 | ], 23 | "zeronet_version": "0.5.1" 24 | } -------------------------------------------------------------------------------- /template-new/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | New ZeroNet site! 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /template-new/js/ZeroFrame.js: -------------------------------------------------------------------------------- 1 | /* 2 | Original code is Copyright (c) 2020 ZeroNet project. 3 | Original code was released under the GPLv2 license by ZeroNet project, December 2016. 4 | Original code was rereleased under the MIT license by ZeroNet project, March 2020. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | // Version 1.0.0 - Initial release 27 | // Version 1.1.0 (2017-08-02) - Added cmdp function that returns promise instead of using callback 28 | // Version 1.2.0 (2017-08-02) - Added Ajax monkey patch to emulate XMLHttpRequest over ZeroFrame API 29 | // Version 1.3.0 (2018-12-05) - Added monkey patch for fetch API 30 | // Version 1.3.1 (2019-09-02) - Fix memory leak while handling responses 31 | // Version 1.4.0 (2019-12-11) - Awaitable monkeyPatchAjax function 32 | 33 | const CMD_INNER_READY = 'innerReady' 34 | const CMD_RESPONSE = 'response' 35 | const CMD_WRAPPER_READY = 'wrapperReady' 36 | const CMD_PING = 'ping' 37 | const CMD_PONG = 'pong' 38 | const CMD_WRAPPER_OPENED_WEBSOCKET = 'wrapperOpenedWebsocket' 39 | const CMD_WRAPPER_CLOSE_WEBSOCKET = 'wrapperClosedWebsocket' 40 | 41 | class ZeroFrame { 42 | constructor(url) { 43 | this.url = url 44 | this.waiting_cb = {} 45 | this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1") 46 | this.connect() 47 | this.next_message_id = 1 48 | this.init() 49 | } 50 | 51 | init() { 52 | return this 53 | } 54 | 55 | connect() { 56 | this.target = window.parent 57 | window.addEventListener('message', e => this.onMessage(e), false) 58 | this.cmd(CMD_INNER_READY) 59 | } 60 | 61 | onMessage(e) { 62 | let message = e.data 63 | let cmd = message.cmd 64 | if (cmd === CMD_RESPONSE) { 65 | if (this.waiting_cb[message.to] !== undefined) { 66 | this.waiting_cb[message.to](message.result) 67 | delete this.waiting_cb[message.to] 68 | } 69 | else { 70 | this.log("Websocket callback not found:", message) 71 | } 72 | } else if (cmd === CMD_WRAPPER_READY) { 73 | this.cmd(CMD_INNER_READY) 74 | } else if (cmd === CMD_PING) { 75 | this.response(message.id, CMD_PONG) 76 | } else if (cmd === CMD_WRAPPER_OPENED_WEBSOCKET) { 77 | this.onOpenWebsocket() 78 | } else if (cmd === CMD_WRAPPER_CLOSE_WEBSOCKET) { 79 | this.onCloseWebsocket() 80 | } else { 81 | this.onRequest(cmd, message) 82 | } 83 | } 84 | 85 | onRequest(cmd, message) { 86 | this.log("Unknown request", message) 87 | } 88 | 89 | response(to, result) { 90 | this.send({ 91 | cmd: CMD_RESPONSE, 92 | to: to, 93 | result: result 94 | }) 95 | } 96 | 97 | cmd(cmd, params={}, cb=null) { 98 | this.send({ 99 | cmd: cmd, 100 | params: params 101 | }, cb) 102 | } 103 | 104 | cmdp(cmd, params={}) { 105 | return new Promise((resolve, reject) => { 106 | this.cmd(cmd, params, (res) => { 107 | if (res && res.error) { 108 | reject(res.error) 109 | } else { 110 | resolve(res) 111 | } 112 | }) 113 | }) 114 | } 115 | 116 | send(message, cb=null) { 117 | message.wrapper_nonce = this.wrapper_nonce 118 | message.id = this.next_message_id 119 | this.next_message_id++ 120 | this.target.postMessage(message, '*') 121 | if (cb) { 122 | this.waiting_cb[message.id] = cb 123 | } 124 | } 125 | 126 | log(...args) { 127 | console.log.apply(console, ['[ZeroFrame]'].concat(args)) 128 | } 129 | 130 | onOpenWebsocket() { 131 | this.log('Websocket open') 132 | } 133 | 134 | onCloseWebsocket() { 135 | this.log('Websocket close') 136 | } 137 | 138 | async monkeyPatchAjax() { 139 | var page = this 140 | XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open 141 | var newOpen = function (method, url, async) { 142 | url += "?ajax_key=" + page.ajax_key 143 | return this.realOpen(method, url, async) 144 | } 145 | XMLHttpRequest.prototype.open = newOpen 146 | 147 | window.realFetch = window.fetch 148 | var newFetch = function (url) { 149 | url += "?ajax_key=" + page.ajax_key 150 | return window.realFetch(url) 151 | } 152 | window.fetch = newFetch 153 | 154 | this.ajax_key = await page.cmdp("wrapperGetAjaxKey", []) 155 | } 156 | } 157 | --------------------------------------------------------------------------------