Prototype for a WYSIWYG editor
We use pulldown-cmark in plume-front too now, but instead of using the provided HTML renderer, we use a custom DOM renderer, which let us use contenteditable only where we want, and which will allow us to add event listeners to provide a good contextual edition experience. Also removed the character counter, as the API limits are almost unreachable.
This commit is contained in:
		
							parent
							
								
									3669a0097d
								
							
						
					
					
						commit
						bce806ac63
					
				
							
								
								
									
										40
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										40
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -243,7 +243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "1.0.4" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| @ -370,7 +370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -382,7 +382,7 @@ name = "cloudabi" | ||||
| version = "0.0.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -659,7 +659,7 @@ name = "devise_core" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -670,7 +670,7 @@ name = "diesel" | ||||
| version = "1.4.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "diesel_derives 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -923,7 +923,7 @@ name = "fsevent" | ||||
| version = "0.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| @ -953,7 +953,7 @@ name = "fuchsia-zircon" | ||||
| version = "0.3.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| @ -1236,7 +1236,7 @@ name = "inotify" | ||||
| version = "0.6.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| @ -1630,7 +1630,7 @@ name = "nix" | ||||
| version = "0.14.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -1656,7 +1656,7 @@ name = "notify" | ||||
| version = "4.0.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "filetime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -1722,7 +1722,7 @@ name = "openssl" | ||||
| version = "0.10.22" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -1966,6 +1966,7 @@ dependencies = [ | ||||
|  "gettext-utils 0.1.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)", | ||||
|  "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "plume-api 0.3.0", | ||||
|  "pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "stdweb 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "stdweb-internal-runtime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -2082,7 +2083,17 @@ name = "pulldown-cmark" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pulldown-cmark" | ||||
| version = "0.5.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| @ -2686,7 +2697,7 @@ name = "shrinkwraprs" | ||||
| version = "0.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @ -3536,7 +3547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" | ||||
| "checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" | ||||
| "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" | ||||
| "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" | ||||
| "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" | ||||
| "checksum bitpacking 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "667f3f518358b2cf64891b46a6dd2eb794e9f80d39f7eb5974f4784bcda9a61b" | ||||
| "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" | ||||
| "checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" | ||||
| @ -3730,6 +3741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" | ||||
| "checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d" | ||||
| "checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15" | ||||
| "checksum pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "77043da1282374688ee212dc44b3f37ff929431de9c9adc3053bd3cee5630357" | ||||
| "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" | ||||
| "checksum quick-xml 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8065cbb01701c11cc195cde85cbf39d1c6a80705b67a157ebb3042e0e5777f" | ||||
| "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" | ||||
|  | ||||
| @ -12,3 +12,7 @@ gettext-utils = { git = "https://github.com/Plume-org/gettext-macros/", rev = "a | ||||
| lazy_static = "1.3" | ||||
| plume-api = { path = "../plume-api" } | ||||
| serde_json = "1.0" | ||||
| 
 | ||||
| [dependencies.pulldown-cmark] | ||||
| default-features = false | ||||
| version = "0.5" | ||||
|  | ||||
| @ -1,15 +1,180 @@ | ||||
| use pulldown_cmark::{Event, Options, Parser, Tag}; | ||||
| use stdweb::{ | ||||
|     unstable::{TryFrom, TryInto}, | ||||
|     web::{event::*, html_element::*, *}, | ||||
| }; | ||||
| use CATALOG; | ||||
| 
 | ||||
| macro_rules! mv { | ||||
|     ( $( $var:ident ),* => $exp:expr ) => { | ||||
|         { | ||||
|             $( let $var = $var.clone(); )* | ||||
|             $exp | ||||
| fn from_md(md: &str) { | ||||
|     let md_parser = Parser::new_ext(md, Options::all()); | ||||
|     md_parser.fold( | ||||
|         document().get_element_by_id("editor-main").unwrap(), | ||||
|         |last_elt, event| { | ||||
|             match event { | ||||
|                 Event::Start(tag) => { | ||||
|                     let new = match tag { | ||||
|                         Tag::Paragraph => document().create_element("p").unwrap(), | ||||
|                         Tag::Rule => document().create_element("hr").unwrap(), | ||||
|                         Tag::Header(level) => { | ||||
|                             document().create_element(&format!("h{}", level)).unwrap() | ||||
|                         } | ||||
|                         Tag::BlockQuote => document().create_element("blockquote").unwrap(), | ||||
|                         Tag::CodeBlock(code) => { | ||||
|                             let pre = document().create_element("pre").unwrap(); | ||||
|                             let code_elt = document().create_element("code").unwrap(); | ||||
|                             code_elt.append_child(&document().create_text_node(&code)); | ||||
|                             pre.append_child(&code_elt); | ||||
|                             pre | ||||
|                         } | ||||
|                         Tag::List(None) => document().create_element("ul").unwrap(), | ||||
|                         Tag::List(Some(_start_index)) => document().create_element("ol").unwrap(), // TODO: handle start_index
 | ||||
|                         Tag::Item => document().create_element("li").unwrap(), | ||||
|                         Tag::FootnoteDefinition(def) => { | ||||
|                             let note = document().create_element("div").unwrap(); | ||||
|                             note.class_list().add("footnote"); | ||||
|                             note.append_child(&document().create_text_node(&def)); | ||||
|                             note | ||||
|                         } | ||||
|                         Tag::HtmlBlock => document().create_element("div").unwrap(), | ||||
|                         Tag::Table(_alignements) => document().create_element("table").unwrap(), // TODO: handle alignements
 | ||||
|                         Tag::TableHead => document().create_element("th").unwrap(), | ||||
|                         Tag::TableRow => document().create_element("tr").unwrap(), | ||||
|                         Tag::TableCell => document().create_element("td").unwrap(), | ||||
|                         Tag::Emphasis => document().create_element("em").unwrap(), | ||||
|                         Tag::Strong => document().create_element("strong").unwrap(), | ||||
|                         Tag::Strikethrough => document().create_element("s").unwrap(), | ||||
|                         Tag::Link(_link_type, url, text) => { | ||||
|                             let url: &str = &url; | ||||
|                             let text: &str = &text; | ||||
|                             let link = document().create_element("a").unwrap(); | ||||
|                             js! { | ||||
|                                 @{&link}.href = @{url}; | ||||
|                                 @{&link}.title = @{text}; | ||||
|                             }; | ||||
|                             link | ||||
|                         } | ||||
|                         Tag::Image(_link_type, url, text) => { | ||||
|                             let url: &str = &url; | ||||
|                             let text: &str = &text; | ||||
|                             let img = document().create_element("img").unwrap(); | ||||
|                             js! { | ||||
|                                 @{&img}.src = @{url}; | ||||
|                                 @{&img}.title = @{text}; | ||||
|                                 @{&img}.alt = @{text}; | ||||
|                             }; | ||||
|                             img | ||||
|                         } | ||||
|                     }; | ||||
|                     last_elt.append_child(&new); | ||||
|                     new | ||||
|                 } | ||||
|                 Event::End(_) => last_elt.parent_element().unwrap(), | ||||
|                 Event::Text(text) => { | ||||
|                     let node = document().create_text_node(&text); | ||||
|                     last_elt.append_child(&node); | ||||
|                     last_elt | ||||
|                 } | ||||
|                 Event::Code(code) => { | ||||
|                     let elt = document().create_element("code").unwrap(); | ||||
|                     let content = document().create_text_node(&code); | ||||
|                     elt.append_child(&content); | ||||
|                     last_elt.append_child(&elt); | ||||
|                     last_elt | ||||
|                 } | ||||
|                 Event::Html(html) => { | ||||
|                     // TODO: sanitize it?
 | ||||
|                     last_elt.set_attribute("innerHtml", &html); | ||||
|                     last_elt | ||||
|                 } | ||||
|                 Event::InlineHtml(html) => { | ||||
|                     let elt = document().create_element("span").unwrap(); | ||||
|                     elt.set_attribute("innerHtml", &html); | ||||
|                     last_elt.append_child(&elt); | ||||
|                     last_elt | ||||
|                 } | ||||
|                 Event::FootnoteReference(reference) => { | ||||
|                     last_elt // TODO
 | ||||
|                 } | ||||
|                 Event::SoftBreak => { | ||||
|                     last_elt.append_child(&document().create_element("br").unwrap()); | ||||
|                     last_elt | ||||
|                 } | ||||
|                 Event::HardBreak => { | ||||
|                     last_elt // TODO
 | ||||
|                 } | ||||
|                 Event::TaskListMarker(done) => { | ||||
|                     last_elt // TODO
 | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| fn to_md() -> String { | ||||
|     let root = document().get_element_by_id("editor-main").unwrap(); | ||||
|     fold_children(&root).join("") | ||||
| } | ||||
| 
 | ||||
| fn fold_children(elt: &Element) -> Vec<String> { | ||||
|     elt.child_nodes().iter().fold(vec![], |mut blocks, node| { | ||||
|         blocks.push(html_to_md(&node)); | ||||
|         blocks | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| fn html_to_md(node: &Node) -> String { | ||||
|     console!(log, node); | ||||
|     if let Ok(elt) = Element::try_from(node.clone()) { | ||||
|         console!(log, elt.node_name().to_lowercase()); | ||||
|         match elt.node_name().to_lowercase().as_ref() { | ||||
|             "hr" => "---".into(), | ||||
|             "h1" => format!("# {}\n\n", fold_children(&elt).join("")), | ||||
|             "h2" => format!("## {}\n\n", fold_children(&elt).join("")), | ||||
|             "h3" => format!("### {}\n\n", fold_children(&elt).join("")), | ||||
|             "h4" => format!("#### {}\n\n", fold_children(&elt).join("")), | ||||
|             "h5" => format!("##### {}\n\n", fold_children(&elt).join("")), | ||||
|             "h6" => format!("###### {}\n\n", fold_children(&elt).join("")), | ||||
|             "blockquote" => format!("> {}\n\n", fold_children(&elt).join("> ")), | ||||
|             "pre" => format!("```\n{}\n```\n\n", node.text_content().unwrap_or_default()), | ||||
|             "li" => match elt | ||||
|                 .parent_element() | ||||
|                 .unwrap() | ||||
|                 .node_name() | ||||
|                 .to_lowercase() | ||||
|                 .as_ref() | ||||
|             { | ||||
|                 "ol" => format!( | ||||
|                     "{}. {}\n", | ||||
|                     elt.parent_element() | ||||
|                         .unwrap() | ||||
|                         .child_nodes() | ||||
|                         .iter() | ||||
|                         .position(|n| Element::try_from(n).unwrap() == elt) | ||||
|                         .unwrap_or_default(), | ||||
|                     fold_children(&elt).join(""), | ||||
|                 ), | ||||
|                 _ => format!("- {}\n", fold_children(&elt).join("")), | ||||
|             }, | ||||
|             "em" => format!("_{}_", fold_children(&elt).join("")), | ||||
|             "strong" => format!("**{}**", fold_children(&elt).join("")), | ||||
|             "s" => format!("~~{}~~", fold_children(&elt).join("")), | ||||
|             "a" => format!( | ||||
|                 "[{}]({})", | ||||
|                 fold_children(&elt).join(""), | ||||
|                 String::try_from(js! { return @{&elt}.href }).unwrap() | ||||
|             ), | ||||
|             "img" => format!( | ||||
|                 "", | ||||
|                 String::try_from(js! { return @{&elt}.alt }).unwrap(), | ||||
|                 String::try_from(js! { return @{&elt}.src }).unwrap() | ||||
|             ), | ||||
|             other => { | ||||
|                 console!(log, "Warning: unhandled element:", other); | ||||
|                 String::new() | ||||
|             } // TODO: refs, tables, raw html
 | ||||
|         } | ||||
|     } else { | ||||
|         node.text_content().unwrap_or_default() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -116,27 +281,16 @@ fn init_editor() -> Result<(), EditorError> { | ||||
|         // And pre-fill the new editor with this values
 | ||||
|         let title = document().get_element_by_id("editor-title")?; | ||||
|         let subtitle = document().get_element_by_id("editor-subtitle")?; | ||||
|         let content = document().get_element_by_id("editor-default-paragraph")?; | ||||
|         let source = get_elt_value("editor-content"); | ||||
| 
 | ||||
|         from_md(&source); | ||||
| 
 | ||||
|         title.add_event_listener(no_return); | ||||
|         subtitle.add_event_listener(no_return); | ||||
| 
 | ||||
|         filter_paste(&title); | ||||
|         filter_paste(&subtitle); | ||||
|         filter_paste(&content); | ||||
| 
 | ||||
|         // character counter
 | ||||
|         content.add_event_listener(mv!(content => move |_: KeyDownEvent| { | ||||
|             window().set_timeout(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(); | ||||
|                 }; | ||||
|             }), 0); | ||||
|         })); | ||||
|         // TODO: filter_paste(&content);
 | ||||
| 
 | ||||
|         document() | ||||
|             .get_element_by_id("publish")? | ||||
| @ -224,6 +378,7 @@ fn save(is_draft: bool) { | ||||
|                 .ok(); | ||||
|         } | ||||
|     }); | ||||
|     console!(log, to_md()); | ||||
|     let data = plume_api::posts::NewPostData { | ||||
|         title: HtmlElement::try_from(document().get_element_by_id("editor-title").unwrap()) | ||||
|             .unwrap() | ||||
| @ -231,13 +386,7 @@ fn save(is_draft: bool) { | ||||
|         subtitle: document() | ||||
|             .get_element_by_id("editor-subtitle") | ||||
|             .map(|s| HtmlElement::try_from(s).unwrap().inner_text()), | ||||
|         source: HtmlElement::try_from( | ||||
|             document() | ||||
|                 .get_element_by_id("editor-default-paragraph") | ||||
|                 .unwrap(), | ||||
|         ) | ||||
|         .unwrap() | ||||
|         .inner_text(), | ||||
|         source: to_md(), | ||||
|         author: String::new(), // it is ignored anyway (TODO: remove it ??)
 | ||||
|         blog_id: i32::try_from(js! { return window.blog_id }).ok(), | ||||
|         published: Some(!is_draft), | ||||
| @ -287,30 +436,3 @@ fn show_errors() { | ||||
|             .unwrap(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn chars_left(selector: &str, content: &Element) -> Option<i32> { | ||||
|     match document().query_selector(selector) { | ||||
|         Ok(Some(form)) => HtmlElement::try_from(form).ok().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() | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         }), | ||||
|         _ => None, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,7 @@ extern crate gettext; | ||||
| extern crate gettext_macros; | ||||
| #[macro_use] | ||||
| extern crate lazy_static; | ||||
| extern crate pulldown_cmark; | ||||
| #[macro_use] | ||||
| extern crate stdweb; | ||||
| extern crate serde_json; | ||||
|  | ||||
| @ -411,7 +411,7 @@ main .article-meta { | ||||
|     margin-top: 110px; | ||||
|   } | ||||
| 
 | ||||
|   #edition-area > *[contenteditable] { | ||||
|   #editor-title, #editor-subtitle, #editor-main > * { | ||||
|     padding-left: 18px; | ||||
|     border-left: 2px solid transparent; | ||||
|     transition: border-left-color 0.1s ease-in; | ||||
|  | ||||
| @ -25,12 +25,11 @@ | ||||
|     <div id="plume-editor" style="display: none;" dir="auto"> | ||||
|       <header> | ||||
|         <a href="#" id="close-editor">@i18n!(ctx.1, "Classic editor (any changes will be lost)")</a> | ||||
|         <p id="char-count">@content_len</p> | ||||
|       </header> | ||||
|       <article id="edition-area"> | ||||
|         <h1 contenteditable id="editor-title" data-placeholder="@i18n!(ctx.1, "Type your title")">@form.title</h1> | ||||
|         <h2 contenteditable id="editor-subtitle" data-placeholder="@i18n!(ctx.1, "Type a subtitle or a summary")">@form.subtitle</h2> | ||||
|         <p contenteditable id="editor-default-paragraph" data-placeholder="@i18n!(ctx.1, "Write your article. You can use Markdown.")">@form.content</p> | ||||
|         <article contenteditable id="editor-main" data-placeholder="@i18n!(ctx.1, "Write your article here. You can use markdown.")"></article> | ||||
|       </article> | ||||
|       <aside id="plume-editor-aside" style="display: none;"> | ||||
|         <div id="options-page"> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user