2 * This is based loosely on tinymce
3 * @class Roo.htmleditor.TidyWriter
4 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
7 Roo.htmleditor.TidyWriter = function(settings)
10 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
11 Roo.apply(this, settings);
15 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
18 Roo.htmleditor.TidyWriter.prototype = {
22 makeMap : function (items, delim, map) {
26 if (typeof items == "string") {
27 items = items.split(delim);
50 * Writes the a start element such as <p id="a">.
53 * @param {String} name Name of the element.
54 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
55 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
57 start: function(name, attrs, empty, node)
59 var i, l, attr, value;
61 // there are some situations where adding line break && indentation will not work. will not work.
62 // <span / b / i ... formating?
64 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
65 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
67 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
70 var indentstr = in_inline || this.in_pre ? '' : this.indentstr;
72 // if this element is inline - then don't add stuff beforehand..
73 if (!in_inline && !this.in_pre) {
77 this.html.push(indentstr + '<', name.toLowerCase());
80 for (i = 0, l = attrs.length; i < l; i++) {
82 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
88 this.html[this.html.length] = '/>';
90 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
93 if (!this.in_inline && !this.in_pre) {
100 this.html[this.html.length] = '>';
102 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
104 if (!in_inline && !in_pre) {
105 var cn = node.firstChild;
107 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
119 indentstr : in_pre || in_inline ? '' : (this.indentstr + this.indent),
121 in_inline : in_inline
123 // add a line after if we are not in a
125 if (!in_inline && !in_pre) {
134 * Writes the a end element such as </p>.
137 * @param {String} name Name of the element.
139 end: function(name) {
143 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
145 if (!this.in_pre && !in_inline) {
147 indentstr = this.indentstr;
149 this.html.push(indentstr + '</', name.toLowerCase(), '>');
150 this.last_inline = in_inline;
152 // pop the indent state..
155 * Writes a text node.
157 * In pre - we should not mess with the contents.
161 * @param {String} text String to write out.
162 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
164 text: function(text, node)
166 // if not in whitespace critical
167 if (text.length < 1) {
170 if (this.in_pre || this.in_inline) {
171 this.html[this.html.length] = text;
174 // see if last element was a inline element.
175 var indentstr = this.indentstr;
176 if (node.previousSibling &&
177 node.previousSibling.nodeType == 1 &&
178 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
187 text = text.replace(/\s/g," ") // all line breaks to ' '
188 .replace(/^\s+/,'') // leding white space
189 .replace(/\s+$/,''); // clean trailing white space
191 if (text.length < 1) {
194 if (!text.match(/\n/)) {
195 this.html.push(indentstr + text);
199 text = this.indentstr + text.replace(
200 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
202 // remoeve the last whitespace / line break.
203 text = text.replace(/\s+$/,'');
205 this.html.push(text);
207 // split and indent..
212 * Writes a cdata node such as <![CDATA[data]]>.
215 * @param {String} text String to write out inside the cdata.
217 cdata: function(text) {
218 this.html.push('<![CDATA[', text, ']]>');
221 * Writes a comment node such as <!-- Comment -->.
224 * @param {String} text String to write out inside the comment.
226 comment: function(text) {
227 this.html.push('<!--', text, '-->');
230 * Writes a PI node such as <?xml attr="value" ?>.
233 * @param {String} name Name of the pi.
234 * @param {String} text String to write out inside the pi.
236 pi: function(name, text) {
237 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
238 this.indent != '' && this.html.push('\n');
241 * Writes a doctype node such as <!DOCTYPE data>.
244 * @param {String} text String to write out inside the doctype.
246 doctype: function(text) {
247 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
250 * Resets the internal buffer if one wants to reuse the writer.
255 this.html.length = 0;
264 * Returns the contents that got serialized.
267 * @return {String} HTML contents that got written down.
269 getContent: function() {
270 return this.html.join('').replace(/\n$/, '');
273 pushState : function(cfg)
275 this.state.push(cfg);
276 Roo.apply(this, cfg);
279 popState : function()
281 if (this.state.length < 1) {
282 return; // nothing to push
289 if (this.state.length > 0) {
290 cfg = this.state[this.state.length-1];
292 Roo.apply(this, cfg);
297 if (this.html.length < 1) {
302 var value = this.html[this.html.length - 1];
303 if (value.length > 0 && '\n' !== value) {
304 this.html.push('\n');
309 //'pre script noscript style textarea video audio iframe object code'
310 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
314 Roo.htmleditor.TidyWriter.inline_elements = [
315 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
316 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP'
318 Roo.htmleditor.TidyWriter.shortend_elements = [
319 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
320 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
323 Roo.htmleditor.TidyWriter.whitespace_elements = [
324 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'