Remplement plume_front::editor using web-sys
This commit is contained in:
		
							parent
							
								
									208c515d70
								
							
						
					
					
						commit
						24ecb15119
					
				
							
								
								
									
										135
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										135
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -9,7 +9,7 @@ dependencies = [ | ||||
|  "activitystreams-derive", | ||||
|  "activitystreams-traits", | ||||
|  "activitystreams-types", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
| ] | ||||
| @ -32,7 +32,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "670ef03168e704b0cae242e7a5d8b40506772b339687e01a3496fc4afe2e8542" | ||||
| dependencies = [ | ||||
|  "failure", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
| ] | ||||
| 
 | ||||
| @ -46,7 +46,7 @@ dependencies = [ | ||||
|  "activitystreams-traits", | ||||
|  "chrono", | ||||
|  "mime 0.3.16", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
| ] | ||||
| @ -209,7 +209,7 @@ checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -353,7 +353,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" | ||||
| dependencies = [ | ||||
|  "byteorder 1.3.4", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -526,7 +526,7 @@ dependencies = [ | ||||
|  "libc", | ||||
|  "num-integer", | ||||
|  "num-traits 0.2.14", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "time", | ||||
|  "winapi 0.3.9", | ||||
| ] | ||||
| @ -573,13 +573,23 @@ dependencies = [ | ||||
|  "lazy_static", | ||||
|  "nom 5.1.2", | ||||
|  "rust-ini", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde-hjson", | ||||
|  "serde_json", | ||||
|  "toml 0.5.8", | ||||
|  "yaml-rust", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "console_error_panic_hook" | ||||
| version = "0.1.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" | ||||
| dependencies = [ | ||||
|  "cfg-if 0.1.10", | ||||
|  "wasm-bindgen", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "const-random" | ||||
| version = "0.1.13" | ||||
| @ -654,7 +664,7 @@ dependencies = [ | ||||
|  "idna 0.1.5", | ||||
|  "log 0.4.11", | ||||
|  "publicsuffix", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "time", | ||||
|  "try_from", | ||||
| @ -966,7 +976,7 @@ checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -1161,7 +1171,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
|  "synstructure", | ||||
| ] | ||||
| 
 | ||||
| @ -1370,7 +1380,7 @@ dependencies = [ | ||||
|  "proc-macro-hack 0.5.19", | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -2012,7 +2022,7 @@ dependencies = [ | ||||
|  "log 0.4.11", | ||||
|  "native-tls", | ||||
|  "nom 4.2.3", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
| ] | ||||
| @ -2079,7 +2089,7 @@ dependencies = [ | ||||
|  "lindera-dictionary", | ||||
|  "lindera-ipadic", | ||||
|  "lindera-ipadic-builder", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
| ] | ||||
| 
 | ||||
| @ -2092,7 +2102,7 @@ dependencies = [ | ||||
|  "bincode", | ||||
|  "byteorder 1.3.4", | ||||
|  "encoding", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "yada", | ||||
| ] | ||||
| 
 | ||||
| @ -2225,7 +2235,7 @@ dependencies = [ | ||||
|  "log 0.4.11", | ||||
|  "phf", | ||||
|  "phf_codegen", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "string_cache", | ||||
| @ -2312,7 +2322,7 @@ dependencies = [ | ||||
|  "migrations_internals", | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -2860,7 +2870,7 @@ checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -2871,7 +2881,7 @@ checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -2908,7 +2918,7 @@ dependencies = [ | ||||
|  "chrono", | ||||
|  "indexmap", | ||||
|  "line-wrap", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "xml-rs", | ||||
| ] | ||||
| 
 | ||||
| @ -2945,7 +2955,7 @@ dependencies = [ | ||||
|  "rsass", | ||||
|  "ructe", | ||||
|  "scheduled-thread-pool", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "shrinkwraprs 0.2.3", | ||||
|  "tracing", | ||||
| @ -2959,7 +2969,7 @@ dependencies = [ | ||||
| name = "plume-api" | ||||
| version = "0.6.1-dev" | ||||
| dependencies = [ | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| @ -2992,7 +3002,7 @@ dependencies = [ | ||||
|  "regex-syntax 0.6.21", | ||||
|  "reqwest 0.9.24", | ||||
|  "rocket", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "shrinkwraprs 0.3.0", | ||||
| @ -3005,11 +3015,14 @@ dependencies = [ | ||||
| name = "plume-front" | ||||
| version = "0.6.1-dev" | ||||
| dependencies = [ | ||||
|  "console_error_panic_hook", | ||||
|  "gettext", | ||||
|  "gettext-macros", | ||||
|  "gettext-utils", | ||||
|  "js-sys", | ||||
|  "lazy_static", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "stdweb", | ||||
|  "stdweb-internal-runtime", | ||||
| @ -3056,7 +3069,7 @@ dependencies = [ | ||||
|  "rocket", | ||||
|  "rocket_i18n", | ||||
|  "scheduled-thread-pool", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "shrinkwraprs 0.2.3", | ||||
| @ -3532,7 +3545,7 @@ dependencies = [ | ||||
|  "mime 0.3.16", | ||||
|  "mime_guess 2.0.3", | ||||
|  "native-tls", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "serde_urlencoded 0.5.5", | ||||
|  "socks", | ||||
| @ -3571,7 +3584,7 @@ dependencies = [ | ||||
|  "native-tls", | ||||
|  "percent-encoding 2.1.0", | ||||
|  "pin-project-lite 0.2.0", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_urlencoded 0.7.0", | ||||
|  "tokio 0.2.24", | ||||
|  "tokio-tls", | ||||
| @ -3671,7 +3684,7 @@ dependencies = [ | ||||
|  "log 0.4.11", | ||||
|  "notify", | ||||
|  "rocket", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
| ] | ||||
| 
 | ||||
| @ -3683,7 +3696,7 @@ dependencies = [ | ||||
|  "data-encoding", | ||||
|  "ring", | ||||
|  "rocket", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "time", | ||||
| ] | ||||
| 
 | ||||
| @ -3771,7 +3784,7 @@ version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" | ||||
| dependencies = [ | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| @ -3882,9 +3895,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.118" | ||||
| version = "1.0.123" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" | ||||
| checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| @ -3904,13 +3917,13 @@ dependencies = [ | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.118" | ||||
| version = "1.0.123" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" | ||||
| checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -3921,7 +3934,7 @@ checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "ryu", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -3941,7 +3954,7 @@ checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" | ||||
| dependencies = [ | ||||
|  "dtoa", | ||||
|  "itoa", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "url 1.7.2", | ||||
| ] | ||||
| 
 | ||||
| @ -3954,7 +3967,7 @@ dependencies = [ | ||||
|  "form_urlencoded", | ||||
|  "itoa", | ||||
|  "ryu", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -3994,7 +4007,7 @@ dependencies = [ | ||||
|  "itertools 0.8.2", | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4007,7 +4020,7 @@ dependencies = [ | ||||
|  "itertools 0.8.2", | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4129,7 +4142,7 @@ checksum = "a68c0ce28cf7400ed022e18da3c4591e14e1df02c70e93573cc59921b3923aeb" | ||||
| dependencies = [ | ||||
|  "discard", | ||||
|  "rustc_version", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "stdweb-derive", | ||||
|  "stdweb-internal-macros", | ||||
| @ -4145,7 +4158,7 @@ checksum = "0e21ebd9179de08f2300a65454268a17ea3de204627458588c84319c4def3930" | ||||
| dependencies = [ | ||||
|  "proc-macro2 0.4.30", | ||||
|  "quote 0.6.13", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "syn 0.15.44", | ||||
| ] | ||||
| @ -4159,7 +4172,7 @@ dependencies = [ | ||||
|  "base-x", | ||||
|  "proc-macro2 0.4.30", | ||||
|  "quote 0.6.13", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "sha1", | ||||
| @ -4191,7 +4204,7 @@ dependencies = [ | ||||
|  "new_debug_unreachable", | ||||
|  "phf_shared", | ||||
|  "precomputed-hash", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "string_cache_codegen", | ||||
|  "string_cache_shared", | ||||
| ] | ||||
| @ -4290,9 +4303,9 @@ dependencies = [ | ||||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "1.0.56" | ||||
| version = "1.0.60" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" | ||||
| checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
| @ -4316,7 +4329,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
|  "unicode-xid 0.2.1", | ||||
| ] | ||||
| 
 | ||||
| @ -4335,7 +4348,7 @@ dependencies = [ | ||||
|  "onig", | ||||
|  "plist", | ||||
|  "regex-syntax 0.6.21", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "walkdir", | ||||
| @ -4375,7 +4388,7 @@ dependencies = [ | ||||
|  "rayon", | ||||
|  "regex", | ||||
|  "rust-stemmers", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "smallvec 1.5.1", | ||||
|  "snap", | ||||
| @ -4480,7 +4493,7 @@ checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4647,7 +4660,7 @@ checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4795,7 +4808,7 @@ version = "0.4.10" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" | ||||
| dependencies = [ | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4804,7 +4817,7 @@ version = "0.5.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" | ||||
| dependencies = [ | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4834,7 +4847,7 @@ checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -4873,7 +4886,7 @@ version = "0.1.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" | ||||
| dependencies = [ | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "tracing-core", | ||||
| ] | ||||
| 
 | ||||
| @ -4888,7 +4901,7 @@ dependencies = [ | ||||
|  "lazy_static", | ||||
|  "matchers", | ||||
|  "regex", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "sharded-slab", | ||||
|  "smallvec 1.5.1", | ||||
| @ -5074,7 +5087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" | ||||
| dependencies = [ | ||||
|  "rand 0.7.3", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -5086,7 +5099,7 @@ dependencies = [ | ||||
|  "idna 0.1.5", | ||||
|  "lazy_static", | ||||
|  "regex", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
|  "serde_json", | ||||
|  "url 1.7.2", | ||||
| @ -5188,7 +5201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" | ||||
| dependencies = [ | ||||
|  "cfg-if 1.0.0", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_json", | ||||
|  "wasm-bindgen-macro", | ||||
| ] | ||||
| @ -5204,7 +5217,7 @@ dependencies = [ | ||||
|  "log 0.4.11", | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
|  "wasm-bindgen-shared", | ||||
| ] | ||||
| 
 | ||||
| @ -5238,7 +5251,7 @@ checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" | ||||
| dependencies = [ | ||||
|  "proc-macro2 1.0.24", | ||||
|  "quote 1.0.8", | ||||
|  "syn 1.0.56", | ||||
|  "syn 1.0.60", | ||||
|  "wasm-bindgen-backend", | ||||
|  "wasm-bindgen-shared", | ||||
| ] | ||||
| @ -5266,7 +5279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ec24b1b0700d4b466d280228ed0f62274eedeaa80206820f071fdc8ed787b664" | ||||
| dependencies = [ | ||||
|  "reqwest 0.9.24", | ||||
|  "serde 1.0.118", | ||||
|  "serde 1.0.123", | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
|  | ||||
| @ -17,19 +17,38 @@ lazy_static = "1.3" | ||||
| serde = "1.0" | ||||
| serde_json = "1.0" | ||||
| wasm-bindgen = "0.2.70" | ||||
| js-sys = "0.3.47" | ||||
| serde_derive = "1.0.123" | ||||
| console_error_panic_hook = "0.1.6" | ||||
| 
 | ||||
| [dependencies.web-sys] | ||||
| version = "0.3.47" | ||||
| features = [ | ||||
|   'console', | ||||
|   'ClipboardEvent', | ||||
|   'CssStyleDeclaration', | ||||
|   'DataTransfer', | ||||
|   'Document', | ||||
|   'DomStringMap', | ||||
|   'DomTokenList', | ||||
|   'Element', | ||||
|   'EventTarget', | ||||
|   'FocusEvent', | ||||
|   'History', | ||||
|   'HtmlAnchorElement', | ||||
|   'HtmlDocument', | ||||
|   'HtmlFormElement', | ||||
|   'HtmlInputElement', | ||||
|   'HtmlSelectElement', | ||||
|   'HtmlTextAreaElement', | ||||
|   'KeyboardEvent', | ||||
|   'Storage', | ||||
|   'Location', | ||||
|   'MouseEvent', | ||||
|   'Navigator', | ||||
|   'Node', | ||||
|   'NodeList', | ||||
|   'Text', | ||||
|   'TouchEvent', | ||||
|   'Window' | ||||
| ] | ||||
|  | ||||
| @ -1,9 +1,12 @@ | ||||
| use crate::CATALOG; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::sync::Mutex; | ||||
| use stdweb::{ | ||||
|     unstable::{TryFrom, TryInto}, | ||||
|     web::{event::*, html_element::*, *}, | ||||
| use js_sys::{encode_uri_component, Date, RegExp}; | ||||
| use serde_derive::{Deserialize, Serialize}; | ||||
| use std::{convert::TryInto, sync::Mutex}; | ||||
| use wasm_bindgen::{prelude::*, JsCast, JsValue}; | ||||
| use web_sys::{ | ||||
|     console, window, ClipboardEvent, Document, Element, Event, FocusEvent, HtmlAnchorElement, | ||||
|     HtmlDocument, HtmlElement, HtmlFormElement, HtmlInputElement, HtmlSelectElement, | ||||
|     HtmlTextAreaElement, KeyboardEvent, MouseEvent, Node, | ||||
| }; | ||||
| 
 | ||||
| macro_rules! mv { | ||||
| @ -15,32 +18,35 @@ macro_rules! mv { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn document() -> Document { | ||||
|     window().unwrap().document().unwrap() | ||||
| } | ||||
| 
 | ||||
| fn get_elt_value(id: &'static str) -> String { | ||||
|     let elt = document().get_element_by_id(id).unwrap(); | ||||
|     let inp: Result<InputElement, _> = elt.clone().try_into(); | ||||
|     let textarea: Result<TextAreaElement, _> = elt.clone().try_into(); | ||||
|     let select: Result<SelectElement, _> = elt.try_into(); | ||||
|     inp.map(|i| i.raw_value()).unwrap_or_else(|_| { | ||||
|     let inp: Option<&HtmlInputElement> = elt.dyn_ref(); | ||||
|     let textarea: Option<&HtmlTextAreaElement> = elt.dyn_ref(); | ||||
|     let select: Option<&HtmlSelectElement> = elt.dyn_ref(); | ||||
|     inp.map(|i| i.value()).unwrap_or_else(|| { | ||||
|         textarea | ||||
|             .map(|t| t.value()) | ||||
|             .unwrap_or_else(|_| select.unwrap().raw_value()) | ||||
|             .unwrap_or_else(|| select.unwrap().value()) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| fn set_value<S: AsRef<str>>(id: &'static str, val: S) { | ||||
|     let elt = document().get_element_by_id(id).unwrap(); | ||||
|     let inp: Result<InputElement, _> = elt.clone().try_into(); | ||||
|     let textarea: Result<TextAreaElement, _> = elt.clone().try_into(); | ||||
|     let select: Result<SelectElement, _> = elt.try_into(); | ||||
|     inp.map(|i| i.set_raw_value(val.as_ref())) | ||||
|         .unwrap_or_else(|_| { | ||||
|             textarea | ||||
|                 .map(|t| t.set_value(val.as_ref())) | ||||
|                 .unwrap_or_else(|_| select.unwrap().set_raw_value(val.as_ref())) | ||||
|         }) | ||||
|     let inp: Option<&HtmlInputElement> = elt.dyn_ref(); | ||||
|     let textarea: Option<&HtmlTextAreaElement> = elt.dyn_ref(); | ||||
|     let select: Option<&HtmlSelectElement> = elt.dyn_ref(); | ||||
|     inp.map(|i| i.set_value(val.as_ref())).unwrap_or_else(|| { | ||||
|         textarea | ||||
|             .map(|t| t.set_value(val.as_ref())) | ||||
|             .unwrap_or_else(|| select.unwrap().set_value(val.as_ref())) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| fn no_return(evt: KeyDownEvent) { | ||||
| fn no_return(evt: KeyboardEvent) { | ||||
|     if evt.key() == "Enter" { | ||||
|         evt.prevent_default(); | ||||
|     } | ||||
| @ -50,7 +56,6 @@ fn no_return(evt: KeyDownEvent) { | ||||
| pub enum EditorError { | ||||
|     NoneError, | ||||
|     DOMError, | ||||
|     TypeError, | ||||
| } | ||||
| 
 | ||||
| impl From<std::option::NoneError> for EditorError { | ||||
| @ -58,22 +63,7 @@ impl From<std::option::NoneError> for EditorError { | ||||
|         EditorError::NoneError | ||||
|     } | ||||
| } | ||||
| impl From<stdweb::web::error::InvalidCharacterError> for EditorError { | ||||
|     fn from(_: stdweb::web::error::InvalidCharacterError) -> Self { | ||||
|         EditorError::DOMError | ||||
|     } | ||||
| } | ||||
| impl From<stdweb::private::TODO> for EditorError { | ||||
|     fn from(_: stdweb::private::TODO) -> Self { | ||||
|         EditorError::DOMError | ||||
|     } | ||||
| } | ||||
| impl From<stdweb::private::ConversionError> for EditorError { | ||||
|     fn from(_: stdweb::private::ConversionError) -> Self { | ||||
|         EditorError::TypeError | ||||
|     } | ||||
| } | ||||
| const AUTOSAVE_DEBOUNCE_TIME: u32 = 5000; | ||||
| const AUTOSAVE_DEBOUNCE_TIME: i32 = 5000; | ||||
| #[derive(Serialize, Deserialize)] | ||||
| struct AutosaveInformation { | ||||
|     contents: String, | ||||
| @ -84,10 +74,16 @@ struct AutosaveInformation { | ||||
|     tags: String, | ||||
|     title: String, | ||||
| } | ||||
| js_serializable!(AutosaveInformation); | ||||
| fn is_basic_editor() -> bool { | ||||
|     if let Some(basic_editor) = window().local_storage().get("basic-editor") { | ||||
|         basic_editor == "true" | ||||
|     if let Some(basic_editor) = window() | ||||
|         .unwrap() | ||||
|         .local_storage() | ||||
|         .unwrap() | ||||
|         .unwrap() | ||||
|         .get("basic-editor") | ||||
|         .unwrap() | ||||
|     { | ||||
|         &basic_editor == "true" | ||||
|     } else { | ||||
|         false | ||||
|     } | ||||
| @ -96,65 +92,58 @@ fn get_title() -> String { | ||||
|     if is_basic_editor() { | ||||
|         get_elt_value("title") | ||||
|     } else { | ||||
|         let title_field = HtmlElement::try_from( | ||||
|             document() | ||||
|                 .query_selector("#plume-editor > h1") | ||||
|                 .ok() | ||||
|                 .unwrap() | ||||
|                 .unwrap(), | ||||
|         ) | ||||
|         .ok() | ||||
|         .unwrap(); | ||||
|         title_field.inner_text() | ||||
|         document() | ||||
|             .query_selector("#plume-editor > h1") | ||||
|             .unwrap() | ||||
|             .unwrap() | ||||
|             .dyn_ref::<HtmlElement>() | ||||
|             .unwrap() | ||||
|             .inner_text() | ||||
|     } | ||||
| } | ||||
| fn get_autosave_id() -> String { | ||||
|     format!( | ||||
|         "editor_contents={}", | ||||
|         window().location().unwrap().pathname().unwrap() | ||||
|         window().unwrap().location().pathname().unwrap() | ||||
|     ) | ||||
| } | ||||
| fn get_editor_contents() -> String { | ||||
|     if is_basic_editor() { | ||||
|         get_elt_value("editor-content") | ||||
|     } else { | ||||
|         let editor = | ||||
|             HtmlElement::try_from(document().query_selector("article").ok().unwrap().unwrap()) | ||||
|                 .ok() | ||||
|                 .unwrap(); | ||||
|         editor.child_nodes().iter().fold(String::new(), |md, ch| { | ||||
|         let editor = document().query_selector("article").unwrap().unwrap(); | ||||
|         let child_nodes = editor.child_nodes(); | ||||
|         let mut md = String::new(); | ||||
|         for i in 0..child_nodes.length() { | ||||
|             let ch = child_nodes.get(i).unwrap(); | ||||
|             let to_append = match ch.node_type() { | ||||
|                 NodeType::Element => { | ||||
|                     if js! { return @{&ch}.tagName; } == "DIV" { | ||||
|                         (js! { return @{&ch}.innerHTML; }) | ||||
|                             .try_into() | ||||
|                             .unwrap_or_default() | ||||
|                 Node::ELEMENT_NODE => { | ||||
|                     let elt = ch.dyn_ref::<Element>().unwrap(); | ||||
|                     if elt.tag_name() == "DIV" { | ||||
|                         elt.inner_html() | ||||
|                     } else { | ||||
|                         (js! { return @{&ch}.outerHTML; }) | ||||
|                             .try_into() | ||||
|                             .unwrap_or_default() | ||||
|                         elt.outer_html() | ||||
|                     } | ||||
|                 } | ||||
|                 NodeType::Text => ch.node_value().unwrap_or_default(), | ||||
|                 Node::TEXT_NODE => ch.node_value().unwrap_or_default(), | ||||
|                 _ => unreachable!(), | ||||
|             }; | ||||
|             format!("{}\n\n{}", md, to_append) | ||||
|         }) | ||||
|             md = format!("{}\n\n{}", md, to_append); | ||||
|         } | ||||
|         md | ||||
|     } | ||||
| } | ||||
| fn get_subtitle() -> String { | ||||
|     if is_basic_editor() { | ||||
|         get_elt_value("subtitle") | ||||
|     } else { | ||||
|         let subtitle_element = HtmlElement::try_from( | ||||
|             document() | ||||
|                 .query_selector("#plume-editor > h2") | ||||
|                 .unwrap() | ||||
|                 .unwrap(), | ||||
|         ) | ||||
|         .ok() | ||||
|         .unwrap(); | ||||
|         subtitle_element.inner_text() | ||||
|         document() | ||||
|             .query_selector("#plume-editor > h2") | ||||
|             .unwrap() | ||||
|             .unwrap() | ||||
|             .dyn_ref::<HtmlElement>() | ||||
|             .unwrap() | ||||
|             .inner_text() | ||||
|     } | ||||
| } | ||||
| fn autosave() { | ||||
| @ -169,27 +158,31 @@ fn autosave() { | ||||
|     }; | ||||
|     let id = get_autosave_id(); | ||||
|     match window() | ||||
|         .unwrap() | ||||
|         .local_storage() | ||||
|         .insert(&id, &serde_json::to_string(&info).unwrap()) | ||||
|         .unwrap() | ||||
|         .unwrap() | ||||
|         .set(&id, &serde_json::to_string(&info).unwrap()) | ||||
|     { | ||||
|         Ok(_) => {} | ||||
|         _ => console!(log, "Autosave failed D:"), | ||||
|         _ => console::log_1(&"Autosave failed D:".into()), | ||||
|     } | ||||
| } | ||||
| //This is only necessary until we go to stdweb 4.20 at least
 | ||||
| fn confirm(message: &str) -> bool { | ||||
|     let result: bool = js! {return confirm(@{message});} == true; | ||||
|     result | ||||
| } | ||||
| fn load_autosave() { | ||||
|     if let Some(autosave_str) = window().local_storage().get(&get_autosave_id()) { | ||||
|     if let Ok(Some(autosave_str)) = window() | ||||
|         .unwrap() | ||||
|         .local_storage() | ||||
|         .unwrap() | ||||
|         .unwrap() | ||||
|         .get(&get_autosave_id()) | ||||
|     { | ||||
|         let autosave_info: AutosaveInformation = serde_json::from_str(&autosave_str).ok().unwrap(); | ||||
|         let message = i18n!( | ||||
|             CATALOG, | ||||
|             "Do you want to load the local autosave last edited at {}?"; | ||||
|             Date::from_time(autosave_info.last_saved).to_date_string() | ||||
|             Date::new(&JsValue::from_f64(autosave_info.last_saved)).to_date_string().as_string().unwrap() | ||||
|         ); | ||||
|         if confirm(&message) { | ||||
|         if let Ok(true) = window().unwrap().confirm_with_message(&message) { | ||||
|             set_value("editor-content", &autosave_info.contents); | ||||
|             set_value("title", &autosave_info.title); | ||||
|             set_value("subtitle", &autosave_info.subtitle); | ||||
| @ -202,18 +195,33 @@ fn load_autosave() { | ||||
|     } | ||||
| } | ||||
| fn clear_autosave() { | ||||
|     window().local_storage().remove(&get_autosave_id()); | ||||
|     console!(log, &format!("Saved to {}", &get_autosave_id())); | ||||
|     window() | ||||
|         .unwrap() | ||||
|         .local_storage() | ||||
|         .unwrap() | ||||
|         .unwrap() | ||||
|         .remove_item(&get_autosave_id()) | ||||
|         .unwrap(); | ||||
|     console::log_1(&&format!("Saved to {}", &get_autosave_id()).into()); | ||||
| } | ||||
| type TimeoutHandle = i32; | ||||
| lazy_static! { | ||||
|     static ref AUTOSAVE_TIMEOUT: Mutex<Option<TimeoutHandle>> = Mutex::new(None); | ||||
| } | ||||
| fn autosave_debounce() { | ||||
|     let window = window().unwrap(); | ||||
|     let timeout = &mut AUTOSAVE_TIMEOUT.lock().unwrap(); | ||||
|     if let Some(timeout) = timeout.take() { | ||||
|         timeout.clear(); | ||||
|         window.clear_timeout_with_handle(timeout); | ||||
|     } | ||||
|     **timeout = Some(window().set_clearable_timeout(autosave, AUTOSAVE_DEBOUNCE_TIME)); | ||||
|     let callback = Closure::once(|| autosave()); | ||||
|     **timeout = window | ||||
|         .set_timeout_with_callback_and_timeout_and_arguments_0( | ||||
|             callback.as_ref().unchecked_ref(), | ||||
|             AUTOSAVE_DEBOUNCE_TIME, | ||||
|         ) | ||||
|         .ok(); | ||||
|     callback.forget(); | ||||
| } | ||||
| fn init_widget( | ||||
|     parent: &Element, | ||||
| @ -222,19 +230,33 @@ fn init_widget( | ||||
|     content: String, | ||||
|     disable_return: bool, | ||||
| ) -> Result<HtmlElement, EditorError> { | ||||
|     let widget = placeholder(make_editable(tag).try_into()?, &placeholder_text); | ||||
|     let widget = placeholder( | ||||
|         make_editable(tag).dyn_into::<HtmlElement>().unwrap(), | ||||
|         &placeholder_text, | ||||
|     ); | ||||
|     if !content.is_empty() { | ||||
|         widget.dataset().insert("edited", "true")?; | ||||
|         widget | ||||
|             .dataset() | ||||
|             .set("edited", "true") | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|     } | ||||
|     widget.append_child(&document().create_text_node(&content)); | ||||
|     widget | ||||
|         .append_child(&document().create_text_node(&content)) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     if disable_return { | ||||
|         widget.add_event_listener(no_return); | ||||
|         let callback = Closure::wrap(Box::new(no_return) as Box<dyn FnMut(KeyboardEvent)>); | ||||
|         widget | ||||
|             .add_event_listener_with_callback("keydown", callback.as_ref().unchecked_ref()) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         callback.forget(); | ||||
|     } | ||||
| 
 | ||||
|     parent.append_child(&widget); | ||||
|     parent | ||||
|         .append_child(&widget) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     // We need to do that to make sure the placeholder is correctly rendered
 | ||||
|     widget.focus(); | ||||
|     widget.blur(); | ||||
|     widget.focus().map_err(|_| EditorError::DOMError)?; | ||||
|     widget.blur().map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     filter_paste(&widget); | ||||
| 
 | ||||
| @ -243,48 +265,88 @@ fn init_widget( | ||||
| 
 | ||||
| fn filter_paste(elt: &HtmlElement) { | ||||
|     // Only insert text when pasting something
 | ||||
|     js! { | ||||
|         @{&elt}.addEventListener("paste", function (evt) { | ||||
|             evt.preventDefault(); | ||||
|             document.execCommand("insertText", false, evt.clipboardData.getData("text")); | ||||
|         }); | ||||
|     }; | ||||
|     let insert_text = Closure::wrap(Box::new(|evt: ClipboardEvent| { | ||||
|         evt.prevent_default(); | ||||
|         if let Some(data) = evt.clipboard_data() { | ||||
|             if let Ok(data) = data.get_data("text") { | ||||
|                 document() | ||||
|                     .dyn_ref::<HtmlDocument>() | ||||
|                     .unwrap() | ||||
|                     .exec_command_with_show_ui_and_value("insertText", false, &data) | ||||
|                     .unwrap(); | ||||
|             } | ||||
|         } | ||||
|     }) as Box<dyn FnMut(ClipboardEvent)>); | ||||
|     elt.add_event_listener_with_callback("paste", insert_text.as_ref().unchecked_ref()) | ||||
|         .unwrap(); | ||||
|     insert_text.forget(); | ||||
| } | ||||
| 
 | ||||
| pub fn init() -> Result<(), EditorError> { | ||||
|     if let Some(ed) = document().get_element_by_id("plume-fallback-editor") { | ||||
|         load_autosave(); | ||||
|         ed.add_event_listener(|_: SubmitEvent| clear_autosave()); | ||||
|         let callback = Closure::wrap(Box::new(|_| clear_autosave()) as Box<dyn FnMut(Event)>); | ||||
|         ed.add_event_listener_with_callback("submit", callback.as_ref().unchecked_ref()) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         callback.forget(); | ||||
|     } | ||||
|     // Check if the user wants to use the basic editor
 | ||||
|     if window() | ||||
|         .unwrap() | ||||
|         .local_storage() | ||||
|         .unwrap() | ||||
|         .unwrap() | ||||
|         .get("basic-editor") | ||||
|         .map(|x| x == "true") | ||||
|         .map(|x| x.is_some() && x.unwrap() == "true") | ||||
|         .unwrap_or(true) | ||||
|     { | ||||
|         if let Some(editor) = document().get_element_by_id("plume-fallback-editor") { | ||||
|             if let Ok(Some(title_label)) = document().query_selector("label[for=title]") { | ||||
|                 let editor_button = document().create_element("a")?; | ||||
|                 js! { @{&editor_button}.href = "#"; } | ||||
|                 editor_button.add_event_listener(|_: ClickEvent| { | ||||
|                     if window() | ||||
|                 let editor_button = document() | ||||
|                     .create_element("a") | ||||
|                     .map_err(|_| EditorError::DOMError)?; | ||||
|                 editor_button | ||||
|                     .dyn_ref::<HtmlAnchorElement>() | ||||
|                     .unwrap() | ||||
|                     .set_href("#"); | ||||
|                 let disable_basic_editor = Closure::wrap(Box::new(|_| { | ||||
|                     let window = window().unwrap(); | ||||
|                     if window | ||||
|                         .local_storage() | ||||
|                         .insert("basic-editor", "false") | ||||
|                         .unwrap() | ||||
|                         .unwrap() | ||||
|                         .set("basic-editor", "false") | ||||
|                         .is_err() | ||||
|                     { | ||||
|                         console!(log, "Failed to write into local storage"); | ||||
|                         console::log_1(&"Failed to write into local storage".into()); | ||||
|                     } | ||||
|                     window().history().go(0).ok(); // refresh
 | ||||
|                 }); | ||||
|                 editor_button.append_child( | ||||
|                     &document().create_text_node(&i18n!(CATALOG, "Open the rich text editor")), | ||||
|                     window.history().unwrap().go_with_delta(0).ok(); // refresh
 | ||||
|                 }) | ||||
|                     as Box<dyn FnMut(MouseEvent)>); | ||||
|                 editor_button | ||||
|                     .add_event_listener_with_callback( | ||||
|                         "click", | ||||
|                         disable_basic_editor.as_ref().unchecked_ref(), | ||||
|                     ) | ||||
|                     .map_err(|_| EditorError::DOMError)?; | ||||
|                 disable_basic_editor.forget(); | ||||
|                 editor_button | ||||
|                     .append_child( | ||||
|                         &document().create_text_node(&i18n!(CATALOG, "Open the rich text editor")), | ||||
|                     ) | ||||
|                     .map_err(|_| EditorError::DOMError)?; | ||||
|                 editor | ||||
|                     .insert_before(&editor_button, Some(&title_label)) | ||||
|                     .ok(); | ||||
|                 let callback = Closure::wrap( | ||||
|                     Box::new(|_| autosave_debounce()) as Box<dyn FnMut(KeyboardEvent)> | ||||
|                 ); | ||||
|                 editor.insert_before(&editor_button, &title_label).ok(); | ||||
|                 document() | ||||
|                     .get_element_by_id("editor-content") | ||||
|                     .unwrap() | ||||
|                     .add_event_listener(|_: KeyDownEvent| autosave_debounce()); | ||||
|                     .add_event_listener_with_callback("keydown", callback.as_ref().unchecked_ref()) | ||||
|                     .map_err(|_| EditorError::DOMError)?; | ||||
|                 callback.forget(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -297,14 +359,26 @@ pub fn init() -> Result<(), EditorError> { | ||||
| fn init_editor() -> Result<(), EditorError> { | ||||
|     if let Some(ed) = document().get_element_by_id("plume-editor") { | ||||
|         // Show the editor
 | ||||
|         js! { @{&ed}.style.display = "block"; }; | ||||
|         ed.dyn_ref::<HtmlElement>() | ||||
|             .unwrap() | ||||
|             .style() | ||||
|             .set_property("display", "block") | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         // And hide the HTML-only fallback
 | ||||
|         let old_ed = document().get_element_by_id("plume-fallback-editor")?; | ||||
|         let old_title = document().get_element_by_id("plume-editor-title")?; | ||||
|         js! { | ||||
|             @{&old_ed}.style.display = "none"; | ||||
|             @{&old_title}.style.display = "none"; | ||||
|         }; | ||||
|         old_ed | ||||
|             .dyn_ref::<HtmlElement>() | ||||
|             .unwrap() | ||||
|             .style() | ||||
|             .set_property("display", "none") | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         old_title | ||||
|             .dyn_ref::<HtmlElement>() | ||||
|             .unwrap() | ||||
|             .style() | ||||
|             .set_property("display", "none") | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|         // Get content from the old editor (when editing an article for instance)
 | ||||
|         let title_val = get_elt_value("title"); | ||||
| @ -326,35 +400,44 @@ fn init_editor() -> Result<(), EditorError> { | ||||
|             content_val.clone(), | ||||
|             false, | ||||
|         )?; | ||||
|         js! { @{&content}.innerHTML = @{content_val}; }; | ||||
|         content.set_inner_html(&content_val); | ||||
| 
 | ||||
|         // character counter
 | ||||
|         content.add_event_listener(mv!(content => move |_: KeyDownEvent| { | ||||
|             window().set_timeout(mv!(content => move || { | ||||
|         let character_counter = Closure::wrap(Box::new(mv!(content => move |_| { | ||||
|             let update_char_count = Closure::wrap(Box::new(mv!(content => move || { | ||||
|                 if let Some(e) = document().get_element_by_id("char-count") { | ||||
|                     let count = chars_left("#plume-fallback-editor", &content).unwrap_or_default(); | ||||
|                     let text = i18n!(CATALOG, "Around {} characters left"; count); | ||||
|                     HtmlElement::try_from(e).map(|e| { | ||||
|                         js!{@{e}.innerText = @{text}}; | ||||
|                     }).ok(); | ||||
|                     e.dyn_ref::<HtmlElement>().map(|e| { | ||||
|                         e.set_inner_text(&text); | ||||
|                     }).unwrap(); | ||||
|                 }; | ||||
|             }), 0); | ||||
|             })) as Box<dyn FnMut()>); | ||||
|             window().unwrap().set_timeout_with_callback_and_timeout_and_arguments(update_char_count.as_ref().unchecked_ref(), 0, &js_sys::Array::new()).unwrap(); | ||||
|             update_char_count.forget(); | ||||
|             autosave_debounce(); | ||||
|         })); | ||||
|         })) as Box<dyn FnMut(KeyboardEvent)>); | ||||
|         content | ||||
|             .add_event_listener_with_callback("keydown", character_counter.as_ref().unchecked_ref()) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         character_counter.forget(); | ||||
| 
 | ||||
|         document().get_element_by_id("publish")?.add_event_listener( | ||||
|             mv!(title, subtitle, content, old_ed => move |_: ClickEvent| { | ||||
|                 let popup = document().get_element_by_id("publish-popup").or_else(|| | ||||
|                         init_popup(&title, &subtitle, &content, &old_ed).ok() | ||||
|                     ).unwrap(); | ||||
|                 let bg = document().get_element_by_id("popup-bg").or_else(|| | ||||
|                         init_popup_bg().ok() | ||||
|                     ).unwrap(); | ||||
|         let show_popup = Closure::wrap(Box::new(mv!(title, subtitle, content, old_ed => move |_| { | ||||
|             let popup = document().get_element_by_id("publish-popup").or_else(|| | ||||
|                                                                               init_popup(&title, &subtitle, &content, &old_ed).ok() | ||||
|             ).unwrap(); | ||||
|             let bg = document().get_element_by_id("popup-bg").or_else(|| | ||||
|                                                                       init_popup_bg().ok() | ||||
|             ).unwrap(); | ||||
| 
 | ||||
|                 popup.class_list().add("show").unwrap(); | ||||
|                 bg.class_list().add("show").unwrap(); | ||||
|             }), | ||||
|         ); | ||||
|             popup.class_list().add_1("show").unwrap(); | ||||
|             bg.class_list().add_1("show").unwrap(); | ||||
|         })) as Box<dyn FnMut(MouseEvent)>); | ||||
|         document() | ||||
|             .get_element_by_id("publish")? | ||||
|             .add_event_listener_with_callback("click", show_popup.as_ref().unchecked_ref()) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         show_popup.forget(); | ||||
| 
 | ||||
|         show_errors(); | ||||
|         setup_close_button(); | ||||
| @ -364,32 +447,47 @@ fn init_editor() -> Result<(), EditorError> { | ||||
| 
 | ||||
| fn setup_close_button() { | ||||
|     if let Some(button) = document().get_element_by_id("close-editor") { | ||||
|         button.add_event_listener(|_: ClickEvent| { | ||||
|         let close_editor = Closure::wrap(Box::new(|_| { | ||||
|             window() | ||||
|                 .unwrap() | ||||
|                 .local_storage() | ||||
|                 .insert("basic-editor", "true") | ||||
|                 .unwrap() | ||||
|                 .unwrap() | ||||
|                 .set("basic-editor", "true") | ||||
|                 .unwrap(); | ||||
|             window().history().go(0).unwrap(); // Refresh the page
 | ||||
|         }); | ||||
|             window() | ||||
|                 .unwrap() | ||||
|                 .history() | ||||
|                 .unwrap() | ||||
|                 .go_with_delta(0) | ||||
|                 .unwrap(); // Refresh the page
 | ||||
|         }) as Box<dyn FnMut(MouseEvent)>); | ||||
|         button | ||||
|             .add_event_listener_with_callback("click", close_editor.as_ref().unchecked_ref()) | ||||
|             .unwrap(); | ||||
|         close_editor.forget(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn show_errors() { | ||||
|     if let Ok(Some(header)) = document().query_selector("header") { | ||||
|         let list = document().create_element("header").unwrap(); | ||||
|         list.class_list().add("messages").unwrap(); | ||||
|         for error in document().query_selector_all("p.error").unwrap() { | ||||
|     let document = document(); | ||||
|     if let Ok(Some(header)) = document.query_selector("header") { | ||||
|         let list = document.create_element("header").unwrap(); | ||||
|         list.class_list().add_1("messages").unwrap(); | ||||
|         let errors = document.query_selector_all("p.error").unwrap(); | ||||
|         for i in 0..errors.length() { | ||||
|             let error = errors.get(i).unwrap(); | ||||
|             error | ||||
|                 .parent_element() | ||||
|                 .unwrap() | ||||
|                 .remove_child(&error) | ||||
|                 .unwrap(); | ||||
|             list.append_child(&error); | ||||
|             let _ = list.append_child(&error); | ||||
|         } | ||||
|         header | ||||
|             .parent_element() | ||||
|             .unwrap() | ||||
|             .insert_before(&list, &header.next_sibling().unwrap()) | ||||
|             .insert_before(&list, header.next_sibling().as_ref()) | ||||
|             .unwrap(); | ||||
|     } | ||||
| } | ||||
| @ -400,9 +498,17 @@ fn init_popup( | ||||
|     content: &HtmlElement, | ||||
|     old_ed: &Element, | ||||
| ) -> Result<Element, EditorError> { | ||||
|     let popup = document().create_element("div")?; | ||||
|     popup.class_list().add("popup")?; | ||||
|     popup.set_attribute("id", "publish-popup")?; | ||||
|     let document = document(); | ||||
|     let popup = document | ||||
|         .create_element("div") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     popup | ||||
|         .class_list() | ||||
|         .add_1("popup") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     popup | ||||
|         .set_attribute("id", "publish-popup") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     let tags = get_elt_value("tags") | ||||
|         .split(',') | ||||
| @ -410,112 +516,157 @@ fn init_popup( | ||||
|         .map(str::to_string) | ||||
|         .collect::<Vec<_>>(); | ||||
|     let license = get_elt_value("license"); | ||||
|     make_input(&i18n!(CATALOG, "Tags"), "popup-tags", &popup).set_raw_value(&tags.join(", ")); | ||||
|     make_input(&i18n!(CATALOG, "License"), "popup-license", &popup).set_raw_value(&license); | ||||
|     make_input(&i18n!(CATALOG, "Tags"), "popup-tags", &popup).set_value(&tags.join(", ")); | ||||
|     make_input(&i18n!(CATALOG, "License"), "popup-license", &popup).set_value(&license); | ||||
| 
 | ||||
|     let cover_label = document().create_element("label")?; | ||||
|     cover_label.append_child(&document().create_text_node(&i18n!(CATALOG, "Cover"))); | ||||
|     cover_label.set_attribute("for", "cover")?; | ||||
|     let cover = document().get_element_by_id("cover")?; | ||||
|     let cover_label = document | ||||
|         .create_element("label") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     cover_label | ||||
|         .append_child(&document.create_text_node(&i18n!(CATALOG, "Cover"))) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     cover_label | ||||
|         .set_attribute("for", "cover") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     let cover = document.get_element_by_id("cover")?; | ||||
|     cover.parent_element()?.remove_child(&cover).ok(); | ||||
|     popup.append_child(&cover_label); | ||||
|     popup.append_child(&cover); | ||||
|     popup | ||||
|         .append_child(&cover_label) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     popup | ||||
|         .append_child(&cover) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     if let Some(draft_checkbox) = document().get_element_by_id("draft") { | ||||
|         let draft_label = document().create_element("label")?; | ||||
|         draft_label.set_attribute("for", "popup-draft")?; | ||||
|     if let Some(draft_checkbox) = document.get_element_by_id("draft") { | ||||
|         let draft_checkbox = draft_checkbox.dyn_ref::<HtmlInputElement>().unwrap(); | ||||
|         let draft_label = document | ||||
|             .create_element("label") | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         draft_label | ||||
|             .set_attribute("for", "popup-draft") | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|         let draft = document().create_element("input").unwrap(); | ||||
|         js! { | ||||
|             @{&draft}.id = "popup-draft"; | ||||
|             @{&draft}.name = "popup-draft"; | ||||
|             @{&draft}.type = "checkbox"; | ||||
|             @{&draft}.checked = @{&draft_checkbox}.checked; | ||||
|         }; | ||||
|         let draft = document.create_element("input").unwrap(); | ||||
|         draft.set_id("popup-draft"); | ||||
|         let draft = draft.dyn_ref::<HtmlInputElement>().unwrap(); | ||||
|         draft.set_name("popup-draft"); | ||||
|         draft.set_type("checkbox"); | ||||
|         draft.set_checked(draft_checkbox.checked()); | ||||
| 
 | ||||
|         draft_label.append_child(&draft); | ||||
|         draft_label.append_child(&document().create_text_node(&i18n!(CATALOG, "This is a draft"))); | ||||
|         popup.append_child(&draft_label); | ||||
|         draft_label | ||||
|             .append_child(&draft) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         draft_label | ||||
|             .append_child(&document.create_text_node(&i18n!(CATALOG, "This is a draft"))) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|         popup | ||||
|             .append_child(&draft_label) | ||||
|             .map_err(|_| EditorError::DOMError)?; | ||||
|     } | ||||
| 
 | ||||
|     let button = document().create_element("input")?; | ||||
|     js! { | ||||
|         @{&button}.type = "submit"; | ||||
|         @{&button}.value = @{i18n!(CATALOG, "Publish")}; | ||||
|     }; | ||||
|     button.append_child(&document().create_text_node(&i18n!(CATALOG, "Publish"))); | ||||
|     button.add_event_listener( | ||||
|         mv!(title, subtitle, content, old_ed => move |_: ClickEvent| { | ||||
|             title.focus(); // Remove the placeholder before publishing
 | ||||
|             set_value("title", title.inner_text()); | ||||
|             subtitle.focus(); | ||||
|             set_value("subtitle", subtitle.inner_text()); | ||||
|             content.focus(); | ||||
|             set_value("editor-content", content.child_nodes().iter().fold(String::new(), |md, ch| { | ||||
|                 let to_append = match ch.node_type() { | ||||
|                     NodeType::Element => { | ||||
|                         if js!{ return @{&ch}.tagName; } == "DIV" { | ||||
|                             (js!{ return @{&ch}.innerHTML; }).try_into().unwrap_or_default() | ||||
|                         } else { | ||||
|                             (js!{ return @{&ch}.outerHTML; }).try_into().unwrap_or_default() | ||||
|                         } | ||||
|                     }, | ||||
|                     NodeType::Text => ch.node_value().unwrap_or_default(), | ||||
|                     _ => unreachable!(), | ||||
|                 }; | ||||
|                 format!("{}\n\n{}", md, to_append) | ||||
|             })); | ||||
|             set_value("tags", get_elt_value("popup-tags")); | ||||
|             if let Some(draft) = document().get_element_by_id("popup-draft") { | ||||
|                 js!{ | ||||
|                     document.getElementById("draft").checked = @{draft}.checked; | ||||
|                 }; | ||||
|             } | ||||
|             let cover = document().get_element_by_id("cover").unwrap(); | ||||
|             cover.parent_element().unwrap().remove_child(&cover).ok(); | ||||
|             old_ed.append_child(&cover); | ||||
|             set_value("license", get_elt_value("popup-license")); | ||||
|             clear_autosave(); | ||||
|             js! { | ||||
|                 @{&old_ed}.submit(); | ||||
|     let button = document | ||||
|         .create_element("input") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     button | ||||
|         .append_child(&document.create_text_node(&i18n!(CATALOG, "Publish"))) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     let button = button.dyn_ref::<HtmlInputElement>().unwrap(); | ||||
|     button.set_type("submit"); | ||||
|     button.set_value(&i18n!(CATALOG, "Publish")); | ||||
|     let callback = Closure::wrap(Box::new(mv!(title, subtitle, content, old_ed => move |_| { | ||||
|         let document = self::document(); | ||||
|         title.focus().unwrap(); // Remove the placeholder before publishing
 | ||||
|         set_value("title", title.inner_text()); | ||||
|         subtitle.focus().unwrap(); | ||||
|         set_value("subtitle", subtitle.inner_text()); | ||||
|         content.focus().unwrap(); | ||||
|         let mut md = String::new(); | ||||
|         let child_nodes = content.child_nodes(); | ||||
|         for i in 0..child_nodes.length() { | ||||
|             let ch = child_nodes.get(i).unwrap(); | ||||
|             let to_append = match ch.node_type() { | ||||
|                 Node::ELEMENT_NODE => { | ||||
|                     let ch = ch.dyn_ref::<Element>().unwrap(); | ||||
|                     if ch.tag_name() == "DIV" { | ||||
|                         ch.inner_html() | ||||
|                     } else { | ||||
|                         ch.outer_html() | ||||
|                     } | ||||
|                 }, | ||||
|                 Node::TEXT_NODE => ch.node_value().unwrap_or_default(), | ||||
|                 _ => unreachable!(), | ||||
|             }; | ||||
|         }), | ||||
|     ); | ||||
|     popup.append_child(&button); | ||||
|             md = format!("{}\n\n{}", md, to_append); | ||||
|         } | ||||
|         set_value("editor-content", md); | ||||
|         set_value("tags", get_elt_value("popup-tags")); | ||||
|         if let Some(draft) = document.get_element_by_id("popup-draft") { | ||||
|             if let Some(draft_checkbox) = document.get_element_by_id("draft") { | ||||
|                 let draft_checkbox = draft_checkbox.dyn_ref::<HtmlInputElement>().unwrap(); | ||||
|                 let draft = draft.dyn_ref::<HtmlInputElement>().unwrap(); | ||||
|                 draft_checkbox.set_checked(draft.checked()); | ||||
|             } | ||||
|         } | ||||
|         let cover = document.get_element_by_id("cover").unwrap(); | ||||
|         cover.parent_element().unwrap().remove_child(&cover).ok(); | ||||
|         old_ed.append_child(&cover).unwrap(); | ||||
|         set_value("license", get_elt_value("popup-license")); | ||||
|         clear_autosave(); | ||||
|         let old_ed = old_ed.dyn_ref::<HtmlFormElement>().unwrap(); | ||||
|         old_ed.submit().unwrap(); | ||||
|     })) as Box<dyn FnMut(MouseEvent)>); | ||||
|     button | ||||
|         .add_event_listener_with_callback("click", callback.as_ref().unchecked_ref()) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     callback.forget(); | ||||
|     popup | ||||
|         .append_child(&button) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     document().body()?.append_child(&popup); | ||||
|     document | ||||
|         .body()? | ||||
|         .append_child(&popup) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     Ok(popup) | ||||
| } | ||||
| 
 | ||||
| fn init_popup_bg() -> Result<Element, EditorError> { | ||||
|     let bg = document().create_element("div")?; | ||||
|     bg.class_list().add("popup-bg")?; | ||||
|     bg.set_attribute("id", "popup-bg")?; | ||||
|     let bg = document() | ||||
|         .create_element("div") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     bg.class_list() | ||||
|         .add_1("popup-bg") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     bg.set_attribute("id", "popup-bg") | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
| 
 | ||||
|     document().body()?.append_child(&bg); | ||||
|     bg.add_event_listener(|_: ClickEvent| close_popup()); | ||||
|     document() | ||||
|         .body()? | ||||
|         .append_child(&bg) | ||||
|         .map_err(|_| EditorError::DOMError)?; | ||||
|     let callback = Closure::wrap(Box::new(|_| close_popup()) as Box<dyn FnMut(MouseEvent)>); | ||||
|     bg.add_event_listener_with_callback("click", callback.as_ref().unchecked_ref()) | ||||
|         .unwrap(); | ||||
|     callback.forget(); | ||||
|     Ok(bg) | ||||
| } | ||||
| 
 | ||||
| fn chars_left(selector: &str, content: &HtmlElement) -> Option<i32> { | ||||
|     match document().query_selector(selector) { | ||||
|         Ok(Some(form)) => HtmlElement::try_from(form).ok().and_then(|form| { | ||||
|         Ok(Some(form)) => form.dyn_ref::<HtmlElement>().and_then(|form| { | ||||
|             if let Some(len) = form | ||||
|                 .get_attribute("content-size") | ||||
|                 .and_then(|s| s.parse::<i32>().ok()) | ||||
|             { | ||||
|                 (js! { | ||||
|                     let x = encodeURIComponent(@{content}.innerHTML) | ||||
|                         .replace(/%20/g, "+") | ||||
|                         .replace(/%0A/g, "%0D%0A") | ||||
|                         .replace(new RegExp("[!'*()]", "g"), "XXX") // replace exceptions of encodeURIComponent with placeholder
 | ||||
|                         .length + 2; | ||||
|                     console.log(x); | ||||
|                     return x; | ||||
|                 }) | ||||
|                 .try_into() | ||||
|                 .map(|c: i32| len - c) | ||||
|                 .ok() | ||||
|                 (encode_uri_component(&content.inner_html()) | ||||
|                     .replace("%20", "+") | ||||
|                     .replace("%0A", "%0D0A") | ||||
|                     .replace_by_pattern(&RegExp::new("[!'*()]", "g"), "XXX") | ||||
|                     .length() | ||||
|                     + 2_u32) | ||||
|                     .try_into() | ||||
|                     .map(|c: i32| len - c) | ||||
|                     .ok() | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
| @ -525,26 +676,26 @@ fn chars_left(selector: &str, content: &HtmlElement) -> Option<i32> { | ||||
| } | ||||
| 
 | ||||
| fn close_popup() { | ||||
|     let hide = |x: Element| x.class_list().remove("show"); | ||||
|     let hide = |x: Element| x.class_list().remove_1("show"); | ||||
|     document().get_element_by_id("publish-popup").map(hide); | ||||
|     document().get_element_by_id("popup-bg").map(hide); | ||||
| } | ||||
| 
 | ||||
| fn make_input(label_text: &str, name: &'static str, form: &Element) -> InputElement { | ||||
|     let label = document().create_element("label").unwrap(); | ||||
|     label.append_child(&document().create_text_node(label_text)); | ||||
| fn make_input(label_text: &str, name: &'static str, form: &Element) -> HtmlInputElement { | ||||
|     let document = document(); | ||||
|     let label = document.create_element("label").unwrap(); | ||||
|     label | ||||
|         .append_child(&document.create_text_node(label_text)) | ||||
|         .unwrap(); | ||||
|     label.set_attribute("for", name).unwrap(); | ||||
| 
 | ||||
|     let inp: InputElement = document() | ||||
|         .create_element("input") | ||||
|         .unwrap() | ||||
|         .try_into() | ||||
|         .unwrap(); | ||||
|     let inp = document.create_element("input").unwrap(); | ||||
|     let inp = inp.dyn_into::<HtmlInputElement>().unwrap(); | ||||
|     inp.set_attribute("name", name).unwrap(); | ||||
|     inp.set_attribute("id", name).unwrap(); | ||||
| 
 | ||||
|     form.append_child(&label); | ||||
|     form.append_child(&inp); | ||||
|     form.append_child(&label).unwrap(); | ||||
|     form.append_child(&inp).unwrap(); | ||||
|     inp | ||||
| } | ||||
| 
 | ||||
| @ -558,36 +709,46 @@ fn make_editable(tag: &'static str) -> Element { | ||||
| } | ||||
| 
 | ||||
| fn placeholder(elt: HtmlElement, text: &str) -> HtmlElement { | ||||
|     elt.dataset().insert("placeholder", text).unwrap(); | ||||
|     elt.dataset().insert("edited", "false").unwrap(); | ||||
|     elt.dataset().set("placeholder", text).unwrap(); | ||||
|     elt.dataset().set("edited", "false").unwrap(); | ||||
| 
 | ||||
|     elt.add_event_listener(mv!(elt => move |_: FocusEvent| { | ||||
|     let callback = Closure::wrap(Box::new(mv!(elt => move |_: FocusEvent| { | ||||
|         if elt.dataset().get("edited").unwrap().as_str() != "true" { | ||||
|             clear_children(&elt); | ||||
|         } | ||||
|     })); | ||||
|     elt.add_event_listener(mv!(elt => move |_: BlurEvent| { | ||||
|     })) as Box<dyn FnMut(FocusEvent)>); | ||||
|     elt.add_event_listener_with_callback("focus", callback.as_ref().unchecked_ref()) | ||||
|         .unwrap(); | ||||
|     callback.forget(); | ||||
|     let callback = Closure::wrap(Box::new(mv!(elt => move |_: Event| { | ||||
|         if elt.dataset().get("edited").unwrap().as_str() != "true" { | ||||
|             clear_children(&elt); | ||||
| 
 | ||||
|             let ph = document().create_element("span").expect("Couldn't create placeholder"); | ||||
|             ph.class_list().add("placeholder").expect("Couldn't add class"); | ||||
|             ph.append_child(&document().create_text_node(&elt.dataset().get("placeholder").unwrap_or_default())); | ||||
|             elt.append_child(&ph); | ||||
|             ph.class_list().add_1("placeholder").expect("Couldn't add class"); | ||||
|             ph.append_child(&document().create_text_node(&elt.dataset().get("placeholder").unwrap_or_default())).unwrap(); | ||||
|             elt.append_child(&ph).unwrap(); | ||||
|         } | ||||
|     })); | ||||
|     elt.add_event_listener(mv!(elt => move |_: KeyUpEvent| { | ||||
|         elt.dataset().insert("edited", if elt.inner_text().trim_matches('\n').is_empty() { | ||||
|     })) as Box<dyn FnMut(Event)>); | ||||
|     elt.add_event_listener_with_callback("blur", callback.as_ref().unchecked_ref()) | ||||
|         .unwrap(); | ||||
|     callback.forget(); | ||||
|     let callback = Closure::wrap(Box::new(mv!(elt => move |_: KeyboardEvent| { | ||||
|         elt.dataset().set("edited", if elt.inner_text().trim_matches('\n').is_empty() { | ||||
|             "false" | ||||
|         } else { | ||||
|             "true" | ||||
|         }).expect("Couldn't update edition state"); | ||||
|     })); | ||||
|     })) as Box<dyn FnMut(KeyboardEvent)>); | ||||
|     elt.add_event_listener_with_callback("keyup", callback.as_ref().unchecked_ref()) | ||||
|         .unwrap(); | ||||
|     callback.forget(); | ||||
|     elt | ||||
| } | ||||
| 
 | ||||
| fn clear_children(elt: &HtmlElement) { | ||||
|     for child in elt.child_nodes() { | ||||
|         elt.remove_child(&child).unwrap(); | ||||
|     let child_nodes = elt.child_nodes(); | ||||
|     for _ in 0..child_nodes.length() { | ||||
|         elt.remove_child(&child_nodes.get(0).unwrap()).unwrap(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,7 +7,7 @@ extern crate gettext_macros; | ||||
| extern crate lazy_static; | ||||
| 
 | ||||
| use wasm_bindgen::{prelude::*, JsCast}; | ||||
| use web_sys::{window, Element, Event, HtmlInputElement, TouchEvent}; | ||||
| use web_sys::{console, window, Element, Event, HtmlInputElement, TouchEvent}; | ||||
| 
 | ||||
| init_i18n!( | ||||
|     "plume-front", | ||||
| @ -53,7 +53,7 @@ init_i18n!( | ||||
|     zh | ||||
| ); | ||||
| 
 | ||||
| // mod editor;
 | ||||
| mod editor; | ||||
| 
 | ||||
| compile_i18n!(); | ||||
| 
 | ||||
| @ -78,8 +78,15 @@ lazy_static! { | ||||
| 
 | ||||
| #[wasm_bindgen(start)] | ||||
| pub fn main() -> Result<(), JsValue> { | ||||
|     extern crate console_error_panic_hook; | ||||
|     use std::panic; | ||||
|     panic::set_hook(Box::new(console_error_panic_hook::hook)); | ||||
| 
 | ||||
|     menu(); | ||||
|     search(); | ||||
|     editor::init() | ||||
|         .map_err(|e| console::error_1(&&format!("Editor error: {:?}", e).into())) | ||||
|         .ok(); | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user