mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-25 13:01:08 +00:00
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
6 changed files with 209 additions and 71 deletions
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…
Reference in a new issue