├── 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 | 
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 |
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 ("
")
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 |
--------------------------------------------------------------------------------