Archived
Add GUI validations for node and repo editors (fixes #153)
This commit is contained in:
1 parent
8b4282fe28
commit
dc2f83e522
3 files changed
+108
-63
No files matched your search
+2
-2
@@ -18,7 +18,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["angular.min.js"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffd41b6b6fdbb6f67b7f05e77593dc3a723a5c0c1775d3616bbb21b7db5a34cbbe04fe205bb4cd45267d29ba8991f9bfdfc3872452a464e5d1dbce401b9b3c3c6f9e07458d9ffc55e4840a34e3ecaac0fc39127c8b4768cea820748bcbdf9b7c5bc87ffa377a327e347eb2ccd92ccdd1e3e76891e60500a574b9cd536e7e4ba047d116be148293b988268f1e7d4c392a76742e56842ed149b92259b36c9be338aae6a211ba980e276ac196e7b314d09ca088e342e1a9e012c92867798e791c9d95a3af04cf01c3620bbf09a3287e5cccd906387cbc12623344378f107c24ee0dc71f5fa742223f9e54a34b2cdebd8521296b3d2a89a75c68ce9588c0899cd4d8252b142b820500dcec278dc90559fae3ebdde96b2959e48c52966189e4621a40724aa5a00e7b661e73ce78605d81317d23e77c4aa0799cfb6c71bc612522353e1ea33330235d166886178c6334632c2f50ced8258c0881b94b5108050c28d4f80dc99ea3e85752084c01115807249c83af28aba1f79c09366739d210e8c72c037517b80040b1db801f46025f0bf865aca01d713fb290ff965e9f619abd9d6d0a0bfdbbad583269b30fd2ccbf923511287efbd3a618d6a8e9763dc3bc13f9074097d2530a727e4cf3338b809e41e5148a6f8dd9384e18b999bc3bfef7294f617fe41ff07fb70064eb063486403fb092664a4535487ffc80e4d50a36326e285e22ff99e418e959adffde9ab708fca2e2cc8f94bea1e92cc79945434f219863b0d7718d573a6727d7bfb2790b4e357317946772fc271d476dff96c3a81eb7d139ebcfdfd3f73e3b7a04c9c9f0e2a91b85965b7216de7c664fd9ea3b3fbdcf863b770595d87edc8a1586d4314f55e03d77443e88f07d5a14578c67dd482d288378538f8490971aaaf201c4f7b3ed7c8e7186b3b84c07f2431628fe4a457f7bd4d22ea144c4c3893b15475f532c80814b1564a3a18caa691e472b92e1a801ede716f9d93b2cd499e6966cd40bbb9908a5329795bdafb29f53d8cdbebeda99e5586c396d1332ace6365ddeccd2f965c6d906cc0df4c019c0d89778376329cf4cc5b16f51752f0165ca7dc5d69b94e3381da159534ce96a6c81d2e4f7748dd157279049b734c30b4261bba26fbf450660160468ca29311a542fcc92a10350eb0f1d3d6b1a4f0d9be52fcdf23635031848064586a2a3be86ede4d0a971d4d4349a97159a49508fb272e8d2639abc261c121ae33bc552f5eb6e5cd5c85edac81cdeaa9a6601beba920e5115863651551c26e0347159713e45d1b8d8417c5c831f16326214455c2fce529136b97643cb24b47935466043aeb7841beafa2d0e736790979bd05e577fb7abb704aab437e97c65e193e3de8e0b4aadcac21f24fc49040398ce61e0fcc3a9b42ca3108c15ae047ca9af622cf614f20b8360ea29a229942360905babf2ee6d2859d1537605d465fd0f15f7553c1c39102283c958c21c557dc2108dd1b3e3e36317926426c3941fabad80f5412f2014caa5baed68ccb2ada8a69d79302a8a498608456d5a56494c4e26abb4787745a1b6de602e76b06c1882979fb2df9b78b37b6f44c0660b6391442f4836ad84fb2d15ab649d5ec7c723f46ff4445b42419cd29f7602177f3001c5d651a085f2a0a4ea4536f4397428578a6b270d156f1fda0e582bf13d82aa64be42316e53ada794e3be2204007d6b38eef4f4a441cec7e03a98bd400f35f661c839dd46b719c50e6d56dda4f6dea7cdd6364c6e3f79d46c5fa1ee16dbc289f6cdf867e5752f34415cea4ee1260545e7f412f6388dec34e550f051836da093835dacaa8456cc67826d36403988591da5081d632219a30364d47cbdba04577f2f8ea78960e74080bf02d340ae79aa27c02eb342f0f8d970e2ca61969fa0816cd9a1de1ba0bfff46f5e86996e34153163d0d6e3640f1a0e6521ee0bcc77c0e99245d626398a768f0cd701094d6a8c412a899dba1eac8a10df8f4f6965dcc11a10bf6092cae7067b25de60f6675c774110123759337db326a2b286b5c8576836e741b4ed629df05d1b5a9d4b5adeb2b9fdcc090dedb440f285b1f82aa44a1f01ff7c3ea18743317201540409a0aba8edc6f9ac4380460f13069ea56a5c13926790c54c2c152763e8160a91aa285d3d4496665f007b840d2340b4c7730753d5002869a20399ec8aa32c78aacd4216822944c4b6f39a5482a246a642a84e5a173fb3213b4502cb74d932c24a66f864d8c1d81287a4d0a237b15a0036a3d9d4b91fe094a6597b7d5a769a76ea3b435949845bbb6fc00fec5aacb0f92bd74e607c3833ab3c3a2af32796cf77fd698614c29cd9c1a76c5f61f3aec6da9fcb389d008029d79aa5d943ff1413348665c4e95a359cf815ad834101f312f007788bf4fa99e9c40defdb39d760f3bab73aadb68479e868579b2413ab871452e2b5ae804bf0fa7409c11619d9ab79c0fc5d1d7e573adbee792ea147a18ac6ad28ff830c9e073bfc679a60394bcdb28ab27fa5cdf6c50c7317ca833c1936293131147232959bab19ab26b60a8d4eb75222082c5c349a0cfdbb0c23b950152a08eff9cbdfb3d29d4d343b2d8c50e27c311ba59e13403077b8e6ea2578c023fe2e80f28e2225069ba01b6f4b9fff8af82d168ef100e5964200fbc076d7da13aab6ed5b7732eee9ed0b71daf172b76151d5686591af98775ad0f75033e2afdf9d04e2ad16e39074d9a158f1328b131cde29bfda8dc1d3e2b920408f7e61abc22a80307ec0ce70b7d42d6086a4e4cf3e5ad194baaa74ee081eaac4c63aa8693bf18a1e094286a58bd54c69df6a1abd734cb3cb51ed4e78dcd3990cc76345d03cd7d0f9d86b7afabd426ccc38a0cad0316b85bea1045ef59923a6a0c0b7af861505303e5a507fb67b220b9c0f699785bb6a2a5ff7d553ba0ed6af6330b45bf6d272ac8261fd651883a81951997a8233af8f3c23d77cf315d8a154c3c7dda7298a5e02ec8b449ca1def21fb3de46feac0b7c781acf339827ec38f650eedf4627dc8af82ca08658ce2112293db0ada6b27182a28a47b0faa0a95cd42051c204fe7381ea331301c0139936f3d0c55fc0985cd3be6f38a88d494af08fde8c1737abd4f3b9cde3a3951b0a5839b02d85541c763a372712db17fba6d3877d356f999719c5ef668bdf40314c0d4b27db5c49b6db18afd54da12d7928271115b0f99ef1a7ebe847dc7c40af392cfb64acaee5d5a6379ef502e6b8983c7fe62458a83f1804001d40ce777f06ada301138e6c4737bdab3d3b384bfa0d376170dc8ac1e8cc832bea729f48314df166d1d174efe20ea9a41f32ee1416bcc739cf237e5739bcee2cabea0e8f079e172aded818ed0b3a962eb603a52ebc68a9328cce58243839be53bdf9d6113d9ac76e4fd83cea21a743b3b845da6505530fc5f258279151847cd8e5ace0d871d4d30e0696d243e40a571bb46c2aca81b89028a4879faa91407e164dfd5595848cae2c6bbLine truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffd41cef6fdbb6f27bff0aceeb26b975e57478181eeaa6c39676435ed73668d67e09f241b6689b8d4c7a1495c4c8fcbfbfe30fc9a448c94ad26e9d812d3679bc3bde1def97a88e1f7d2a7242059a72765560fe0c095ee2119a312a082d71f57b9d9785fc4fff468fc60fc68f16399ba6397af80ccdd3bc00a0942eca3ce5e6b7047a1095f0a5109ccc443479f0e032e5a8d8d0995812ba4087d58a64c5b232c77154cf452374763e9ca80525cfa729a0394411c785c253c3259251cef21cf3383aad468f04cf01c3bc84df8451143f2c666c0d1c3e5c0ab11ea29b07083e12f79ae3cb97a990c80f26f5e8028b77af6148ee75372a89a75c68ced516811339a9b14b562856040b00b8d94e1a9373b2f0c7579be397726791334a59862592b3f30092632a37eab067e631e78c07d61518d35772cea70492c7b9cf16c76b56ed428d8fc7e814d44817059ae239e3184d19cb0b94337601234260ee52144201032f6afc8664cf50f43b2904a68008b4033b9c81ad28ada113ce049bb11c6908f4739681b80b5c00a0d8acc10e2381af05fc325ad086b81d59c8dfa4d7a79866afa7ebc242ffae140b2675f65eaaf977b22202c5af7f5917c31d6a5aaea69877227f0fe8527a4c619f97697e6a11d033a89a42f1ad311bc30923379377c77f92f214ce47fe1eff5902902d1b901802f9c04a9a2911ed40fae30724474b38c8b8217889fc579263a467b5fc7b4bde22f09bf2333f53fa8aa6d31c67160d3d85608ec159c73bbcd2383bb9fe9dcd5a70aa99bba03c95e3bf683f6adbb71c46bb711b9db3fec3093df1d9d123484e86179fbb5e685192d3f0e13367ca16df87e3fb1cb80fee4625b69f4bb1c4103a66a972bc1f9c2def45789216c515e35937520bca205eef4642c82b09d5f100fcfb69399b619ce12caec281fc90398abf51dedf1eb5a44b2811f170e24ec5d1b7140b60e04239d96828bd6a9ac7d19264386a40fbb1457eb60e0bbb48734b36760bbb990885329795ad2fb25f5338cdbebcda99e558949cb66d322ce63659de4cd3d945c6d91ad40df4c01840d91778336529cf4cc6b16d1175d706eb7837877d2c25709d34d8bca9c421018471958d3c46d1b8d8c0d959018f85b4a6a288778bb354a4cdcdb96637092956630436e47a8be5a18eed71983b83bc5290bd6ef7fdddf413c491046456c476941f2610ce5fa5b3a5859c649e62820250d9c34f12cd61040398ce60e0c3fbe323b65a330a675662ea2b1e4b0c0aef19c9ce3d493477657f0ff368a565bd3525d33dcaae80ba4c0e211dbb8a872307426430194b98277512394463f4f4e0e0c0852499713fd5c7ca39617dd00c088558bacb491bb3ac14f5b4330f8a94ba4384a236012b0f272793655abcbba29078ad31171ba5a800bcfc54c5c0c49bdd7a23826f5ab048a252a5f5e6dea46299acd2ebf86084fe8b1e694d288863facb46e0e20f2620123f09e4d71e9414bdc8863e870ee55a70eda4211dea43db016b25be4510b2664b14e336d17a4239e8bb8500a0af0dc79c1e1f36c8f9185c03b317e8a1c6390c19a75b0535ddd8bec3aa2b98dee7b459f784c96d270f9ab50d2465a22c1c772f879bb14de6176cee7a2509077ee9104aa99266784e28e46be1d0072912bd80334e233bfa38147cd4a01b48f3e1147f2349b4623e156cbd06ca41ccaace16dac744d23307c8a8f9ddea0a5cfd3d3b384f04fb0004f811a80682cd633d017a9942391f3f1d4edc7d98e5876820eb39480606e8afbfd06ef438cbf1a0b9173d0d663640f160c7a5acee4f309f41fc4817d828e6311a7c371c04776b44626da819dcd7ec28871cf1cbeb5ba6b84f089db32fa071853b93b514ff6c5a7754171150523779732ca3b6a46e87abd066d08d6ecdc92ae59b20ba3691baba756de58b2b18c27bdbd603c2d61d32152814fe837e581d85aea1f63f941010a682a623cf9b26310e01583c4c9ab255617086491e0395b0b3949da880b394c3477327e397cc4ae70f7081a06916246fe1eff1cb73d70225605334d5782273c91c2bb2528620895030adace598222990a811a910961dc9f665c669a1581e9b2659084cdf0d9b183b1c51f4921466efb5830e88f57826b7f46f102abbb8ad3c4d3d751ba1ad20c52cdaa5e53bf0af565cbe93ec2533df19ee9599ed167d91c99ecedf2c31c398129a692975f9f69f3af46d89fc1fdb42c30974c6a9f6ad7cc47bd5209971395586663d246861d3405c625e00ee107f5f523c3981b8fbb19d760f3dbf4d57f856d201f8169e6c900e6edc2d57192d54823f864320ce88b05aaa2d0da238fab67ae8d1b769a55a94c36056935ee2fd24830f851acd2e072879b7565a4f74d3d71c50c7307ca853c193629d13114723b9b3746d1565d7c05025d7eb4480078b8793409db76685d7950152208eff9dbe7b9b14ead112996f628793e108dd2c719a81813d4337d111a3c08f78f20724711188345d035bba293cfe54301a6d1dc2218d0c643774d05617aa4666abbc9da6a9dbbe6debbd164b7615ed178659ea40ee79e217b05169cffb4e5285b6e41c2469563c4c20c5c6348b6fb6a3ea74f8ac4812b0b957d7601541193860a7389feb0e59c3a9393ecddfef8eb1a47e240116a87a651a533d9c7c62848251a280dc24f02be084f1e42118c109574ce3b8611f95d8ee74625d0da459e62960afe46fec3d02c96c43d315d0dcf6907ef8a0bbe20fc3fcfdc28172040bdc2d9f1045efe1856a5f8645b2ffe943480e96f7533f9339c905b61bed6d11905636fdcdcea86df3d5b32dcd2fc7dfbe0df061b5575457574671ddd9d54dfb70234ccda97e5d036763a2c72eefb1d3e66e7dc9ef8959ff44c86858ac8cc09df6aa1f112897344219a37884c8e4b61bed65f3860a0ac9de83aa1d6d33cd010bc8d3198ec7680c0c4740ce446b0f43ed93424ef78ed9404d444aca17847e70a1bada88a0e7ee89cc315d88254c3c7e1c2ad06cd833726ea7cfae083a9e37558b773bf67be3867337e8559f29c7e9458fc24d3f7e014c2d0758ef785d16cbd80fc42d1e2c291817715533a51cdfd5d17c0de78e8925e6159f6d79985df9b47aedde4e5b6622be9b6ef02596a4d8eb0f08a44f4dc77d07aba60d1581614e3cb3a73deb446bf367f4bcdd44037b568f556411d05315fa318caf8bb67a0d277f1028fe5e78d7d4f66a6396e394bfaa9efa74265cf6dd3787cf33976bad0ff4043d3d576ced0d476add58711285b99c73288fb37ce39b331c229bd55d84bfbdb1a8f2de8e0e619329540e0dffaf03c1ac768ca3663d2ee786c38e121af0b494506bd6c7602a38f72242a81296e1f13dccdeaeb0312b76854d0109a8ecc62a558083da76553a16922a5df26e4878f95260ad43f4ac3ab0e7fee59f3b175b526e3d3379c9d1e729733c75ecd54340fc772e70fefe2dcb54b073cffa72f09a7d8e54d0e1d94b050d152f1594ab3ca83adc9f9dLine truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["app.js"] = bs
|
||||
@@ -63,7 +63,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["favicon.png"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec5ce173dbb692ff9ebf02e1cca5c99c29d94ed27be3c89a49ecf4d5f3da241327f7eee6cdfb009190881a045800b4a2babebffd76019222294a96642971a7fd9058248005f0dbc5ee0fe09283c7e7efcf3efdef87b724b1a9183e1ae01f22a89c9c064c06444e429a65a78199c9c8265c4e02acc3683c7c44c82065969228a1da307b1ae4761cfe2d981724d66621fb35e7d7a7c1ff849f5f87672acda8e523c102122969998456176f4f593c61b57692a6ec34b8e66c9a296d6b55a73cb6c969ccae79c442777140b8e49653119a880a767ad43b5c10143313699e59ae644dd642359adb44e9851a82cb2ba2990008a0d846b9253c42498966e3d3604caff1b2973964b081e556b06185d7a0ef6f54b27cbb9152d6584db37e644cbfbaeaa55cf6e04e5074696782998431eb07e3ae899d65305ecbbe586cec4a0819a978466edc4f42321ac7d0773852d6aaf484fcd761f6e555513686f985639a72313b21c18f4c5c33cb234adeb19c0507a4ba71405e6b00f680182a4d6898e6632fe2f691fb931c1d90e418fe3d877f2fe0df4bf8f77d35847b77538898323e49ec09797e78d8e89ef6465656bd211861cc22a529eaf984482559a37e2efe337959d54fa99e70195a959d90a3de4b9636eaf69cb8544965321ab1ee3955c5ada614cc3b04938899342c1e5ab758ac1ea2b12e96a2da96978ec12e96975692e395923b4b2bc97135bb91d231d31e9305f85c67c46af8d1049d0a3e01bc2358324c37a1106aa25a889f90c3574d1baddd713d872fe7a69a29c3bd3a61358062af5b58675a4d3433a6ea24298ce5f8682ea45075b914b84cc0c6ec1241d5af7044752516d62d0bbb643b8330fc370656745cde2e85464a089a01dae13537e8f42a79313799a0b326cccd366b35e71247d6029d1b1b4a158e722198ad4d016e3bf711a2fbf05db71a827d883c95cbfa094742455715ac80a973c1e05dcae55381ad3d5447f382118dae265ae532c65e9406954e464f8f9f7f7f408e5f1ce27f47cf5e350d51d398e706567ecd204a9b011d90176d1d836ee0764d0fad79c16a95bd9882c76fb901c1c630dac3b91f684cefe8707ebb6ef26e92afeec6aa0418fc712414bdea3603f298a718eea8b4f3e10ffa4e651072fb3ee63e1a383f8fa2c0f4349808d3a7c165196cceac1610c10435e63428fb2be2d2e330249f5446d0b0c3d0df93f4baaa4dafb1c4ff412750fe8cd998e602c230f4c65c3d3e714eb6083c83985742705014a6af8b320c59007ab38f70a4a98c83e180a793b2045d45408c8e30e68578151e1dffcd4555e2a37ef0fc3828d6b7fb3d2497f3108b9d543d8e7258e9b28892fea2c2046306fc2b27e57e9b9464b058bcd196b38602477e22c1a32ba04231b797cc5ae8cd3c7d0663af4f6b226659822c8054bf403dc0078a8191b2e5a0ef875300d707e45c58ef43a75e214bb0f4f551833f706d2ca8627a4094143302a4642a091f13c922705b54cf5e918fcc58aa2d99522da1db52db857898140702f218e48ff9e442228c153e2078aebac6604498c6a093aab4590ed36482b8ffc3a2db5acd8eba21dab3ab35489e374b1c5f0a86e52cde3116b31816c0f36185d872b1b83c1a3d43a56cf82961c4cf37f7048124d4901163e014e835785bd00bac424b680451865a16f7e6e645d21c212f86635555098322e03e6d8aee0dfa59638c770f1a83716dcd6c60c760bb75739edb71dd788ba1af65b7404ea17a32b7dd4a0d6ada34dfaee94482513de65f820e55356f342e6b17c5cf058367935c807b02fb6c9973c3704b79d8f02373f441e9990b7ee429fa34f4f4cfe6123a8dfcfb356cbc748af7b6f1628c9c99fb58782e2a478a713e972e6cc4ce0834cb18b568059982e844f0af6909707b93f6ad96f36e72e260787383927ae75cb30851bebd6dfae1b698c2efa42a66e25faee9c5f9bf7b5c5e43388d2bc31674c470ff39429061178acbe2e6a6bbd18a1e5b80547c6861de4be7de06bfa2110171ca3b0d6a067671de2978098639f817d2b5f82c9d078d75c42197a9147171be1c0fd7b26d5a5bcc1377b5895692ffe6bd28b8069b2f1ad336738f549ac21e62f3f97b47e79d008cf40c7f3e2d107956a273e9065addde12a941bfdb52b634a0bf0b35a2c22f488fef98c3767f27704e4036dbca985a6b6de206f9030e8cfc4ea898d2997997a723a66f6ffd780fc892466f66d6351a7149d13fbcd98b79fea4a27d8198a87427180a1ce28610ba36bb4170c766fb59cedd0000b73bb861b792c7610c8416b62df12e8097c01d37c41d9becd77077e32db78914035a9c3eb6b736e8e29d735c8722664c465ccc19e25b1030e8d395c3d9d03607fd5c2c3094859aed5a7b61dab5ad6137e9ae6349e3d841b9168c02236709e2eb38ae31d67d33ed3a4f7e07f65f63c86eaa0f8822e3f036e5c64dd49297c30f8c69524a7ab9057996d0f66c3c41feac60d7a79d2c54f30e6834ca7e4753f6b4e864053bf92abcd6190458321e83eec459e1638fadbc3902020b43af818c93b12d8570337e80dcd61f377a1c3caf9d0351e25330db7b22b4638a705ec470a2a9650f851b8018095b55d0adf95701570f35ef7794a30c237ecaace6d1ed2d5cedc5d23e67fb8025cff6028acaed6e50d9b175b9e57acdb4991f42df0fc56df7dd08d87fb3bdb9a707481411fa6aba7f3caeb8400c3e25dc3862707f5e6041948367dface0094b4df6eadb7083b35c6b8864e4e3eb9f496eb92882e56e5661b2d52234336359da833ff51d1b8e702f1ebc44e0ecc3e73a02e4e9d121318baadf0a086aaecc7db088b2fc03d3f848beb5ef854b097441537172747bfb1f388987e0d01b7401b721968add40b903daf00da8c11e20b80745f8c634a0786e509836ccf5b5942a97117bff0ff2f894e412369a5cb2da23846507adb468480cd3d70ba700dba18b2e36515a6ec9ed4d1ee163db60d52c83e17b9753b05507c53394eaa9ef920ec6e3bb7af88bbd7c73f6b2a2936f71d255929d2d4eba3ce3fa0a675cfeb73fe8ea371f1ee3adb75a2b6dba13241896fd044ceae9b39e607262133224870f3153e29db23c62f7ca90a8735a9838f2d9dafc51c72915028201dced7de2298370008b8d9d043f9ea4e90978b0dbdb13d0b0ab456e6ec69a33198b993f27c3460e6a4762f79f1db191253b2bf38680337dff8faf9be6d0b0d02ae7a7cad0c2db6f5ceae27a895b652648710983647191fb48121ec74c865fcc7a995b592b6d0b7d35f28162e7dd50656df381432b5ad47efaa4941a30e0e2c04f377b70078585efc6e4f1933e806322956bc37a5546754f32db0f86977986f972a44f7e503a4fbd5fae3bcea55d944098b4de9981de26dc26f9a817a9b41f519126fdaad3be66a074834fba7e02bb37b005f23736e87783dea6fc8a07c37315e57870e87618fb9e203726c7e9bdc931516daf7d81f240a740c4ce5c1c68Line truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec5ceb73db3892ff3e7f05c2bacba3cea4ec3ce6b61c5955193bb3e3da99241527b777b5b51f2012123106090e00dad17a7c7ffb76037c8b9425594abcb5f3c11649000da0d18f5f830d8e1f9dbd3ffdf47f1fde92d82462f2dd187f88a0e9fcc463a947d2b94fb3ecc4d38b3434314fe71ed661349a7c47c83861869230a64a3373e2e566e6ffc9ab0b6263329ffd96f3ab13ef7ffdcf6ffc539964d4f0a9601e09656a580aadcedf9eb068ce1aed529ab013ef8ab3eb4c2ad3a87acd23139f44ec8a87ccb7370784a7dc702a7c1d52c14e8e82c3254211d3a1e299e1326dd05aaa4673134bb55443f0f49228268005506cc2dc101e22a558b1d98937a357781b649633d8c07023d8a4e2d778e41e54b45cbba994461b45b351a8f5a8ba0b129e06f0c42bba340bc174cc987183b1f7c42c3218af615f0c36b625844c65b42037f692908c4611f4ed4fa531323926ff7d987d795d94cd607efe8c265c2c8e89f7131357ccf09092772c67de01a91e1c90370a187b40344db5af99e23347e2f63bfb131f1d90f839fcbd80bf97f0f70afebeaf8670ef6e0a12d78ccf63734c5e1c1eb6baa7c1d4a4556fc80c3f62a15414d7f998a43265adfab9f8aff855553fa16ace53dfc8ec981c05af58d2aa1b5872894ca5ce68c8fae75415779a52106f1f442262a966d1c45865316a82c2ba5c8acb365c3a03b9182ead28472b29f7965694a36a7653a922a61c4f96d8673b2346c1459be954f039f03b049561aacd0a21e7b2c3f16372f8ba2da38d27b667ff552daa99d4dc2d2768032cec5587d7999273c5b4ae3a890b61797e54132996ba54059ec6206366805075e54fa9aac882de32bf8fb61508cdffc1408a9e978f4ba2a1148266c06dff8a6b347a15bd88eb4cd0459bcded366b35e7298eacc374ae8d9f4a7f9a0bc14c630af0d89a0f1fcd87ebbad310e443e4493ad48f3f1532bcacd80a3cb52618ac4ba93e15b39563d5515d30a5e1e55cc93c8db017a96049e7d3a7cf5f7c7f409ebf3cc47f47cf5eb70551d188e71a34bf2110a5ccc01a9097dd3586b581c78d75e8cc0bb4350d220a16bf6306049bc1680f6b3bd09aded161fdb829f27692afefe655c960b0c7a190f4b25f0cc8239ea0bba3a9a9873f1ed92503973b723ef7bbb1b5f3480a444f81883075e25d94cee6d428011e4c50ad4fbcb2bfc22f3df27df249660405dbf7ddb3945e55b5e91596b81f3402e565c4663417e086a13766ebf1b935b285e31947bc228283a2307d5594a1cb02a6b7fbf0a78aa6913719f3645e96a0a9f0885621fa3c1feffca3e77fb25e9538afefbd78ee15fa6daf27e4a276b1d849d5e334074d4f0b2fe96e2a9ea0cf80bf7252f65a2724036571425bce1a0a2cf809050f2f010a45dc5c3063a037fdf4198cbd39adb9586431a200525dc1f2001e280646ca96e3911b4ec1b81170cebaf51174ea16648097ae3eaee08f5c69034b717d40642a160440c9754af88ca42c04b345d5e235f9c8b4a1ca906baa52e8b65ced823c4c8a03007904f4677c7e9e221b2bfe00e17ae95a83117e12c19a54a5ed72982613c4fef78b6e1b357beafa28cfb6d6387ed12eb178c99b94b378c758c422508017938a63c364513d5a3d43a56cf22966c4cd37770081c4549329636014e815585b5817d0424368085e861a1605b5789124479617c331b2aa844e11f87edd261d8c47596b8c770f1a9d71436736906390dda638d772dc14de62e86bc92d8053a81ed7b25b2d83bc6e8b6fdf7442c1a89af12f5ecf52b51fb46e1b37c5e592c0b3792ec03c817c76c4b925b8253d6cf89159f820d5c23a3ff2146d1a5afa6735855e21ff7e0d192f8de2bd65bc182367fa3e129e8bca90a29fcf53eb36222b048a658c1a94824c827722f8fb33d442716851b1014af751c782b781b137b9b94172c119572c4456dfdeb68d71974c617c121931f137dbf4fcecef014fafc0a74695740b3a6518844e91d3108aa26edcdcf4375ad163872b15285a9af7e0dcbb2b5061098fd8153cf11a52767ed64b788087391819d2a78186d69e631d720868aa85383f1be6876dd995af2de689a16dac64caffe14c29d80793eb9dcc3d94490281c4e6f377d6ce590218e9295e3e2d38f2ace4ce851d68f5784b4e8d47fd92b2a500fd59c82915562b0bfece38c4fc3b61e71c68b3ad84a9a36b733bc81f7160e47742c5355de877793265eaf6d68df7800c34fa61616ca3294f29da871ff6229e3fcb705f4c8c65b2131e0a1ce2862cb46d76c3c11d8bede7b43603c0b8ddb11b42963cf22340b510bb44bb607c0a007243be6393fd0aee6eace5369e624c8b2dc86e7c8326de1ac7757062c6d2908b1a26be0502e3115d399c0d65733ccac5124259aad9adb517b8dd880ffb91779397348a2c2bd762a340cf5932f14d143560ebbee176132cbf03f96fc0643bd507849371789b02e436d7e257930f8c2952527ab505824ea1ede96c8e205a42e8a72cad1dc168a4fd8e26ec69d1c90a74f25570ad15089064dc0bdd89b1c2771f5b5973640828865a833396c6b610c2cef801625bb7e7e8f8e0706dcd88923f05b2bd2787760c11ce0a1f4e1435eca16003209342a80a6babff56b02bc0957711e534438f9f30a378787b0b777b91b4cfd93ed892677b618acccd6eb8b263e9b2ea7ac594ae77a2efc7c56de36e64d8ffb0bd99a707081491f5d574fff5b0e21230f814736d81c1fd71810152963dfb46058f59a2b3d7df061b9ce64a8127231fdffc4272c345e12c77a385f1564aa817dab024809f66c48623dc8b052f3970fae1739303e4e9d121d1cb4bbf1523a8bed4f7e14598e51f98c2f7f29db8176e53800b8a8ae3a3dbdbffc4493c0483de820b1886182a76c3ca1dc0866f000df6c0827b40846f0c038af7068568c35cdfa4a9ccd390bdff0b797442f214024d9eb2c62b84a18d565a34249aa9aba55d80edb88b2636962add12dbeb3cc477b7deaa597a93f736b160ab0e8a7728d5abdf810e66b3bb7af803bd7c73f4b2a2936fb1d355829d2d76ba1ce2fa0a7b5ceeda6d748dda6f90f1d15ba5a4d2fd59120ccbdcdbd240b0746e623221870f315de29d343c64f74a9368625a9838e2d9c6fc718d132a043803781a7ce209037700cac68ebd9f8e93e4182cd8eded31acb0ad456e6e668ab334120bb74f868d2cab2d88dd7f8ac446926ca5cc0902cef4fd5fbe6eae434b42abc49f2a4d0b1fff60f317d7cbde2ad3418a5b18248b8a044812f32862a9ff45af97be957572b7d056231e2822efd65236820f1c5ad1a271e932531a8c01130776badd83dd282c6c3766901f8f80393a94b9d22ca8d2aa8394999137b9c8334c9a2323f2a35479e2ec72d3700e7651324227cdce34f436e726cea7412893514845128faa4e478ac1a26b7cd3f533c8bd8610c83dd8a0df35a71602fdb9548b5124c31cf70d8b10ebac79bbefe972ad739cec0f39e6aeedb52fLine truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["index.html"] = bs
|
||||
|
||||
+82
-47
@@ -19,7 +19,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$scope.errors = [];
|
||||
$scope.seenError = '';
|
||||
$scope.model = {};
|
||||
$scope.repos = [];
|
||||
$scope.repos = {};
|
||||
|
||||
// Strings before bools look better
|
||||
$scope.settings = [
|
||||
@@ -65,25 +65,6 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
}
|
||||
}
|
||||
|
||||
function nodeCompare(a, b) {
|
||||
if (typeof a.Name !== 'undefined' && typeof b.Name !== 'undefined') {
|
||||
if (a.Name < b.Name)
|
||||
return -1;
|
||||
return a.Name > b.Name;
|
||||
}
|
||||
if (a.NodeID < b.NodeID) {
|
||||
return -1;
|
||||
}
|
||||
return a.NodeID > b.NodeID;
|
||||
}
|
||||
|
||||
function repoCompare(a, b) {
|
||||
if (a.Directory < b.Directory) {
|
||||
return -1;
|
||||
}
|
||||
return a.Directory > b.Directory;
|
||||
}
|
||||
|
||||
$scope.refresh = function () {
|
||||
$http.get(urlbase + '/system').success(function (data) {
|
||||
getSucceeded();
|
||||
@@ -91,9 +72,9 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
}).error(function () {
|
||||
getFailed();
|
||||
});
|
||||
$scope.repos.forEach(function (repo) {
|
||||
$http.get(urlbase + '/model?repo=' + encodeURIComponent(repo.ID)).success(function (data) {
|
||||
$scope.model[repo.ID] = data;
|
||||
Object.keys($scope.repos).forEach(function (id) {
|
||||
$http.get(urlbase + '/model?repo=' + encodeURIComponent(id)).success(function (data) {
|
||||
$scope.model[id] = data;
|
||||
});
|
||||
});
|
||||
$http.get(urlbase + '/connections').success(function (data) {
|
||||
@@ -272,6 +253,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$scope.editingExisting = true;
|
||||
$scope.editingSelf = (nodeCfg.NodeID == $scope.myID);
|
||||
$scope.currentNode.AddressesStr = nodeCfg.Addresses.join(', ');
|
||||
$scope.nodeEditor.$setPristine();
|
||||
$('#editNode').modal({backdrop: 'static', keyboard: true});
|
||||
};
|
||||
|
||||
@@ -279,6 +261,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$scope.currentNode = {AddressesStr: 'dynamic'};
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingSelf = false;
|
||||
$scope.nodeEditor.$setPristine();
|
||||
$('#editNode').modal({backdrop: 'static', keyboard: true});
|
||||
};
|
||||
|
||||
@@ -293,8 +276,8 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
});
|
||||
$scope.config.Nodes = $scope.nodes;
|
||||
|
||||
for (var i = 0; i < $scope.repos.length; i++) {
|
||||
$scope.repos[i].Nodes = $scope.repos[i].Nodes.filter(function (n) {
|
||||
for (var id in repos) {
|
||||
$scope.repos[id].Nodes = $scope.repos[id].Nodes.filter(function (n) {
|
||||
return n.NodeID !== $scope.currentNode.NodeID;
|
||||
});
|
||||
}
|
||||
@@ -367,18 +350,24 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
return str;
|
||||
};
|
||||
|
||||
$scope.repoList = function () {
|
||||
return repoList($scope.repos);
|
||||
}
|
||||
|
||||
$scope.editRepo = function (nodeCfg) {
|
||||
$scope.currentRepo = $.extend({selectedNodes: {}}, nodeCfg);
|
||||
$scope.currentRepo.Nodes.forEach(function (n) {
|
||||
$scope.currentRepo.selectedNodes[n.NodeID] = true;
|
||||
});
|
||||
$scope.editingExisting = true;
|
||||
$scope.repoEditor.$setPristine();
|
||||
$('#editRepo').modal({backdrop: 'static', keyboard: true});
|
||||
};
|
||||
|
||||
$scope.addRepo = function () {
|
||||
$scope.currentRepo = {selectedNodes: {}};
|
||||
$scope.editingExisting = false;
|
||||
$scope.repoEditor.$setPristine();
|
||||
$('#editRepo').modal({backdrop: 'static', keyboard: true});
|
||||
};
|
||||
|
||||
@@ -397,20 +386,8 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
}
|
||||
delete repoCfg.selectedNodes;
|
||||
|
||||
done = false;
|
||||
for (i = 0; i < $scope.repos.length; i++) {
|
||||
if ($scope.repos[i].ID === repoCfg.ID) {
|
||||
$scope.repos[i] = repoCfg;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
$scope.repos.push(repoCfg);
|
||||
}
|
||||
|
||||
$scope.config.Repositories = $scope.repos;
|
||||
$scope.repos[repoCfg.ID] = repoCfg;
|
||||
$scope.config.Repositories = repoList($scope.repos);
|
||||
|
||||
$http.post(urlbase + '/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||
};
|
||||
@@ -421,11 +398,8 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.repos = $scope.repos.filter(function (r) {
|
||||
return r.ID !== $scope.currentRepo.ID;
|
||||
});
|
||||
|
||||
$scope.config.Repositories = $scope.repos;
|
||||
delete $scope.repos[$scope.currentRepo.ID];
|
||||
$scope.config.Repositories = repoList($scope.repos);
|
||||
|
||||
$scope.configInSync = false;
|
||||
$http.post(urlbase + '/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||
@@ -448,8 +422,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
$scope.nodes = $scope.config.Nodes;
|
||||
$scope.nodes.sort(nodeCompare);
|
||||
|
||||
$scope.repos = $scope.config.Repositories;
|
||||
$scope.repos.sort(repoCompare);
|
||||
$scope.repos = repoMap($scope.config.Repositories);
|
||||
|
||||
$scope.refresh();
|
||||
});
|
||||
@@ -463,6 +436,42 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
setInterval($scope.refresh, 10000);
|
||||
});
|
||||
|
||||
function nodeCompare(a, b) {
|
||||
if (typeof a.Name !== 'undefined' && typeof b.Name !== 'undefined') {
|
||||
if (a.Name < b.Name)
|
||||
return -1;
|
||||
return a.Name > b.Name;
|
||||
}
|
||||
if (a.NodeID < b.NodeID) {
|
||||
return -1;
|
||||
}
|
||||
return a.NodeID > b.NodeID;
|
||||
}
|
||||
|
||||
function repoCompare(a, b) {
|
||||
if (a.Directory < b.Directory) {
|
||||
return -1;
|
||||
}
|
||||
return a.Directory > b.Directory;
|
||||
}
|
||||
|
||||
function repoMap(l) {
|
||||
var m = {};
|
||||
l.forEach(function (r) {
|
||||
m[r.ID] = r;
|
||||
});
|
||||
return m;
|
||||
}
|
||||
|
||||
function repoList(m) {
|
||||
var l = [];
|
||||
for (var id in m) {
|
||||
l.push(m[id])
|
||||
}
|
||||
l.sort(repoCompare);
|
||||
return l;
|
||||
}
|
||||
|
||||
function decimals(val, num) {
|
||||
var digits, decs;
|
||||
|
||||
@@ -540,7 +549,12 @@ syncthing.filter('alwaysNumber', function () {
|
||||
|
||||
syncthing.filter('chunkID', function () {
|
||||
return function (input) {
|
||||
return input.match(/.{1,6}/g).join(' ');
|
||||
if (input === undefined)
|
||||
return "";
|
||||
var parts = input.match(/.{1,6}/g);
|
||||
if (!parts)
|
||||
return "";
|
||||
return parts.join(' ');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -555,3 +569,24 @@ syncthing.directive('optionEditor', function () {
|
||||
template: '<input type="text" ng-model="config.Options[setting.id]"></input>',
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.directive('uniqueRepo', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
if (scope.editingExisting) {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('uniqueRepo', true);
|
||||
} else if (scope.repos[viewValue]) {
|
||||
// the repo exists already
|
||||
ctrl.$setValidity('uniqueRepo', false);
|
||||
} else {
|
||||
// the repo is unique
|
||||
ctrl.$setValidity('uniqueRepo', true);
|
||||
}
|
||||
return viewValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
+24
-14
@@ -131,7 +131,7 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h3 class="panel-title">Repositories</h3></div>
|
||||
<div class="panel-body">
|
||||
<ul class="list-unstyled" ng-repeat="repo in repos">
|
||||
<ul class="list-unstyled" ng-repeat="repo in repoList()">
|
||||
<li>
|
||||
<span class="text-monospace">{{repo.Directory}}</span>
|
||||
<span ng-if="model[repo.ID].invalid" class="label label-danger">{{model[repo.ID].invalid}}</span>
|
||||
@@ -358,12 +358,15 @@
|
||||
<h4 ng-show="editingExisting" class="modal-title">Edit Node</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form role="form">
|
||||
<div class="form-group">
|
||||
<form role="form" name="nodeEditor">
|
||||
<div class="form-group" ng-class="{'has-error': nodeEditor.nodeID.$invalid && nodeEditor.nodeID.$dirty}">
|
||||
<label for="nodeID">Node ID</label>
|
||||
<input ng-if="!editingExisting" id="nodeID" class="form-control" type="text" ng-model="currentNode.NodeID"></input>
|
||||
<input ng-if="!editingExisting" name="nodeID" id="nodeID" class="form-control" type="text" ng-model="currentNode.NodeID" required></input>
|
||||
<div ng-if="editingExisting" class="well well-sm">{{currentNode.NodeID | chunkID}}</div>
|
||||
<p class="help-block">The node ID can be found in the "Add Node" dialog on the other node. Spaces are ignored.</p>
|
||||
<p class="help-block">
|
||||
<span ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Add Node" dialog on the other node. Spaces are ignored.</span>
|
||||
<span ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
@@ -382,7 +385,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-click="saveNode()">Save</button>
|
||||
<button type="button" class="btn btn-primary" ng-click="saveNode()" ng-disabled="nodeEditor.$invalid">Save</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button ng-if="editingExisting && !editingSelf" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()">Delete</button>
|
||||
</div>
|
||||
@@ -401,16 +404,23 @@
|
||||
<h4 ng-show="editingExisting" class="modal-title">Edit Repository</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form role="form">
|
||||
<div class="form-group">
|
||||
<form role="form" name="repoEditor">
|
||||
<div class="form-group" ng-class="{'has-error': repoEditor.repoID.$invalid && repoEditor.repoID.$dirty}">
|
||||
<label for="repoID">Repository ID</label>
|
||||
<input placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID"></input>
|
||||
<p class="help-block">Short, unique identifier for the repository. Must be the same on all cluster nodes.</p>
|
||||
<input name="repoID" placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID" required unique-repo></input>
|
||||
<p class="help-block">
|
||||
<span ng-if="repoEditor.repoID.$valid || repoEditor.repoID.$pristine">Short identifier for the repository. Must be the same on all cluster nodes.</span>
|
||||
<span ng-if="repoEditor.repoID.$error.uniqueRepo">The repository ID must be unique.</span>
|
||||
<span ng-if="repoEditor.repoID.$error.required && repoEditor.repoID.$dirty">The repository ID cannot be blank.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" ng-class="{'has-error': repoEditor.repoPath.$invalid && repoEditor.repoPath.$dirty}">
|
||||
<label for="repoPath">Repository Path</label>
|
||||
<input placeholder="~/Documents" id="repoPath" class="form-control" type="text" ng-model="currentRepo.Directory"></input>
|
||||
<p class="help-block">Path to the repository on the local computer. Will be created if it does not exist.</p>
|
||||
<input name="repoPath" placeholder="~/Documents" id="repoPath" class="form-control" type="text" ng-model="currentRepo.Directory" required></input>
|
||||
<p class="help-block">
|
||||
<span ng-if="repoEditor.repoPath.$valid || repoEditor.repoPath.$pristine">Path to the repository on the local computer. Will be created if it does not exist.</span>
|
||||
<span ng-if="repoEditor.repoPath.$error.required && repoEditor.repoPath.$dirty">The repository path cannot be blank.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
@@ -435,7 +445,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-click="saveRepo()">Save</button>
|
||||
<button type="button" class="btn btn-primary" ng-click="saveRepo()" ng-disabled="repoEditor.$invalid">Save</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteRepo()">Delete</button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user