X-Git-Url: http://git.roojs.org/?a=blobdiff_plain;f=Roo%2Fhtmleditor%2FTidyWriter.js;h=575efdc3625914d4347b2bc132345cbfe46345bc;hb=b85f624217edf58108ae121a01b6d0e9a7d613b5;hp=889338620a781ec70c6c7872ac8259e670a1b161;hpb=f23949669cf9c280ac4f220b4f41516ad656cfa9;p=roojs1 diff --git a/Roo/htmleditor/TidyWriter.js b/Roo/htmleditor/TidyWriter.js index 889338620a..575efdc362 100644 --- a/Roo/htmleditor/TidyWriter.js +++ b/Roo/htmleditor/TidyWriter.js @@ -1,7 +1,13 @@ /*** * This is based loosely on tinymce * @class Roo.htmleditor.TidyWriter - * + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js + * + * Known issues? + * - not tested much with 'PRE' formated elements. + * - long text inside of inline can be wrapped and clened? + * + * */ Roo.htmleditor.TidyWriter = function(settings) @@ -10,157 +16,322 @@ Roo.htmleditor.TidyWriter = function(settings) // indent, indentBefore, indentAfter, encode, htmlOutput, html = []; Roo.apply(this, settings); this.html = []; - - this.indentBefore =this.makeMap(settings.indent_before || ''); - this.indentAfter = this.makeMap(settings.indent_after || ''); + this.state = []; + this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities); - + } -Roo.apply(Roo.htmleditor.TidyWriter, - { - +Roo.htmleditor.TidyWriter.prototype = { + + makeMap : function (items, delim, map) { var i; - items = items || []; delim = delim || ','; - if (typeof items == "string") { items = items.split(delim); } - map = map || {}; - i = items.length; while (i--) { map[items[i]] = {}; } - return map; }, - - indent : 0, - indentBefore : false, - indentAfter : false, - encod : false, + state : false, - encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities); - htmlOutput = 'html' == settings.element_format; + indent : ' ', + + // part of state... + indentstr : '', + in_pre: false, + in_inline : false, + last_inline : false, + encode : false, + - - /** - * Writes the a start element such as

