X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=editor.js;h=e4ab02476439c5e7ae15636083a9b88c06b084a4;hb=2424d0277c3130c74e6f0ab84855a653fe7818a5;hp=16986f49e415c956cdca068483d6ec99cbb16e49;hpb=447762cf08b157e951670256bd0d5b6fcf2e3cd1;p=peach-html5-editor.git diff --git a/editor.js b/editor.js index 16986f4..e4ab024 100644 --- a/editor.js +++ b/editor.js @@ -225,7 +225,8 @@ function new_cursor_position (args) { return null } -// encode text so it can be safely placed inside an html attribute +// html encoding for attributes +// encoding nbsp is not required, but hopefully it is useful enc_attr_regex = new RegExp('(&)|(")|(\u00A0)', 'g') function enc_attr (txt) { return txt.replace(enc_attr_regex, function(match, amp, quote) { @@ -238,6 +239,8 @@ function enc_attr (txt) { return ' ' }) } +// html encoding for text (does nothing to stop whitespace collapse) +// encoding nbsp is not required, but hopefully it is useful enc_text_regex = new RegExp('(&)|(<)|(\u00A0)', 'g') function enc_text (txt) { return txt.replace(enc_text_regex, function(match, amp, lt) { @@ -251,23 +254,94 @@ function enc_text (txt) { }) } +// no closing tag, cannot have children void_elements = { - area: true, - base: true, - br: true, - col: true, - embed: true, - hr: true, - img: true, - input: true, - keygen: true, - link: true, - meta: true, - param: true, - source: true, - track: true, - wbr: true + area: 1, + base: 1, + br: 1, + col: 1, + embed: 1, + hr: 1, + img: 1, + input: 1, + keygen: 1, + link: 1, + meta: 1, + param: 1, + source: 1, + track: 1, + wbr: 1 } + +// contents are not html encoded +plaintext_elements = { + style: 1, + script: 1, + xmp: 1, + iframe: 1, + noembed: 1, + noframes: 1, + plaintext: 1, + noscript: 1 +} + +// parser deletes a starting newline inside: +newline_eating_elements = { + pre: 1, + textarea: 1, + listing: 1 +} + +// this does not pretty-print +function nodes_to_html (tree) { + var attr_keys, i, k, n, ret + ret = '' + for (i = 0; i < tree.length; ++i) { + n = tree[i] + switch (n.type) { + case 'tag': + ret += '<' + n.name + attr_keys = [] + for (k in n.attrs) { + ret += " " + k + if (n.attrs[k].length > 0) { + ret += "=\"" + (enc_attr(n.attrs[k])) + "\"" + } + } + ret += '>' + if (void_elements[n.name] == null) { + if (n.children.length) { + ret += nodes_to_html(n.children) + } + ret += "" + } + break + case 'text': + if (n.parent != null ? plaintext_elements[n.parent.name] : false) { + ret += n.text + } else if (n.parent != null ? newline_eating_elements[n.parent.name] && n.text.charAt(0) === "\n" : false) { + ret += enc_text("\n" + n.text) + } else { + ret += enc_text(n.text) + } + break + case 'comment': + ret += "" // TODO encode? + break + case 'doctype': + ret += " 0) { + ret += " \"" + n.public_identifier + "\"" + } + if ((n.system_identifier != null) && n.system_identifier.length > 0) { + ret += " \"" + n.system_identifier + "\"" + } + ret += ">" + } + } + return ret +} + // TODO make these always pretty-print (on the inside) like blocks // TODO careful though: whitespace might get pushed to parent, which might be rendered no_text_elements = { // these elements never contain text @@ -723,6 +797,7 @@ function PeachHTML5Editor (in_el, options) { // on_init: callback for when the editable content is in place var css, opt_fragment, outer_bounds, outer_iframe_style, outer_wrap this.options = options != null ? options : {} + this.pretty_print = options.pretty_print != null ? options.pretty_print : true this.in_el = in_el this.tree = null // array of Nodes, all editable content this.tree_parent = null // this.tree is this.children. .el might === this.idoc.body @@ -1628,7 +1703,11 @@ PeachHTML5Editor.prototype.load_html = function(html) { } PeachHTML5Editor.prototype.changed = function() { this.in_el.onchange = null - this.in_el.value = this.pretty_html(this.tree) + if (this.pretty_print) { + this.in_el.value = this.pretty_html(this.tree) + } else { + this.in_el.value = nodes_to_html(this.tree) + } this.in_el.onchange = (function(_this) { return function() { return _this.load_html(_this.in_el.value) }})(this) @@ -1754,7 +1833,7 @@ PeachHTML5Editor.prototype.preserve_space = function(n, ideal_target) { PeachHTML5Editor.prototype.update_style_from_el = function(n) { var style style = n.el.getAttribute('style') - if (style != null) { + if (style != null && style != '') { return n.attrs.style = style } else { if (n.attrs.style != null) { @@ -1996,6 +2075,7 @@ PeachHTML5Editor.prototype.text_cleanup = function(n) { if (run == null) { return } + // merge consecutive text elements if (run.length > 1) { i = 1 prev = run[0] @@ -2077,7 +2157,7 @@ PeachHTML5Editor.prototype.text_cleanup = function(n) { if (need_preserve) { // do we have it already? ws = this.computed_style(n, 'white-space') // FIXME implement this - if (ws_props[ws] == null ? true : ws_props[ws].space == null) { + if (ws_props[ws] != null ? !ws_props[ws].space : true) { // 2nd arg is ideal target for css rule ws = this.preserve_space(n, block) } @@ -2418,7 +2498,13 @@ PeachHTML5Editor.prototype.pretty_html = function(tree, indent, parent_flags) { } break case 'text': - ret += enc_text(n.text) + if (n.parent != null ? plaintext_elements[n.parent.name] : false) { + ret += n.text + } else if (n.parent != null ? newline_eating_elements[n.parent.name] && n.text.charAt(0) === "\n" : false) { + ret += enc_text("\n" + n.text) + } else { + ret += enc_text(n.text) + } break case 'comment': ret += "" // TODO encode?