Archived
Use event interface for GUI (fixes #383)
This commit is contained in:
1 parent
9c99d65716
commit
e27d42935c
9 files changed
+361
-171
No files matched your search
+3
-3
@@ -28,7 +28,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["angular.min.js"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7cff73dbb692f8effe2bf0f4c97ba21c9972927e3aefa238bdd44ddff9354d3271d2bb19d79da14448624d912a01dad624fedf6f1700498000282a4e73ef664ed33a12b0582c168bc57e01309990d37cb32d92e58a93e074441e1f3ffa86fc33baca67e4fbbc5892288b0122e345322b795e3012304a095f5172fae6f5fb7767df7f78ffe6dd395924291d850780ee459a12818e9182325a5cd338241f1825f9029a258cb0bc2ce694ccf39812f8b9ccaf6991d198ccb6d019f9f9ecfd11e3db9422ae3499d38c6177112773a89d51b2c84b2029c9040dafce4e5fbe3e7f29ba0f0f0e2687bfb334c9389915f90df4fd94f0a2a463e82be34956d2eaf7262d19fe2f7f93c309b45ca6f92c4ac983a76411a50c80a26c59a651a17e23d0c1b0842f0c7831e7c3e9c1c1755410b6cde630ac6c494eaa16e13a8fcb9406c3ba6e382617c34dc4e651ba29e87cc5435e44194b234e8797a3a9405416e92c02f42764087c43fc587a1da549fc0a1033a8b818c474301ed00cff30f8b328e0cf86c31f763db8048aea1e4318f22259068b120a923c23c18315e79bb7457e9dc4b4189307350555d9887c3c20f03100c3982ea232e52cbc65c5e23f680465afa3b520f3bf8e4ecfdffd78f43ebfa21990bba3ed699e5f25b46a6bb4944d2d8242e0f6398f7832ff11a697bdcab1f34012891f60e522b97d4a8629f0e76838ae2b58b99015e1ef2ccf86a2fc0ed88cff9b2ce2459ea68074785e959ef22285e9d2f8c6e6f906c4410c4de71b7c4ff379845015eb70c680aaeb1fa01a46793cad4b9794bff9098a50de9a529ce8a8e0527a849855cc109d2285191574e0f47fbc9bb62a6186edf2f5f6ec0764f1d028cd60b90919ba742039cb70fc0679aa7e53e43c9fe7e9e90a780c8bb4265383a145818ac1c20d8a227b89753635b042686a935ed04dee182916171c981af9eade02d3137ae324afdc2c0b909cb36c91abe68da886302f815a790f071314a4c12864e57c4e19d3160f56b06a96abd9c3c2695db2807106589c8899877f9e090816a6345bf215943c7ca8a3c00f0200b880bb482ea74665b24084d5fa0f932ca6b76f16829611790e9db4b199cb08978f049e5a50b382465766f1dd81f9ed6e6488e2833c0b06b5c04b7138977c1ae8cbc5c5241861dd12a4222ae6ab60149aecc3c1ca919963ea1ecf5dbdb60d59a7347ed12c9b1a7a58ac87a0157ea0a9a62ca0304e0a554e02f83ed26b515d60252e10bd9ce7e57c85151f3631aa715975d726e36cee21a2a06bd8f89c74d8551511717e93a5a0069d84448cd32261573529e29f7a6640d2c57cd198c6c62c21e7ff22f493c57b398e244b78d012a207c1f0ff6594dfe4c59558e2c311aee9280d862b50ddc316b4adfd1a66552434ba704f329a86dd44202c5b951c99d80de952cb26d10ee6fe18c1266573d63fac82f2b2c87cec704f888feb1f67d1fc2a2ef20dc801135b26ec6157743bcba3225616cc9d6752ba06582bd9058c6385c0ce856ee952f2900c276c0b12b91ebaf4292c99a83d385340a72e119018810c6caf913c927b50e0a64e21af26486fd77c7f33fb1d76da1078c6027d371a85a0d85f46a0b11ae449dc468f33c6b71b0a36aebec35d24f125393981ed0f6c563056c0cc1dba943698baaf2998c16441f97c85b630855ea9addc9d7c165d7d87d49e0ca18066685a7f7877769aaf377946338e04f79d8516c7b561b498ee62a2f84d289acb8e9dc94ffc04c6ce80a42f3d089c16ac0f157ef2dc1a5755e543f195d9be37fb7dd35097ef9e30e77a708f5933487baf6bb402b21c6d33348bc110bd0946630382a35d1920cc516d3e8fc8843c3a3e3e36219358edf4d547b3b6a1bd3930619125c257f4b15cec7d424256117b739381ebb1a105df8aa9f34c51e54c3a968255c28bad070b762ae42fc9661bb4107e8ef82a5c47b7c1f198fc9d1c4a5e0a88b3ecfb2da7ec7dcec13f3d72f8061614328fc71e89a8f1e625dfd5f59b92f7e9db00f3767e072e3ca83712501f6b2da61cf71d8203f0ce23e5f871fa57ed4d65d762907e4fef75d0f696dcdddd4d0fdaee0dfac02533365f2c6e5b1aaefd07e176ee40d2104188ab0c6d23dd16307ab051c344090f85fc05bbf062663cdf6ca06727e60a0895a8a3073467a89737a769c4be026b12701eff04bec4e845156ec422b8c4a56aebc19b8a16d504da24714afd33224576e8333f1b34e87b08dbda83695324eba8d83a3139d8d79a43c4fe961673d82ea325fdd327127614df881dec952141a1d9047ecbe1766335a67033e7302a8000bdea9413742c651713178046c3b4cd57a1b717690e962f74e3562018f74127d4602d169e2e966d5f1dd521faeab692570dc2d788ed874b53e410d06510637988d6504a45b7c84260844bf95782925f0d7b5a93550be59ab49b75299a35ecdecc2d8e629c964af9976597bd8a7bf1cc5eb23b79e65fc138d617715c7c658e29c204d3b07b60439706faae63be3596ff8f0d4123024c8bbf766a53ff507ea13ba7018931291582a6c58e3d642a08e52eb9e8fb33d99326b043fce2ef7bc73cc3a61063c71677cce1e208d668a38a50b91e39070c29a77a8401e9adbacd1a561289756ad88f384485574583c130208f3c23ae77b18e812a6c17c7975e6150a9969dd2a0f65455db73231d0c7cfb683dcfd0bda7b10ed231465362c0ca9e315ea08ff2ad7babc3ece26bd7c0db539c494968350b5a13a6f1a52f530265428b46a34e16ede24f1fe6f4e60c8d137e4e3946219997339309181457944404e38b18fc84b6dbc60752dc5a6fde6c2a8fa94a7822606064944205a43b516d04e187772fb368968ab452e0aa7e01dbdb8643fd73b0bd5c98fef1e1ac9b0c00d01b620858f1a16fec1463d61ee78c45d7b07ba82c5cc5544bdae6b8a8c83fcfdfbc0e31759c2d93458b4a8d426c906fb84819ac44b6953d251f879881070578f41e6452c4fa379b3491099589c871deddb5bdd54dceacd80df405630382c6a21397e76a79adbe3810e09a88bcc43e212e77d6510460f4b29661e208d93a66a2b780b3b2803ff99acabcfd5ca636cd39abd39d7fa9848bfe514629734bf9d816ee11f9f4c91abefc74a304891d9b12ded24b8ab6367bf124c78aceaf10a664e84cc9d4280e71153132a3342354adb7bc2071c2c477cb0eed5aa77ffb9b7319d7ebf4992f2bd9d54898b9c74e1b56c4e7ee43102a8ebdc9397ad4b68cbd9caef2e3e426495382193fc2733ca652491008a14a01b5186d4f7bf881d1f7afce45ccc0100055e11f889da537336dee41bc0025b2059a6f48a513096669d3747be0e8c394788fd6d5e47fda81c4afb62d89f75210be4a18e84465fb1b36a70d75ce8b9081cae4c1708c8a3fda68caea56b3e46e4350d16b30059aa4728b8a46e91ba9a483ee4d668079c6812fc627e4c3abb78c74a439b1260680789ff0146d9bc1bbba68e087Line truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7dff73dbb692f8effe2b107df22a29912927e9ebbc4f1ca7d7bac93bbfa649268e7b37e3fadd502264b1a648955fec6812ffefb7bb00497ca52827cddd9b394d224bc462b1582c16bb8b05349db2e36cbdc9e3cb65c946c763f6f8e0d1b7ec1fe15536633f66f9250bd30820d2328f675599e5051b159cb372c9d9f19bd7efdf9dfc78f6fecdbb53b688133e0ef600dd0f49c2085dc1725ef0fc9a47013b2b38cb16502d2e589155f99cb3791671065f2fb36b9ea73c62b30d34c67e3979bf5f949b8423ae249ef3b4c0e6c292cda174c6d922ab80a438251a5e9d1cbf787dfa829a0ff6f6a60f7e2f92382dd92ccf6ea0eda7accc2b3e81b6d2324e2b5e7f5f275581ffc577f6600a352f936c1626ecfe53b608930280c2f4b24ac25c7e47a0bd61051f0ae0c5bc1c1eeeed5d87392b36e91cba955eb2a3ba46b0caa22ae1a36153369cb0f3e13a2ce661b2cef97c5906651ea64512967c78313e2444559ecc42407fc486c037c48f4fafc3248e5e01e2020ace07111f4c063cc5b702de1639bcad4b782bae07174051d362005d5ec497a345050fe22c65a3fbcbb25cbfcdb3eb38e2f984dd6f28a89f8dd9c73d062f0d3088f822ac92b2083e14f9e2df7908cf5e872b22f33ff78f4fdfbddc7f9f5df114c8dd52f738cbae625ed7d56a8aaa16410170fbb40ccb78fe1286b7789561e3234124be80958bf8c353364c803ffbc3495350540b5110fc5e64e9909edf029bf1bfcea232cf9204900e5f5cf3b43c2ef304864ae15931cfd6200ad4ad863ff43048c2a2a45ad09db44a12c1001cb32c0519c45e92e0b4cfb1c6c94ff0fc407699e4a79acf7951bc4c11be69380acbb06e0f5ff1828dee09bcea63859cfb7c1597a3e1d9c91b821a8e0f35a8862614f8b6e8764f6b4252f89c1d98ad2041c122cb5f84f3a522541c1960c25a541154506e9095a2864edcadf2fdf6b025c9c169a4e39c8849787a592ed93e7b74d1d66e586c560de248415cf0f27dbce259552a7db1188b831e5cf272544fcd876c3825fa8bef8b389df3a3213c124d8eb5aa811cd55133ba4639cff32c1fd13b9429bd9fb0bf1e1cc807b78a9848d0ad42d25346168b4e2151049708f917655c2721490ccc387a346c1be86adc6ab843979cd64fbbf589aa83e17392cd4384aab989a30e1aeefa2728163aa37e0a1d7af3b33695f1292e1a615e8a95480ea0aaafe6d96a9d70a2e3887dbc3dd4cb70b1f03d3f49b1475a836d79caa96f855d997845cbd685f61c56479ed8e0ab0d4ddce1507b9a02b003c73acfca6c9e25c74b50fd603b68222b6172becef212d817da8d89b2b7c0de98df786b3b3a05c64ffa023b66935aad2f7358a14ed24526ebb985f0e1608a0bd660dcc85b2b205850a8f349ac1be9652be9a084d9081fc72415f0e71941145223c293870fcd298900004e70e7f1853eed516fb4764610a711fff06641b48cd9f3237b31a06e35b28bcbb4003eb4a066390faf0c656fa895dbb126a6f7b3743468268318df53c1a7813a955c4c42bd5fd784910a7358abc681ce3eb1cc61cf0ca5d5d99fdbc686d08493f3e88756fc1be861be1a82f5f1134f14a3041e46712e9f83f28ef3b15a8aaa040b71aaa9cfcbac9a2fb1e06c1da1b9288a6e4d324ee61e2272be0203db49875d54131165376902e696931050db3c8f8bab861473f85a23441d3052bb6055e71aeb65b538858549e1f7fdd1f0ffa5bcbcc9f22b9a6dc331aa8d30190d9760190e0dc856f575c315cbaac4aeb9a1ccd1adbb22d7caed7d2123ada5c592305f9f3ecec2f95594676b606e41f62e3476c537b32ccc23e97edc6e15462217cd652e95620f8a71e24442414249801ff599a2aa6c6176a15ebcf0d817265880bde1d26a0bcaac57175ec1044e4e50070989ffec7e80970083b27c07148d1adac68a35088ea6688a296b24925e15a46d43f068c53a447ee80db880cb30e7c29b4564ec262e978129d3b482a8dc788d381c4634e23e5e58f212f119b8ba731ebd131d386e88ab6b10c6939f264ced56c3e22eb17e0753bfe45f83cb46b9d20982c29ed8f43b69c6cefe1417d2e6e8453188238761b5ad95f3ba03e0175c6c69f378870649056c69ce33797ce09a526fda4967ebe2293b985825609afb8a4ed21f37252fde6765983801de54e516881fa2084611d0371486f04487bbed35cd8fc9b4249fdec15893a79a3d1abc59139b82b3773f8061b02ed10074982930af4f16ac2ac24bce84c187c6f1322cd80cac38967298b51ca6778d2385b91ef1396afb68626282597f13820b5a662c2cae28f80476428edf57e11567219b2fb378ce03f663453a22cad26149754c545065565d228a158baa1c89c2f52f0e13f4af2a700c8a8cb40c2f112dc54d48c55888808812dc31115ae36c11e745c9aee3222e03f61f4b9ecac099c000ca6a8d11399b208cf135b8006c9591760b530cb6e56c096f050b2fb30952257b6fe2f8a382850fedae3dad04750691f52b52854b4136af56e8900baa70d627e19c8fa6a3ef9fc2bf7f7e0a1e1cfe563c18b795e0db6f47f0363affe7e1c58371f0e0fef8d33fe17d3a6183fb8f0663db98bdd7567699ae060940d4a0ad70340027117d2e504d3760653e6483c355f8611f84888a9e1c3c78fced8327df1d1c18662de31825744c5520e8a1c282672af67d26703d787400deab5d195f68375477b7161a027b59e1cd3c54f5396966d56873fad5b4fa7f8fb0e4def31423bc67ef4e50e56729f05be071b93d6624831a516d0a5a40a521e158e2dcb49f6ec0525d8db6535e10e07057ca84c34a5a103f1fba8004ea6d94e32c696d8f97d0baea7a9afd320c810933c707450e4b402b1ea9d49a1dc941dbe4a92209fa3a6650748e281f3aed3f3f24f65cda311d1123ffd0b498bf47a42459d43528fce68bc99ad66d2b5e42ddb1faec186cb38a3286e6ebd63dd5dd7834f1574a0ff79c58509aca0c95edc184cd296c7ae0a6a28925cc576bdc52b943d7b1a187475eca01f185bb6d7c217150f9d117e051f05f255a2d18a5028aa688daa103bb02cfcd27bf348fba15ce7163c2a11f56f4d03c8ad1d75bfde078c1ea013d6d1712dd5c293120364298fd268438069ee022a3436a51717c291147a8af738ba425a6bd37df24a20944520a66d69b9bf42dac533c2f37506dec93a27a736edb92453dcb371e2cb43100e67240a63190ff4b083e19acdc2398047f630f042f0942b58381430efbdb8442e695916351d55a16a67767d39a85ed6f5b03f3367ecbe661395f82c9ec63adc5148f1eb0bbe000f41910f872c683775aac2924d267ca88b0f2ae8b75138cde8928e1a4f49bc700b8d3148609725c87ddefc5c58bd5badcbc99fd0eecd3bd9db13143cd90bdde210ba271965ec560898062cad951278474f182dfb318dcb4091b7a08a823f33a2e8a7138c921f8a0002f4c842f40c98639f720afa3eff8f797706df87f68948229Line truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["app.js"] = bs
|
||||
@@ -73,7 +73,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["favicon.png"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7d697bdc3692f0f7fc0a9893439a57ec966cc79357917a578733510edb8f244f369b273b0f9a44772322091a0025f7289adfbe5500efa3c9d665cf4e3ed86a92205055a81b4071efc9f1eba3f39fdfbc240b1d06934ff69eb8ee27e3313912f152f2f942938da34df2747be739f98e5e88293914724e68e4438b484b3e4db4908a6c28c6885e3072f4fad5f9e9c9e1dbf3d7a76764c603b639c2ee0e828098ee14914c3179c9fc1179ab181133788d2ba244223d463ce13302977371c964c47c325dc260e4c7937357e965c0b0af807b2c52381cd5c483a7534666220190786460f8e1e4e8e5abb39766f8d127ae0b58217224a0d17cdf619143a2b94be378df51cbc883e1a3b9b9e52146220898dc77ceb227475a060ef102aad4be838d02412f1cec92517ff209217b21d394780b2a15d3fb4ea267ee574ef160a175ecb27709bfdc77fecb7d7be01e8930a69a4f0306ddc2882c82b74e5eee337fce4aef453464fbce256757b190bad4f48afb7ab1efb34b20836b2eb60071ae390d5ce5d180edef8cb61b1df94c7992c79a8ba8d457a3194df442c8468b804717307101500c1e6b2fd1847bd8d342b2d9be33a39778398a8190934ff005cd75c0263911c9efe4fa1ae7f915ccef2b186963f3e6666f6c5be503d8cea64268a5258dc79e52e3fc6a14f26804779c140ee406b5604c5b08cd35d1cb1890d0ecbdc697cd1342a6c25f926bf3939098fa3e00e44e85d622dc257fd98edf7f9d3e9b01d2ee8c863c58ee12e75b165c32cd3d4a5eb184395b24bfb1450e24507b8b281a2917b899cf6c17379f983f49f0ff165fe6238654ce79e46a11ef929dd1972cacb41d21b06e2822a1620a0270dd06cb8f2c0ac416f95144d483bf20794a00436e11e708c4863309205e01847937b52128301b72b78f72e34fb4615d2d27c83acda748afeea7339890eea779cffeca9e5b9fe63dfb3915a642fa4c5ada4522aae11588b9a89179976c7f5d9de9d21dd38dfb6531e1b1501c256217790a64f2b23e0057da8d843b4d4029e87c2873db309c8b0c6741abbd0888054918e5eff85cc50185b9e411f03a73a781f02e323880b5ad24033f66fc91338e519bc03ac58329f52ee612351e8e2224803f9f6e3c7df6628b3c7dbe8dffed6c7e5da5a0a43e4fd42e7956423ea3cf4efc9e3c2fee67847c0ab79f66b76fea78019b45239f82e2a8f179c06600ed76c1e815f476b68bdb86f369c0e7487f44f2eb7e5a6504ce147193be3817e4090f516bd248b74802d888fcb5ab05d7cc353283af5e81a2a968832b66a9ff7c7bbbb527bfa1582cfe4f41adf441e18f544883c0ad50b113a0f4e5ff0c99cf29d908e9fb94a67f79f197f8fd66de412a576065635013c0d2137ba72c7d796342c67f06de075bab0925b9ae2560c934aa6d369a8f76f3b6e4cf4014095a660a8695c40ba0b402a1228084b822385553c9e885423b1c08785db2580a782700161c2bb09060d0afb85e947bb4fca446e4cfe3fc768d0812c894cdca4dce8e7b6323826089c7d6147fb2673034f607bc18722e62101549d001c07b11bdcced38bdc427f60faa97eca7cf663409c0dc821fc04c3b3ea7c6685a5bb2e7f3bc13b491149096e933b4422015d531dca90467c999ecf1709e3d41bde510253d34632e5eb93b4fbf32d69358ebee3c7b0ab6d5f09efd3d9e90dc98ee19b6c93a5b701f14a8fb1eac5d757c6b5812cd60f4df8158f068d26a854d7793b44586491294f0c8a853fa69043647dc587074a13818f0249e83ba6127d14c8c227655a28f69081ea316516aaaed45ee5f4d75940d803fe15f3123d63bb08e1ab88017f9401b9bd501ca449807cb7881be09c97fb9de825d4af89bc44e86f5e72c54b9e0179d802c440a0c032b7eb9973448e07f261570c5be737d5d46175b287d73e34cdedabbc084e4fab3b4f5673755229b81c69604254a8e035ea56b8a8b2f45ec8baba88a2d4d09f327a7de0ef87a3e472f13154c7a51eea586e3e4a5cfb505f0dde7d114e8b137cd591d445723b5a6f08f56c62f18251f3764515201d2a031c9412dcf2168cd53160b9cc3de895bf87e6dc6ea281cf83ec1eec0ac0bb94c9b22c455a2f64184f2310822a0ca15b394e9810abbec83272724bfe43e8acd1a508355d067566bab41a07b62de0376d6ddedc8c88753f19dc4b0af0f9a05d89893e387a4a15a241a197810d06236eb85d876773bfa8101d754ea816c3883e68b1e784e6d8f0f49413a15c93090c1644bedb230d6cb3ee9c13e5700bd374e8276ed593cd91b0336264e1c8379b1ee408725b7edd17ff8864ba5c111b8da22220a960442dfab88f0198998c794a272f93549694aaea88cd0614a7d8db4fbd4203e81fe677c7e12a109cfd534745c380e15600237f4c123285bd7d273a0030b88f9df4d87ad5a847a5ba4b56f5aed2d9e956c5aa58d09c59d8c47209e643ef3c1b37a36c969d73d00fa5d75851f97a6f07c81991da441228d374516549129631047d04bccf2241afc3cf0413d08c2e00d7f54b83b244c701a52c0d0dd4c1b995c0f3818d5ae477be3b8ca1ebde063e059f35206f829e89ca8b0eca39018e2c4d4337a5449aebb100d960a40da66fcbdd33299d51b95cbd245fab3211c6c9e04e048032fd758bfc2e4597ff862619e4d1c4d36d0fbc6a069b3e8a155205eac920717e3e1d821dc477aa72370a6564b462a45d7d7f8ca113ed8c05fa39363708ccd14c225a3daf689610dfefd01c0aef99c2ba5aed20e5a8210b6895ead9971ed2a9e1bd021a0b1ca1cba187c32ccd0fda9826fe60de24d40ec531ef9ec3d3aa48dee0778ca65878b582a8d8eb9649e99bedf894906bea17a7173d3d77d211aa4885890c20a02426f61297ca6a94e8a396805bae99be79db8570b062e79125d44c6c17d6b7f349deee17d29e0ce1823a833fbe34e7d7934b2dafa2cfdd5df5ba30fd08a6d4cd50183c9c0f60c44c8c6f535f6fb86490f388ace59490a3edb6c876e6dc8b9dfcae4ed609f40e34784b9eb492dd6b1b7c0245625bfae52536590a9a2b21456053f1368924b7663b421a6b6deb09e006a977d9b842abf426a19d9f45a69c98d08b413d5a695bae655cbae47f870d16f10616a7b8d616e504ae101f4bd6260bf9224497319a9823b39364b137e274ee36ea4ee8eaf4d96b92266510fdedf9896f78070aed1ef8477e6ec8610ca05bfa494fc75c4a34b1af02eee194c96d4d175159ff7d1e5a594e28e6469c7e143b2c53c10d3be08f9afd086066df98ffb20c2dcf4fe0d0f9802cb4f832bba54af9270cae4cd4d437f730d10a6c36f918ebe0e97daf435e51145e63bfc70e45d88b08fba3f08efe1881b60e7f7435bd3d547445a2f1089ef621e2410b42f7ff71ae2c0d73313fbdd85bedded69879a8a20deb5f49f90ed6a4e485c61305c38a559daa3398b4527779d44eca9650e1bde4809b3cce95a89dcfe3e6037d9ee01679b1cLine truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7d697bdb36b6f0f7fe0a44d3c59ed794ec24cdf4756dddeb259dba4be2c776a6b7b74fef3c100949a8498201403b1ad7f3dbef3900f745a42cdbf1dce987c422b11d1c9c1dc0e1deb3e3b747173f9fbe26731df8e34ff69e39ce27a3113912d142f2d95c938da34df27c7be725f98e5e8a093914724668e8418d504b3e89b5908a6c28c6889e3372f4f6cdc5d9c9e1bb8bb767e764ca7db639c4ee0e7c9f98ee14914c3179c5bc2179a718115368c6155122962e23aef01881c799b86232641e992c6030f2e3c985a3f4c267d897cf5d162a1c8e6ae242e98491a98801241e1a187e38397afde6fcb5197ef889e3c0ac7072c4a7e16c7fc0c20109670e8da2fd815a842e0c1fcecc2b1767247c9fc9fdc1795a72a4a53f20ae4f95da1f60255fd0cb0176c9a837fe8490bd80694adc39958ae9fd41aca7ce5783bc60ae75e4b0f731bfda1ffc97f3eec039124144359ff80cba85115908ad4e5eef336fc60aed421ab0fdc11567d79190ba50f59a7b7abeefb12b4083631eb660e25c73ea3bcaa53edbdf196ed73af29872258f341761a1af5a351aebb990b51a3e0f2f61e17cc018146b37d684bbd8d35cb2e9fe604aaff071180122c79f6003cdb5cfc61912c9efe4e606d7f90dacef1b186963f3f6766f646b6503d8ce264268a5258d46ae52a3ec6918f070086f06091c480d6ace98b6109a67a217114c42b30f1a1b9b124226c25b901bf39390887a1e00e44c84d622d8257fd98e3e7c9d944d61d2ce9406dc5fec92c1b7ccbf629abb94bc61311b6c91ecc516399080ed2da268a81ca0663eb55ddc7e62fec4feff9b7f998d185039e3a1a345b44b76865fb2a0547788c03a8108858a2830c04d132c3fb2d0175be447115217fe02e7290104b9450647c0369c4900f11a20ccbaa90c4181d890ba3de41b6fac0de96a3946d2a99722beda4ba7b020eda559cfded29e1b4bb39ebd0c0b13213d262dee421156e6e58b99a8a079976c7f5d5ee9c21bd38df365bee091501c396217690a78f2aa3a0057da098533894128e86c28f3da109c83046741ab348489f97110666d3cae229fc25af210689d39135fb897291c40da9693811e53fac808c7884d209dbc6042ddcb99448987a30809e0cf261bcf5fbcda22cf5f6ee37f3b9b5f973128a9c763b54b5e14269fe26727fa405ee6ef53443e87d7cfd3d7b7d579019985438f82e0a8d0b9cfa600ed764ee8a5e9ed6ce7af0de5539fcf10ff38c9afbb7195223815c475fce25a90673c40a94943ddc009a023b266d773ae996378069b5e83a02949836b66b1ff727bbbb127af2658ecfc9f8358e982c21baa80fabe53c2622b4049e3ff0c98c729d908e88704a77f79f597e8c366d641c257a06523101340d263fba6c87d596542467f06da075dab092599ac25a0c9348a6d369c0d77b3bae4cf8014095266028a954473c0b402a6223009714d70a92692d14b857ad817d05cb2480a68e303098e14684850e8d75ccf8b3d5a7a5243f2e751f6ba820409684a57e53623c7bd916141d0c423ab8a3fd93333c4228f5f5515faeb2bd06746998ff746506ef514583be44244c05292a0a180ef427a95e97b7a8525f60f8aa1f4a7c7a634f6412d43f7ccd4e3336a94abd5390682a41384820272645286da0ab8a73c8633916054016c3c98a52528df06444917d59d834fcecef3af8c9625d60a18bc780e3ad8d0a8fd3d1a934ce9ee19f24a3b9b730f04adf301b462797cab8062cd60f4df01a950346ed4d6a6bb7152239d49ec17e69162a7f0d330763671a3e971653828fa389a81586227e1540c43765dc08fa90896a51661a2d2ed4366874d74980e803fe15fbe22d68ab0061d988a97d9401b9be5018a4898f98b688e360cc97e39ee9c5d49f81b478374d69fb340650222ef04782654a04058fecbb9a27e0cff33a9802af6073737c5e9620da56f6f07e377f62d1021b9f92ca9fdd96d19c966a09145410193239f97f19accc59322f2c475589e2d4d10f3a741b51ed0f56c86d6280aa2e4a1d84b658ee3d71ed716c0f79f8713c0c7de24237560718dd89ac03f5a1a3f27946cdc8085710948338d71066a710d41ba9eb148e01a762edcdcf32a2b569dc281e711ec0ed4bf908ba42a425c466a1744c81fbd2002ac5c338b990ea8b0cb2e783244f22bee21dbac0035680f7d6ea5bbea05ba2b661d60a7dddd0d8dbc3f16df4b740fbba099832e3a397e481caa79ac91807b012da6d34e886d7777c31f287a4da5ee498653a83eef80e7ccf6f89018a41311f7031954bbd40e0b22bde8e21eec7309d07ba3d86f969e79496219e00f502fd61c68d1e4b63eda0fdf70a9341802d75b4484fe82808b7c1d123e2521739952542ebe26094ec93595211a5689ad919b2aa8109f41ff533e3b09518567621a3ace0d871230be1378601114b56ba11cf0c07c62fe779261cb1aa15a1771ed995a7bf317059d56aa635cf6414a23e077328f796081bd1867b86b1f00edb3aac08f0a4b7831c70810e22096c69a2273aac88431f037e8154683620df620d8aa2e386bd0c21be6e60e09625c860430344b934a2626040646b9ebe1de282a934727f8e8a056ac941e760a1a272a28da2824027f32b18c1e9593ab26448da47ce0b629ff306858ccf28bd263e12137adcbccc166b10f8634d07285f44b449ef6870d73f56cfc6db281d6373a579b790f8d0cf16a193f38e8374703c23dc4773202676a3967245c7473834d8eb060037f0d4f8ec130364b088f8c6adb27ba3ff8f70700bb62732ee5ba523da8094cd8c47a956ac6b42b596e80079f462a35e822b0c93092f7a7d27c536b105fc2c43ee5a1c73ea0415aebbe87a55c34b888c5d2f0984be69ae5fb9d98a0e129d5f3dbdbaeee73d620b9c7821856e038ba738be1734d759caf4123d075db3cebc4b99e3330c9e3f0323406ee3bfba36e74f7ef4b017546e8419ddb1f6bf5e5d2d04aebf3e457776fb53e402a3611550b0c2652db3110211b3737d8ef29932e50149db102177cb6d90cddca9073af91c89bc13e81ca8f08735b49c5d7b1af40259639bf2a521361908aa2221796193f65689271766db43eaab65ab11a286ae67d1bac2a362195c86df2acb4e486059a91aad3e04c73a96c2bc2c279b74284a5ed5486994229b807d0f79281bd52902489652402eee4d86c6178ad731ab54f6afdf9daa09a23221676ccfb1b53f31e269c49f4b5e69d1abb01b872fe2f09267f1df2f08afabc8d7a7aa32531741dc5675d78792da558132dcd73f8986431f3c5a4cb43fe2bd4a17e53fce33e903033bd7fc37da640f353ff9a2ed49b389830797b5b93df5c0384c9f05ba4a5afc385367d4d784891f80e3f1e7ae722e8c2ee0fc27d38e4fad8f9fde0d674f58450ebfa22f61c8c83f88276c5efde821ff8766a7cbf75f0db5e9fb688a910fc5d8bff31d92ec784c4353ac3b9519a863deaab9877b2ee22624f0d6b58b3460a334bLine truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["index.html"] = bs
|
||||
@@ -103,7 +103,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["lang-fr.json"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ff9459dd6e1cc7b1bef75314081820017a7d8ee1732e74618322251d1efd90114519060404bd33bdbb6dcd748fbb7b965a090e729b57c85584003114c057426e74997d933c49beaaeef95bfed83160883b5d535d5d5df5d55735ef3e23fcb777747e4a8ff566ef1eed1dafd45a131eec1de6b5b96b23af1c15da178a4addaf94253d73f8cd8ba5298cb3cad3b3edc7b1c073ddb860a2f39ba9587ebefde88d1bc97b1d020b3eb0a5f67afbf3ee9a9eae867eb9aadc151d596737b56b035d06b5d4b2898fc62ebfe5b7ceb5af4d349e1ed8b5713808d62b954c087877fbd19ada856f7b9dd6bad6169a2eb45f6bcf2af82f533acf2f2bbbfd64719ac186db764f26dfb567a7e27ebb4c07f47e78787c7e4997d154e6ad8a701eafe79fdb9fb77f85524510e9a52b17e44a1eea62a57cffd8351b6f96ab48fffc405ffdd77f7f4dffaf5ebb39dd777e89a39414579a168edd088be9d8d9e8cdbcc5c5857bacedc4788d6b0cb03fc009aac49f3768d284e7412f5b63a30ebd1af69986a26ccc89ae744c71d3a8e560e4890985b356175197b2a9e6dff889ed7a1957b4b5b6b1f745ff40bc31885dd9ca2904a04a3bbd506f3878297a65c342fbeddf7179aa137f501a0972fe77b0877ff5219e96c6f12dcbd300cf423745f703abe695a6cb737b2e278f660d49f9d94b44eda97075ade0c34679985ed2ab3dd3dce3487ab547aacb02420cbeda2b3756d5a6c04274d468bf70be26854babe19d824a76272277436e21f79b5f9ff1fea7b81957b66f15e93e9b86bd1c742613f8a2f184d6c62fdb4a61e776b233cb104736f69a7bc337e1920ddbf730022b6ed861d61f1501eebb48ef1e3e34f0cf4bed036e36a7cd231da2c4b8261c246cffa1c344bae19c0efc02cd4d0ca4bc26b3b488b692ae56da52e5dc6b0e68b886900f76a9c30cf98938b5f4f0e8052da0256c42d47572cb51e874f25e6cfcc2142b6dbca3c086887255c20f564132fcd89aa0c441755b02127e56ac5fd25393e56430ac1cf23842a72af0d6b3f14192e5356eabe4bb2c71f314a2aa1bfc5e278f04329614cd42ec1f205fe1d87450af9b4a15101774e2fc2a69bea1b0b1455cc10372bab330b241ce832d8d5c315f63e7622a146e722ec74438e27a7184c6916d11998d826113337e6c0120f04f3b0fd1c476fb0b2b4498284eee2e7e46865c3b77e35d94a4a785c776f99ea856a5dc9343ec7a3813317648c0a46b02d01b649dae4c55d11cbe4318b22339ea11f2b1cb80a26a711dfe1667881d4bf10774f5174a0b401ffed1f0441b591ab80d53f8565505750c3decb62c67b1a1661998e159af06f88b3373b2b04592186ae9db4649ce0c6e914b9582c59eee1e3fba3ca5a31667b0c82a013fc27ab872be4c95c9ae04df2093445271c06b77684022f8513de92adb9d2a9e70405bbab920f3eb40821611327e4d5bed330e3fc25f3dc03eaadc5c5574d201550ffa73ed59852cef084f01778cb43bf2a7659580bb864948eade95a7021074de63879c419e0291cf87f4ef5e78ac75d30193c83e5502d52f7720e909ce18b824545aa522bcfd73154d9d336ba84e4f5c71c7b165f516513a073a4b70304ab3bbef7ef10e6f5563e9a78af342c4af093ec5a1bc1e24df9060efb164a11458da7f6cee7f190efa420bf07fc3e79682eb84368e92ad931e693c6b2382dd968cd5cff58f2db6144f3fdb7eaa35c037299403031313647159619ad1e979e6d21b839fb976d3e9895cef0927dca87acbda33249fbce46a7d6d1d29901787343c7b2cd0f1b8ffbd5854c6eadbd8ca99ed568faf2d01c8ce1674015ceccc46b1b08547f17b3b955b3a710b3bfa8901871dbb5b1e68268131331c2d5473c7c767081e6fcaeed6c4b71728d8cc51e96987749df4b98aab013ebb084a50ab53e07061685ac652fa2e836ee1b53016b320d8583acda01c49bf0162cce805de04ccc004e6a5aae070dbffc3016a8de597db8037195029ac10da8010aed9e239300dbb7203788e42d3ba6c876097183663bebe7d0f6b80b97029233163305b6160ed1957b7edfb7eff86e30038486d0782ac12d81c559577eddd22794d572a91c5a365abfcd00f9d7bbd36fa6ac2fe59eca509ad1ad1f5710fd0bdfbfce8e9af717c88f4d23ad3643a651c5ae336f6533474bf651baf91456a0e342a322fc3c3ca2cb3cefd213a06949864cb4d3476249a30e357e06224cf5135bed05b77081c13771ead46924067ef9fc95970e8e4f9e7da58349c43bdc96bf44ceb32b518db3fa1eac33f81036b47fa02adb0942cafd6a3a730aea3a72f95f740a1def40bf675942411bac23914564c7184a28c52e9cac4d52c156ebe1e8003655e218c0b94ca085d4010e07d1246318efcd9b065e41e33648c599865dbf1915e442cf80e3b4a331312b7e854f37680bda97429e263415cd6b1ab0721043719c65fb330481ee6d753b4980156500ce75a9e07002de38702546412965c949a91aca890342e5a2e6d37a43b4044b36f52bee2a4880e3c884e086776df2db40a165fe5e07eea10a038397e8d162df835cc42cb08ae980b0753ee116d644a1edb647252c29ba129b0ebed07d9b9af328c4c1c6ce5ad3cef62d5c612dbe6d2c17939c4986b7d01acceade7f1f66369968e1ea29dee13eb4222f9be775799c49da6e8454d5b6be6dd83b6e89a2685fbb97474fd42db3048d197d0ecdb5ac2b1e5279a1fc1e5ed70e12853740e7eec0a57ddcd01c38804968954f34b92b743819b74ebac7d959e77f07771bb644ebe41629c815d9b412b1493b98695018e26f674cab75e60e10cb7adecf872742dfd3ad457ad24f1643412dc225e7142215265c0c32d107728da2dee4d77100d86abda3012514d85302fb838a245e20ce736130561188f0c0a82465fca2032d77282434a69c65d2e974af8765e2103184d6883a049a869357aa4542160dd0c281bfd06fafef5c7bf4d0dc4f6bad012a89e3369aecc1b77c88649b94455aebb3d940005aa73ab4635a4db6e4647c44d83e2ac5e4ba4ebd9ac772ad77eb55c7ac42513044e23aec6456effda397c526d08306b2a9992a848efdeb5befae9a7dc94b38971fb0bbfc42441f2893baa6e3e6042e3ecf697b566d8147dc9083e55a769644cd121a5b44143a8286ec0b9c564e2028e62d66cf08c0697d50c6a3ed7111e7564a104fa28ff13d569a6303c4b5ee3c85b72514159e72652084a5254aaf16631c35d5f9a122a26518fdb6067ef689f385d2345360dfbbc1556227c2f72f048875cc2df00ea5379220c8c491fc8d96b1943c1350d980a8f95d0eef2bb48e0b71cc4165cad69fad104542c7af8f7bc5d09fa843d44716ad74b0ed0be414754d5629784ae54c7d2a0b82f71b7ca24bf9d415d4794183cda31232acc42e077fb21b7d71c040a9c2fc51ea1b00f844ebc2639e8e5448a8f94861badc5d1a2aa15184948ac69283888af612882cb4ac99bdc0c9a49Line truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ff9459dd6e1cc7b1bef75314081820017a7d8ee1732e74618322251d1efd90114519060404bd33bdbb6dcd748fbb7b965a090e729b57c85584003114c057426e74997d933c49beaaeef95bfed83160883b5d535d5d5df5d55735ef3e23fcb777747e4a8ff566ef1eed1dafd45a131eec1de6b5b96b23af1c15da178a4addaf94253d73f8cd8ba5298cb3cad3b3edc7b1c073ddb860a2f39ba9587ebefde88d1bc97b1d020b3eb0a5f67afbf3ee9a9eae867eb9aadc151d596737b56b035d06b5d4b2898fc62ebfe5b7ceb5af4d349e1ed8b5713808d62b954c087877fbd19ada856f7b9dd6bad6169a2eb45f6bcf2af82f533acf2f2bbbfd64719ac186db764f26dfb567a7e27ebb4c07f47e78787c7e4997d154e6ad8a701eafe79fdb9fb77f85524510e9a52b17e44a1eea62a57cffd8351b6f96ab48fffc405ffdd77f7f4dffaf5ebb39dd777e89a39414579a168edd088be9d8d9e8cdbcc5c5857bacedc4788d6b0cb03fc009aac49f3768d284e7412f5b63a30ebd1af69986a26ccc89ae744c71d3a8e560e4890985b356175197b2a9e6dff889ed7a1957b4b5b6b1f745ff40bc31885dd9ca2904a04a3bbd506f3878297a65c342fbeddf7179aa137f501a0972fe77b0877ff5219e96c6f12dcbd300cf423745f703abe695a6cb737b2e278f660d49f9d94b44eda97075ade0c34679985ed2ab3dd3dce3487ab547aacb02420cbeda2b3756d5a6c04274d468bf70be26854babe19d824a76272277436e21f79b5f9ff1fea7b81957b66f15e93e9b86bd1c742613f8a2f184d6c62fdb4a61e776b233cb104736f69a7bc337e1920ddbf730022b6ed861d61f1501eebb48ef1e3e34f0cf4bed036e36a7cd231da2c4b8261c246cffa1c344bae19c0efc02cd4d0ca4bc26b3b488b692ae56da52e5dc6b0e68b886900f76a9c30cf98938b5f4f0e8052da0256c42d47572cb51e874f25e6cfcc2142b6dbca3c086887255c20f564132fcd89aa0c441755b02127e56ac5fd25393e56430ac1cf23842a72af0d6b3f14192e5356eabe4bb2c71f314a2aa1bfc5e278f04329614cd42ec1f205fe1d87450af9b4a15101774e2fc2a69bea1b0b1455cc10372bab330b241ce832d8d5c315f63e7622a146e722ec74438e27a7184c6916d11998d826113337e6c0120f04f3b0fd1c476fb0b2b4498284eee2e7e46865c3b77e35d94a4a785c776f99ea856a5dc9343ec7a3813317648c0a46b02d01b649dae4c55d11cbe4318b22339ea11f2b1cb80a26a711dfe1667881d4bf10774f5174a0b401ffed1f0441b591ab80d53f8565505750c3decb62c67b1a1661998e159af06f88b3373b2b04592186ae9db4649ce0c6e914b9582c59eee1e3fba3ca5a31667b0c82a013fc27ab872be4c95c9ae04df2093445271c06b77684022f8513de92adb9d2a9e70405bbab920f3eb40821611327e4d5bed330e3fc25f3dc03eaadc5c5574d201550ffa73ed59852cef084f01778cb43bf2a7659580bb864948eade95a7021074de63879c419e0291cf87f4ef5e78ac75d30193c83e5502d52f7720e909ce18b824545aa522bcfd73154d9d336ba84e4f5c71c7b165f516513a073a4b70304ab3bbef7ef10e6f5563e9a78af342c4af093ec5a1bc1e24df9060efb164a11458da7f6cee7f190efa420bf07fc3e79682eb84368e92ad931e693c6b2382dd968cd5cff58f2db6144f3fdb7eaa35c037299403031313647159619ad1e979e6d21b839fb976d3e9895cef0927dca87acbda33249fbce46a7d6d1d29901787343c7b2cd0f1b8ffbd5854c6eadbd8ca99ed568faf2d01c8ce1674015ceccc46b1b08547f17b3b955b3a710b3bfa8901871dbb5b1e68268131331c2d5473c7c767081e6fcaeed6c4b71728d8cc51e96987749df4b98aab013ebb084a50ab53e07061685ac652fa2e836ee1b53016b320d8583acda01c49bf0162cce805de04ccc004e6a5aae070dbffc3016a8de597db8037195029ac10da8010aed9e239300dbb7203788e42d3ba6c876097183663bebe7d0f6b80b97029233163305b6160ed1957b7edfb7eff86e30038486d0782ac12d81c559577eddd22794d572a91c5a365abfcd00f9d7bbd36fa6ac2fe59eca509ad1ad1f5710fd0bdfbfce8e9af717c88f4d23ad3643a651c5ae336f6533474bf651baf91456a0e342a322fc3c3ca2cb3cefd213a06949864cb4d3476249a30e357e06224cf5135bed05b77081c13771ead46924067ef9fc95970e8e4f9e7da58349c43bdc96bf44ceb32b518db3fa1eac33f81036b47fa02adb0942cafd6a3a730aea3a72f95f740a1def40bf675942411bac23914564c7184a28c52e9cac4d52c156ebe1e8003655e218c0b94ca085d4010e07d1246318efcd9b065e41e33648c599865dbf1915e442cf80e3b4a331312b7e854f37680bda97429e263415cd6b1ab0721043719c65fb330481ee6d753b4980156500ce75a9e07002de38702546412965c949a91aca890342e5a2e6d37a43b4044b36f52bee2a4880e3c884e086776df2db40a165fe5e07eea10a038397e8d162df835cc42cb08ae980b0753ee116d644a1edb647252c29ba129b0ebed07d9b9af328c4c1c6ce5ad3cef62d5c612dbe6d2c17939c4986b7d01acceade7f1f66369968e1ea29dee13eb4222f9be775799c49da6e8454d5b6be6dd83b6e89a2685fbb97474fd42db3048d197d0ecdb5ac2b1e5279a1fc1e5ed70e12853740e7eec0a57ddcd01c38804968954f34b92b743819b74ebac7d959e77f07771bb644ebe41629c815d9b412b1493b98695018e26f674cab75e60e10cb7adecf872742dfd3ad457ad24f1643412dc225e7142215265c0c32d107728da2dee4d77100d86abda3012514d85302fb838a245e20ce736130561188f0c0a82465fca2032d77282434a69c65d2e974af8765e2103184d6883a049a869357aa4542160dd0c281bfd06fafef5c7bf4d0dc4f6bad012a89e3369aecc1b77c88649b94455aebb3d940005aa73ab4635a4db6e4647c44d83e2ac5e4ba4ebd9ac772ad77eb55c7ac42513044e23aec6456effda397c526d08306b2a9992a848efdeb5befae9a7dc94b38971fb0bbfc42441f2893baa6e3e6042e3ecf697b566d8147dc9083e55a769644cd121a5b44143a8286ec0b9c564e2028e62d66cf08c0697d50c6a3ed7111e7564a104fa28ff13d569a6303c4b5ee3c85b72514159e72652084a5254aaf16631c35d5f9a122a26518fdb6067ef689f385d2345360dfbbc1556227c2f72f048875cc2df00ea5379220c8c491fc8d96b1943c1350d980a8f95d0eef2bb48e0b71cc4165cad69fad104542c7af8f7bc5d09fa843d44716ad74b0ed0be414754d5629784ae54c7d2a0b82f71b7ca24bf9d415d4794183cda31232acc42e077fb21b7d71c040a9c2fc51ea1b00f844ebc2639e8e5448a8f94861badc5d1a2aa15184948ac69283888af612882cb4ac99bdc0c9a49Line truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["lang-pt.json"] = bs
|
||||
|
||||
+48
-19
@@ -47,7 +47,7 @@ var (
|
||||
static func(http.ResponseWriter, *http.Request, *log.Logger)
|
||||
apiKey string
|
||||
modt = time.Now().UTC().Format(http.TimeFormat)
|
||||
eventSub = events.NewBufferedSubscription(events.Default.Subscribe(events.AllEvents), 1000)
|
||||
eventSub *events.BufferedSubscription
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -56,6 +56,8 @@ const (
|
||||
|
||||
func init() {
|
||||
l.AddHandler(logger.LevelWarn, showGuiError)
|
||||
sub := events.Default.Subscribe(^events.EventType(events.ItemStarted | events.ItemCompleted))
|
||||
eventSub = events.NewBufferedSubscription(sub, 1000)
|
||||
}
|
||||
|
||||
func startGUI(cfg config.GUIConfiguration, assetDir string, m *model.Model) error {
|
||||
@@ -92,32 +94,33 @@ func startGUI(cfg config.GUIConfiguration, assetDir string, m *model.Model) erro
|
||||
|
||||
// The GET handlers
|
||||
getRestMux := http.NewServeMux()
|
||||
getRestMux.HandleFunc("/rest/version", restGetVersion)
|
||||
getRestMux.HandleFunc("/rest/completion", withModel(m, restGetCompletion))
|
||||
getRestMux.HandleFunc("/rest/config", restGetConfig)
|
||||
getRestMux.HandleFunc("/rest/config/sync", restGetConfigInSync)
|
||||
getRestMux.HandleFunc("/rest/connections", withModel(m, restGetConnections))
|
||||
getRestMux.HandleFunc("/rest/discovery", restGetDiscovery)
|
||||
getRestMux.HandleFunc("/rest/errors", restGetErrors)
|
||||
getRestMux.HandleFunc("/rest/events", restGetEvents)
|
||||
getRestMux.HandleFunc("/rest/lang", restGetLang)
|
||||
getRestMux.HandleFunc("/rest/model", withModel(m, restGetModel))
|
||||
getRestMux.HandleFunc("/rest/model/version", withModel(m, restGetModelVersion))
|
||||
getRestMux.HandleFunc("/rest/need", withModel(m, restGetNeed))
|
||||
getRestMux.HandleFunc("/rest/connections", withModel(m, restGetConnections))
|
||||
getRestMux.HandleFunc("/rest/config", restGetConfig)
|
||||
getRestMux.HandleFunc("/rest/config/sync", restGetConfigInSync)
|
||||
getRestMux.HandleFunc("/rest/system", restGetSystem)
|
||||
getRestMux.HandleFunc("/rest/errors", restGetErrors)
|
||||
getRestMux.HandleFunc("/rest/discovery", restGetDiscovery)
|
||||
getRestMux.HandleFunc("/rest/report", withModel(m, restGetReport))
|
||||
getRestMux.HandleFunc("/rest/events", restGetEvents)
|
||||
getRestMux.HandleFunc("/rest/upgrade", restGetUpgrade)
|
||||
getRestMux.HandleFunc("/rest/nodeid", restGetNodeID)
|
||||
getRestMux.HandleFunc("/rest/lang", restGetLang)
|
||||
getRestMux.HandleFunc("/rest/report", withModel(m, restGetReport))
|
||||
getRestMux.HandleFunc("/rest/system", restGetSystem)
|
||||
getRestMux.HandleFunc("/rest/upgrade", restGetUpgrade)
|
||||
getRestMux.HandleFunc("/rest/version", restGetVersion)
|
||||
|
||||
// The POST handlers
|
||||
postRestMux := http.NewServeMux()
|
||||
postRestMux.HandleFunc("/rest/config", withModel(m, restPostConfig))
|
||||
postRestMux.HandleFunc("/rest/restart", restPostRestart)
|
||||
postRestMux.HandleFunc("/rest/reset", restPostReset)
|
||||
postRestMux.HandleFunc("/rest/shutdown", restPostShutdown)
|
||||
postRestMux.HandleFunc("/rest/discovery/hint", restPostDiscoveryHint)
|
||||
postRestMux.HandleFunc("/rest/error", restPostError)
|
||||
postRestMux.HandleFunc("/rest/error/clear", restClearErrors)
|
||||
postRestMux.HandleFunc("/rest/discovery/hint", restPostDiscoveryHint)
|
||||
postRestMux.HandleFunc("/rest/model/override", withModel(m, restPostOverride))
|
||||
postRestMux.HandleFunc("/rest/reset", restPostReset)
|
||||
postRestMux.HandleFunc("/rest/restart", restPostRestart)
|
||||
postRestMux.HandleFunc("/rest/shutdown", restPostShutdown)
|
||||
postRestMux.HandleFunc("/rest/upgrade", restPostUpgrade)
|
||||
|
||||
// A handler that splits requests between the two above and disables
|
||||
@@ -175,6 +178,25 @@ func restGetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(Version))
|
||||
}
|
||||
|
||||
func restGetCompletion(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
var qs = r.URL.Query()
|
||||
var repo = qs.Get("repo")
|
||||
var nodeStr = qs.Get("node")
|
||||
|
||||
node, err := protocol.NodeIDFromString(nodeStr)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
res := map[string]float64{
|
||||
"completion": m.Completion(node, repo),
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}
|
||||
|
||||
func restGetModelVersion(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
var qs = r.URL.Query()
|
||||
var repo = qs.Get("repo")
|
||||
@@ -423,11 +445,18 @@ func restGetReport(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func restGetEvents(w http.ResponseWriter, r *http.Request) {
|
||||
qs := r.URL.Query()
|
||||
ts := qs.Get("since")
|
||||
since, _ := strconv.Atoi(ts)
|
||||
sinceStr := qs.Get("since")
|
||||
limitStr := qs.Get("limit")
|
||||
since, _ := strconv.Atoi(sinceStr)
|
||||
limit, _ := strconv.Atoi(limitStr)
|
||||
|
||||
evs := eventSub.Since(since, nil)
|
||||
if 0 < limit && limit < len(evs) {
|
||||
evs = evs[len(evs)-limit:]
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(eventSub.Since(since, nil))
|
||||
json.NewEncoder(w).Encode(evs)
|
||||
}
|
||||
|
||||
func restGetUpgrade(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
+12
-1
@@ -18,6 +18,12 @@ type translation struct {
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
|
||||
if u, p := userPass(); u == "" || p == "" {
|
||||
log.Fatal("Need environment variables TRANSIFEX_USER and TRANSIFEX_PASS")
|
||||
}
|
||||
|
||||
resp := req("https://www.transifex.com/api/2/project/syncthing/resource/gui/stats")
|
||||
|
||||
var stats map[string]stat
|
||||
@@ -63,9 +69,14 @@ func main() {
|
||||
json.NewEncoder(os.Stdout).Encode(langs)
|
||||
}
|
||||
|
||||
func req(url string) *http.Response {
|
||||
func userPass() (string, string) {
|
||||
user := os.Getenv("TRANSIFEX_USER")
|
||||
pass := os.Getenv("TRANSIFEX_PASS")
|
||||
return user, pass
|
||||
}
|
||||
|
||||
func req(url string) *http.Response {
|
||||
user, pass := userPass()
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
|
||||
+4
-3
@@ -632,9 +632,6 @@ func ldbWithNeed(db *leveldb.DB, repo, node []byte, fn fileIterator) {
|
||||
|
||||
if need || !have {
|
||||
name := globalKeyName(dbi.Key())
|
||||
if debug {
|
||||
l.Debugf("need repo=%q node=%v name=%q need=%v have=%v haveV=%d globalV=%d", repo, protocol.NodeIDFromBytes(node), name, need, have, haveVersion, vl.versions[0].version)
|
||||
}
|
||||
fk := nodeKey(repo, vl.versions[0].node, name)
|
||||
bs, err := snap.Get(fk, nil)
|
||||
if err != nil {
|
||||
@@ -652,6 +649,10 @@ func ldbWithNeed(db *leveldb.DB, repo, node []byte, fn fileIterator) {
|
||||
continue
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugf("need repo=%q node=%v name=%q need=%v have=%v haveV=%d globalV=%d", repo, protocol.NodeIDFromBytes(node), name, need, have, haveVersion, vl.versions[0].version)
|
||||
}
|
||||
|
||||
if cont := fn(gf); !cont {
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ func (s *Set) Update(node protocol.NodeID, fs []protocol.FileInfo) {
|
||||
|
||||
func (s *Set) WithNeed(node protocol.NodeID, fn fileIterator) {
|
||||
if debug {
|
||||
l.Debugf("%s Need(%v)", s.repo, node)
|
||||
l.Debugf("%s WithNeed(%v)", s.repo, node)
|
||||
}
|
||||
ldbWithNeed(s.db, []byte(s.repo), node[:], fn)
|
||||
}
|
||||
|
||||
+255
-102
@@ -21,23 +21,68 @@ syncthing.config(function ($httpProvider, $translateProvider) {
|
||||
});
|
||||
});
|
||||
|
||||
syncthing.controller('EventCtrl', function ($scope, $http) {
|
||||
$scope.lastEvent = null;
|
||||
var online = false;
|
||||
var lastID = 0;
|
||||
|
||||
var successFn = function (data) {
|
||||
if (!online) {
|
||||
$scope.$emit('UIOnline');
|
||||
online = true;
|
||||
}
|
||||
|
||||
if (lastID > 0) {
|
||||
data.forEach(function (event) {
|
||||
$scope.$emit(event.type, event);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.lastEvent = data[data.length - 1];
|
||||
lastID = $scope.lastEvent.id;
|
||||
|
||||
setTimeout(function () {
|
||||
$http.get(urlbase + '/events?since=' + lastID)
|
||||
.success(successFn)
|
||||
.error(errorFn);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
var errorFn = function (data) {
|
||||
if (online) {
|
||||
$scope.$emit('UIOffline');
|
||||
online = false;
|
||||
}
|
||||
setTimeout(function () {
|
||||
$http.get(urlbase + '/events?since=' + lastID)
|
||||
.success(successFn)
|
||||
.error(errorFn);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$http.get(urlbase + '/events?limit=1')
|
||||
.success(successFn)
|
||||
.error(errorFn);
|
||||
});
|
||||
|
||||
syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $location) {
|
||||
var prevDate = 0;
|
||||
var getOK = true;
|
||||
var restarting = false;
|
||||
|
||||
$scope.connections = {};
|
||||
$scope.completion = {};
|
||||
$scope.config = {};
|
||||
$scope.configInSync = true;
|
||||
$scope.connections = {};
|
||||
$scope.errors = [];
|
||||
$scope.model = {};
|
||||
$scope.myID = '';
|
||||
$scope.nodes = [];
|
||||
$scope.configInSync = true;
|
||||
$scope.protocolChanged = false;
|
||||
$scope.errors = [];
|
||||
$scope.seenError = '';
|
||||
$scope.model = {};
|
||||
$scope.repos = {};
|
||||
$scope.reportData = {};
|
||||
$scope.reportPreview = false;
|
||||
$scope.repos = {};
|
||||
$scope.seenError = '';
|
||||
$scope.upgradeInfo = {};
|
||||
|
||||
$http.get(urlbase+"/lang").success(function (langs) {
|
||||
@@ -71,53 +116,118 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
'touch': 'asterisk',
|
||||
}
|
||||
|
||||
function getSucceeded() {
|
||||
if (!getOK) {
|
||||
$scope.init();
|
||||
$('#networkError').modal('hide');
|
||||
getOK = true;
|
||||
}
|
||||
if (restarting) {
|
||||
$scope.init();
|
||||
$('#restarting').modal('hide');
|
||||
$('#shutdown').modal('hide');
|
||||
restarting = false;
|
||||
}
|
||||
}
|
||||
$scope.$on('UIOnline', function (event, arg) {
|
||||
$scope.init();
|
||||
$('#networkError').modal('hide');
|
||||
$('#restarting').modal('hide');
|
||||
$('#shutdown').modal('hide');
|
||||
});
|
||||
|
||||
function getFailed() {
|
||||
if (restarting) {
|
||||
return;
|
||||
}
|
||||
if (getOK) {
|
||||
$scope.$on('UIOffline', function (event, arg) {
|
||||
if (!restarting) {
|
||||
$('#networkError').modal({backdrop: 'static', keyboard: false});
|
||||
getOK = false;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('StateChanged', function (event, arg) {
|
||||
var data = arg.data;
|
||||
if ($scope.model[data.repo]) {
|
||||
$scope.model[data.repo].state = data.to;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('LocalIndexUpdated', function (event, arg) {
|
||||
var data = arg.data;
|
||||
refreshRepo(data.repo);
|
||||
|
||||
// Update completion status for all nodes that we share this repo with.
|
||||
$scope.repos[data.repo].Nodes.forEach(function (nodeCfg) {
|
||||
debouncedRefreshCompletion(nodeCfg.NodeID, data.repo);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('RemoteIndexUpdated', function (event, arg) {
|
||||
var data = arg.data;
|
||||
refreshRepo(data.repo);
|
||||
refreshCompletion(data.node, data.repo);
|
||||
});
|
||||
|
||||
$scope.$on('NodeDisconnected', function (event, arg) {
|
||||
delete $scope.connections[arg.data.id];
|
||||
});
|
||||
|
||||
$scope.$on('NodeConnected', function (event, arg) {
|
||||
if (!$scope.connections[arg.data.id]) {
|
||||
$scope.connections[arg.data.id] = {
|
||||
inbps: 0,
|
||||
outbps: 0,
|
||||
InBytesTotal: 0,
|
||||
OutBytesTotal: 0,
|
||||
Address: arg.data.addr,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('ConfigLoaded', function (event) {
|
||||
if ($scope.config.Options.URAccepted == 0) {
|
||||
// If usage reporting has been neither accepted nor declined,
|
||||
// we want to ask the user to make a choice. But we don't want
|
||||
// to bug them during initial setup, so we set a cookie with
|
||||
// the time of the first visit. When that cookie is present
|
||||
// and the time is more than four hours ago, we ask the
|
||||
// question.
|
||||
|
||||
var firstVisit = document.cookie.replace(/(?:(?:^|.*;\s*)firstVisit\s*\=\s*([^;]*).*$)|^.*$/, "$1");
|
||||
if (!firstVisit) {
|
||||
document.cookie = "firstVisit=" + Date.now() + ";max-age=" + 30*24*3600;
|
||||
} else {
|
||||
if (+firstVisit < Date.now() - 4*3600*1000){
|
||||
$('#ur').modal({backdrop: 'static', keyboard: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function refreshRepo(repo) {
|
||||
$http.get(urlbase + '/model?repo=' + encodeURIComponent(repo)).success(function (data) {
|
||||
$scope.model[repo] = data;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.refresh = function () {
|
||||
function refreshSystem() {
|
||||
$http.get(urlbase + '/system').success(function (data) {
|
||||
getSucceeded();
|
||||
$scope.myID = data.myID;
|
||||
$scope.system = data;
|
||||
}).error(function () {
|
||||
getFailed();
|
||||
});
|
||||
Object.keys($scope.repos).forEach(function (id) {
|
||||
if (typeof $scope.model[id] === 'undefined') {
|
||||
// Never fetched before
|
||||
$http.get(urlbase + '/model?repo=' + encodeURIComponent(id)).success(function (data) {
|
||||
$scope.model[id] = data;
|
||||
});
|
||||
} else {
|
||||
$http.get(urlbase + '/model/version?repo=' + encodeURIComponent(id)).success(function (data) {
|
||||
if (data.version > $scope.model[id].version) {
|
||||
$http.get(urlbase + '/model?repo=' + encodeURIComponent(id)).success(function (data) {
|
||||
$scope.model[id] = data;
|
||||
});
|
||||
}
|
||||
|
||||
var completionFuncs = {};
|
||||
function refreshCompletion(node, repo) {
|
||||
if (node === $scope.myID) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!completionFuncs[node+repo]) {
|
||||
completionFuncs[node+repo] = debounce(function () {
|
||||
$http.get(urlbase + '/completion?node=' + node + '&repo=' + encodeURIComponent(repo)).success(function (data) {
|
||||
if (!$scope.completion[node]) {
|
||||
$scope.completion[node] = {};
|
||||
}
|
||||
$scope.completion[node][repo] = data.completion;
|
||||
|
||||
var tot = 0, cnt = 0;
|
||||
for (var cmp in $scope.completion[node]) {
|
||||
tot += $scope.completion[node][cmp];
|
||||
cnt += 1;
|
||||
}
|
||||
$scope.completion[node]._total = tot / cnt;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
completionFuncs[node+repo]();
|
||||
}
|
||||
|
||||
function refreshConnectionStats() {
|
||||
$http.get(urlbase + '/connections').success(function (data) {
|
||||
var now = Date.now(),
|
||||
td = (now - prevDate) / 1000,
|
||||
@@ -138,9 +248,66 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
}
|
||||
$scope.connections = data;
|
||||
});
|
||||
}
|
||||
|
||||
function refreshErrors() {
|
||||
$http.get(urlbase + '/errors').success(function (data) {
|
||||
$scope.errors = data;
|
||||
});
|
||||
}
|
||||
|
||||
function refreshConfig() {
|
||||
$http.get(urlbase + '/config').success(function (data) {
|
||||
var hasConfig = !isEmptyObject($scope.config);
|
||||
|
||||
$scope.config = data;
|
||||
$scope.config.Options.ListenStr = $scope.config.Options.ListenAddress.join(', ');
|
||||
|
||||
$scope.nodes = $scope.config.Nodes;
|
||||
$scope.nodes.sort(nodeCompare);
|
||||
|
||||
$scope.repos = repoMap($scope.config.Repositories);
|
||||
Object.keys($scope.repos).forEach(function (repo) {
|
||||
refreshRepo(repo);
|
||||
$scope.repos[repo].Nodes.forEach(function (nodeCfg) {
|
||||
refreshCompletion(nodeCfg.NodeID, repo);
|
||||
});
|
||||
});
|
||||
|
||||
if (!hasConfig) {
|
||||
$scope.$emit('ConfigLoaded');
|
||||
}
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/config/sync').success(function (data) {
|
||||
$scope.configInSync = data.configInSync;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.init = function() {
|
||||
refreshSystem();
|
||||
refreshConfig();
|
||||
refreshConnectionStats();
|
||||
|
||||
$http.get(urlbase + '/version').success(function (data) {
|
||||
$scope.version = data;
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/report').success(function (data) {
|
||||
$scope.reportData = data;
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/upgrade').success(function (data) {
|
||||
$scope.upgradeInfo = data;
|
||||
}).error(function () {
|
||||
$scope.upgradeInfo = {};
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refresh = function () {
|
||||
refreshSystem();
|
||||
refreshConnectionStats();
|
||||
refreshErrors();
|
||||
};
|
||||
|
||||
$scope.repoStatus = function (repo) {
|
||||
@@ -187,9 +354,8 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
};
|
||||
|
||||
$scope.nodeIcon = function (nodeCfg) {
|
||||
var conn = $scope.connections[nodeCfg.NodeID];
|
||||
if (conn) {
|
||||
if (conn.Completion === 100) {
|
||||
if ($scope.connections[nodeCfg.NodeID]) {
|
||||
if ($scope.completion[nodeCfg.NodeID] && $scope.completion[nodeCfg.NodeID]._total === 100) {
|
||||
return 'ok';
|
||||
} else {
|
||||
return 'refresh';
|
||||
@@ -200,9 +366,8 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
};
|
||||
|
||||
$scope.nodeClass = function (nodeCfg) {
|
||||
var conn = $scope.connections[nodeCfg.NodeID];
|
||||
if (conn) {
|
||||
if (conn.Completion === 100) {
|
||||
if ($scope.connections[nodeCfg.NodeID]) {
|
||||
if ($scope.completion[nodeCfg.NodeID] && $scope.completion[nodeCfg.NodeID]._total === 100) {
|
||||
return 'success';
|
||||
} else {
|
||||
return 'primary';
|
||||
@@ -552,60 +717,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
cfg.APIKey = randomString(30, 32);
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
$http.get(urlbase + '/version').success(function (data) {
|
||||
$scope.version = data;
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/system').success(function (data) {
|
||||
$scope.system = data;
|
||||
$scope.myID = data.myID;
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/config').success(function (data) {
|
||||
$scope.config = data;
|
||||
$scope.config.Options.ListenStr = $scope.config.Options.ListenAddress.join(', ');
|
||||
|
||||
$scope.nodes = $scope.config.Nodes;
|
||||
$scope.nodes.sort(nodeCompare);
|
||||
|
||||
$scope.repos = repoMap($scope.config.Repositories);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
if ($scope.config.Options.URAccepted == 0) {
|
||||
// If usage reporting has been neither accepted nor declined,
|
||||
// we want to ask the user to make a choice. But we don't want
|
||||
// to bug them during initial setup, so we set a cookie with
|
||||
// the time of the first visit. When that cookie is present
|
||||
// and the time is more than four hours ago, we ask the
|
||||
// question.
|
||||
|
||||
var firstVisit = document.cookie.replace(/(?:(?:^|.*;\s*)firstVisit\s*\=\s*([^;]*).*$)|^.*$/, "$1");
|
||||
if (!firstVisit) {
|
||||
document.cookie = "firstVisit=" + Date.now() + ";max-age=" + 30*24*3600;
|
||||
} else {
|
||||
if (+firstVisit < Date.now() - 4*3600*1000){
|
||||
$('#ur').modal({backdrop: 'static', keyboard: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/config/sync').success(function (data) {
|
||||
$scope.configInSync = data.configInSync;
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/report').success(function (data) {
|
||||
$scope.reportData = data;
|
||||
});
|
||||
|
||||
$http.get(urlbase + '/upgrade').success(function (data) {
|
||||
$scope.upgradeInfo = data;
|
||||
}).error(function () {
|
||||
$scope.upgradeInfo = {};
|
||||
});
|
||||
};
|
||||
|
||||
$scope.acceptUR = function () {
|
||||
$scope.config.Options.URAccepted = 1000; // Larger than the largest existing report version
|
||||
@@ -717,6 +829,47 @@ function randomString(len, bits)
|
||||
return outStr.toLowerCase();
|
||||
}
|
||||
|
||||
function isEmptyObject(obj) {
|
||||
var name;
|
||||
for (name in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout, args, context, timestamp, result;
|
||||
|
||||
var later = function() {
|
||||
var last = Date.now() - timestamp;
|
||||
if (last < wait) {
|
||||
timeout = setTimeout(later, wait - last);
|
||||
} else {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function() {
|
||||
context = this;
|
||||
args = arguments;
|
||||
timestamp = Date.now();
|
||||
var callNow = immediate && !timeout;
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(later, wait);
|
||||
}
|
||||
if (callNow) {
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
syncthing.filter('natural', function () {
|
||||
return function (input, valid) {
|
||||
return input.toFixed(decimals(input, valid));
|
||||
|
||||
+4
-3
@@ -89,6 +89,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div ng-controller="EventCtrl"></div>
|
||||
|
||||
<!-- Top bar -->
|
||||
|
||||
@@ -289,10 +290,10 @@
|
||||
<span class="glyphicon glyphicon-retweet"></span>
|
||||
{{nodeName(nodeCfg)}}
|
||||
<span class="pull-right hidden-xs">
|
||||
<span ng-if="connections[nodeCfg.NodeID].Completion == 100">
|
||||
<span ng-if="connections[nodeCfg.NodeID] && completion[nodeCfg.NodeID]._total == 100">
|
||||
<span translate>Up to Date</span> (100%)
|
||||
</span>
|
||||
<span ng-if="connections[nodeCfg.NodeID].Completion < 100">
|
||||
<span ng-if="connections[nodeCfg.NodeID] && completion[nodeCfg.NodeID]._total < 100">
|
||||
<span translate>Syncing</span> ({{connections[nodeCfg.NodeID].Completion}}%)
|
||||
</span>
|
||||
<span translate ng-if="!connections[nodeCfg.NodeID]">Disconnected</span>
|
||||
@@ -311,7 +312,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="glyphicon glyphicon-comment"></span> <span translate>Synchronization</span></th>
|
||||
<td class="text-right">{{connections[nodeCfg.NodeID].Completion | alwaysNumber}}%</td>
|
||||
<td class="text-right">{{completion[nodeCfg.NodeID]._total | alwaysNumber}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="glyphicon glyphicon-compressed"></span> <span translate>Use Compression</span></th>
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@
|
||||
"Upgrade To {%version%}": "Atualizar para {{version}}",
|
||||
"Upload Rate": "Taxa de envio",
|
||||
"Usage": "Utilização",
|
||||
"Use Compression": "Use Compression",
|
||||
"Use Compression": "Usar Compressão",
|
||||
"Use HTTPS for GUI": "Utilizar HTTPS para GUI",
|
||||
"Version": "Versão",
|
||||
"When adding a new node, keep in mind that this node must be added on the other side too.": "Quando adicionar um novo nó, lembre-se que este nó tem que ser adicionado do outro lado também.",
|
||||
|
||||
+33
-38
@@ -157,7 +157,6 @@ type ConnectionInfo struct {
|
||||
protocol.Statistics
|
||||
Address string
|
||||
ClientVersion string
|
||||
Completion int
|
||||
}
|
||||
|
||||
// ConnectionStats returns a map with connection statistics for each connected node.
|
||||
@@ -179,43 +178,6 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
|
||||
ci.Address = nc.RemoteAddr().String()
|
||||
}
|
||||
|
||||
var tot int64
|
||||
var have int64
|
||||
|
||||
for _, repo := range m.nodeRepos[node] {
|
||||
m.repoFiles[repo].WithGlobal(func(f protocol.FileInfo) bool {
|
||||
if !protocol.IsDeleted(f.Flags) {
|
||||
var size int64
|
||||
if protocol.IsDirectory(f.Flags) {
|
||||
size = zeroEntrySize
|
||||
} else {
|
||||
size = f.Size()
|
||||
}
|
||||
tot += size
|
||||
have += size
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
m.repoFiles[repo].WithNeed(node, func(f protocol.FileInfo) bool {
|
||||
if !protocol.IsDeleted(f.Flags) {
|
||||
var size int64
|
||||
if protocol.IsDirectory(f.Flags) {
|
||||
size = zeroEntrySize
|
||||
} else {
|
||||
size = f.Size()
|
||||
}
|
||||
have -= size
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
ci.Completion = 100
|
||||
if tot != 0 {
|
||||
ci.Completion = int(100 * have / tot)
|
||||
}
|
||||
|
||||
res[node.String()] = ci
|
||||
}
|
||||
|
||||
@@ -234,6 +196,39 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
|
||||
return res
|
||||
}
|
||||
|
||||
// Returns the completion status, in percent, for the given node and repo.
|
||||
func (m *Model) Completion(node protocol.NodeID, repo string) float64 {
|
||||
var tot int64
|
||||
m.repoFiles[repo].WithGlobal(func(f protocol.FileInfo) bool {
|
||||
if !protocol.IsDeleted(f.Flags) {
|
||||
var size int64
|
||||
if protocol.IsDirectory(f.Flags) {
|
||||
size = zeroEntrySize
|
||||
} else {
|
||||
size = f.Size()
|
||||
}
|
||||
tot += size
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
var need int64
|
||||
m.repoFiles[repo].WithNeed(node, func(f protocol.FileInfo) bool {
|
||||
if !protocol.IsDeleted(f.Flags) {
|
||||
var size int64
|
||||
if protocol.IsDirectory(f.Flags) {
|
||||
size = zeroEntrySize
|
||||
} else {
|
||||
size = f.Size()
|
||||
}
|
||||
need += size
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return 100 * (1 - float64(need)/float64(tot))
|
||||
}
|
||||
|
||||
func sizeOf(fs []protocol.FileInfo) (files, deleted int, bytes int64) {
|
||||
for _, f := range fs {
|
||||
fs, de, by := sizeOfFile(f)
|
||||
|
||||
Reference in new issue
Block a user