. - * - * @method start - * @param {String} name Name of the element. - * @param {Array} attrs Optional attribute array or undefined if it hasn't any. - * @param {Boolean} empty Optional empty state if the tag should end like
. - */ - start: function(name, attrs, empty) { - var i, l, attr, value; - if (indent && indentBefore[name] && html.length > 0) { - value = html[html.length - 1]; - value.length > 0 && '\n' !== value && html.push('\n'); - } - html.push('<', name); - if (attrs) { - for (i = 0, l = attrs.length; i < l; i++) { - attr = attrs[i]; - html.push(' ', attr.name, '="', encode(attr.value, true), '"'); - } - } - html[html.length] = !empty || htmlOutput ? '>' : ' />'; - if (empty && indent && indentAfter[name] && html.length > 0) { - value = html[html.length - 1]; - value.length > 0 && '\n' !== value && html.push('\n'); - } - }, /** - * Writes the a end element such as

. - * - * @method end - * @param {String} name Name of the element. - */ - end: function(name) { - var value; - /*if (indent && indentBefore[name] && html.length > 0) { - value = html[html.length - 1]; - if (value.length > 0 && value !== '\n') - html.push('\n'); - }*/ - html.push(''); - if (indent && indentAfter[name] && html.length > 0) { - value = html[html.length - 1]; - value.length > 0 && '\n' !== value && html.push('\n'); + * Writes the a start element such as

. + * + * @method start + * @param {String} name Name of the element. + * @param {Array} attrs Optional attribute array or undefined if it hasn't any. + * @param {Boolean} empty Optional empty state if the tag should end like
. + */ + start: function(name, attrs, empty, node) + { + var i, l, attr, value; + + // there are some situations where adding line break && indentation will not work. will not work. + // -1; + var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1; + + var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false; + + var i_inline = name == 'BR' ? false : in_inline; + + var indentstr = in_inli_inlineine || this.in_pre ? '' : this.indentstr; + + // e_inline = elements that can be inline, but still allow \n before and after? + // only 'BR' ??? any others? + var e_inline = name == 'BR' ? false : this.in_inline; + + // if this element is inline - then don't add stuff beforehand.. + if (!e_inline && !this.in_pre) { + this.addLine(); + } + + this.html.push(indentstr + '<', name.toLowerCase()); + + if (attrs) { + for (i = 0, l = attrs.length; i < l; i++) { + attr = attrs[i]; + this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"'); + } + } + + if (empty) { + if (is_short) { + this.html[this.html.length] = '/>'; + } else { + this.html[this.html.length] = '>'; + } + var e_inline = name == 'BR' ? false : this.in_inline; + + if (!e_inline && !this.in_pre) { + this.addLine(); + } + return; + + } + // not empty.. + this.html[this.html.length] = '>'; + + // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these. + /* + if (!in_inline && !in_pre) { + var cn = node.firstChild; + while(cn) { + if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) { + in_inline = true + break; } - }, - /** - * Writes a text node. - * - * @method text - * @param {String} text String to write out. - * @param {Boolean} raw Optional raw state if true the contents wont get encoded. - */ - text: function(text, raw) { - text.length > 0 && (html[html.length] = raw ? text : encode(text)); - }, - /** - * Writes a cdata node such as . - * - * @method cdata - * @param {String} text String to write out inside the cdata. - */ - cdata: function(text) { - html.push(''); - }, - /** - * Writes a comment node such as . - * - * @method cdata - * @param {String} text String to write out inside the comment. - */ - comment: function(text) { - html.push(''); - }, - /** - * Writes a PI node such as . - * - * @method pi - * @param {String} name Name of the pi. - * @param {String} text String to write out inside the pi. - */ - pi: function(name, text) { - text ? html.push('') : html.push(''); - indent && html.push('\n'); - }, - /** - * Writes a doctype node such as . - * - * @method doctype - * @param {String} text String to write out inside the doctype. - */ - doctype: function(text) { - html.push('', indent ? '\n' : ''); - }, - /** - * Resets the internal buffer if one wants to reuse the writer. - * - * @method reset - */ - reset: function() { - html.length = 0; - }, - /** - * Returns the contents that got serialized. - * - * @method getContent - * @return {String} HTML contents that got written down. - */ - getContent: function() { - return html.join('').replace(/\n$/, ''); + cn = cn.nextSibling; } + + } + */ + + + this.pushState({ + indentstr : in_pre || in_inline ? '' : (this.indentstr + this.indent), + in_pre : in_pre, + in_inline : in_inline + }); + // add a line after if we are not in a + + if (!in_inline && !in_pre) { + this.addLine(); + } + + + + + }, + /** + * Writes the a end element such as

. + * + * @method end + * @param {String} name Name of the element. + */ + end: function(name) { + var value; + this.popState(); + var indentstr = ''; + var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1; + + if (!this.in_pre && !in_inline) { + this.addLine(); + indentstr = this.indentstr; + } + this.html.push(indentstr + ''); + this.last_inline = in_inline; + + // pop the indent state.. + }, + /** + * Writes a text node. + * + * In pre - we should not mess with the contents. + * + * + * @method text + * @param {String} text String to write out. + * @param {Boolean} raw Optional raw state if true the contents wont get encoded. + */ + text: function(text, node) + { + // if not in whitespace critical + if (text.length < 1) { + return; + } + if (this.in_pre || this.in_inline) { + this.html[this.html.length] = text; + return; + } + // see if last element was a inline element. + var indentstr = this.indentstr; + if (node.previousSibling && + node.previousSibling.nodeType == 1 && + Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1) + { + indentstr = ''; + } else { + this.addLine(); + } + + + + text = text.replace(/\s/g," ") // all line breaks to ' ' + .replace(/^\s+/,'') // leding white space + .replace(/\s+$/,''); // clean trailing white space + + if (text.length < 1) { + return; + } + if (!text.match(/\n/)) { + this.html.push(indentstr + text); + return; + } + + text = this.indentstr + text.replace( + /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr + ); + // remoeve the last whitespace / line break. + text = text.replace(/\s+$/,''); + + this.html.push(text); + + // split and indent.. + + + }, + /** + * Writes a cdata node such as . + * + * @method cdata + * @param {String} text String to write out inside the cdata. + */ + cdata: function(text) { + this.html.push(''); + }, + /** + * Writes a comment node such as . + * + * @method cdata + * @param {String} text String to write out inside the comment. + */ + comment: function(text) { + this.html.push(''); + }, + /** + * Writes a PI node such as . + * + * @method pi + * @param {String} name Name of the pi. + * @param {String} text String to write out inside the pi. + */ + pi: function(name, text) { + text ? this.html.push('') : this.html.push(''); + this.indent != '' && this.html.push('\n'); + }, + /** + * Writes a doctype node such as . + * + * @method doctype + * @param {String} text String to write out inside the doctype. + */ + doctype: function(text) { + this.html.push('', this.indent != '' ? '\n' : ''); + }, + /** + * Resets the internal buffer if one wants to reuse the writer. + * + * @method reset + */ + reset: function() { + this.html.length = 0; + this.state = []; + this.pushState({ + indentstr : '', + in_pre : false, + in_inline : false + }) + }, + /** + * Returns the contents that got serialized. + * + * @method getContent + * @return {String} HTML contents that got written down. + */ + getContent: function() { + return this.html.join('').replace(/\n$/, ''); + }, + + pushState : function(cfg) + { + this.state.push(cfg); + Roo.apply(this, cfg); + }, + + popState : function() + { + if (this.state.length < 1) { + return; // nothing to push + } + var cfg = { + in_pre: false, + indentstr : '' }; - }; -}); \ No newline at end of file + this.state.pop(); + if (this.state.length > 0) { + cfg = this.state[this.state.length-1]; + } + Roo.apply(this, cfg); + }, + + addLine: function() + { + if (this.html.length < 1) { + return; + } + + + var value = this.html[this.html.length - 1]; + if (value.length > 0 && '\n' !== value) { + this.html.push('\n'); + } + } + + +//'pre script noscript style textarea video audio iframe object code' +// shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track'); +// inline +}; + +Roo.htmleditor.TidyWriter.inline_elements = [ + 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR', + 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP' +]; +Roo.htmleditor.TidyWriter.shortend_elements = [ + 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT', + 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK' +]; + +Roo.htmleditor.TidyWriter.whitespace_elements = [ + 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE' +]; \ No newline at end of file