"use strict"; var put; function Element(tag){ this.tag = tag; } // create set of elements with an empty content model, so we now to skip their closing tag var emptyElements = {}; ["base", "link", "meta", "hr", "br", "wbr", "img", "embed", "param", "source", "track", "area", "col", "input", "keygen", "command"].forEach(function(tag){ emptyElements[tag] = true; }); var prototype = Element.prototype; var currentIndentation = ''; prototype.nodeType = 1; prototype.put = function(){ var args = [this]; args.push.apply(args, arguments); return put.apply(null, args); } prototype.toString = function(noClose){ var tag = this.tag; var emptyElement = emptyElements[tag]; if(put.indentation && !noClose){ // using pretty printing with indentation var lastIndentation = currentIndentation; currentIndentation += put.indentation; var html = (tag == 'html' ? '\n' + (this.children ? this.children.join('') : '') + (!this.mixed && !emptyElement && this.children ? '\n' +lastIndentation : '') + (emptyElement ? '' : ('')); currentIndentation = lastIndentation; return html; } return (this.tag == 'html' ? '\n' + (this.children ? this.children.join('') : '') + ((noClose || emptyElement) ? '' : ('')); }; prototype.sendTo = function(stream){ if(typeof stream == 'function'){ // write function itself stream = {write: stream, end: stream}; } var active = this; var streamIndentation = ''; function pipe(element){ // TODO: Perhaps consider buffering if it is any faster and having a non-indentation version that is faster var closing = returnTo(this); if(closing){ stream.write(closing); } var tag = element.tag; if(element.tag){ if(put.indentation){ stream.write('\n' + streamIndentation + element.toString(true)); streamIndentation += put.indentation; }else{ stream.write(element.toString(true)); } this.children = true; active = element; element.pipe = pipe; }else{ stream.write(element.toString()); } } function returnTo(element){ var output = ''; while(active != element){ if(!active){ throw new Error("Can not add to an element that has already been streamed"); } var tag = active.tag; var emptyElement = emptyElements[tag]; if(put.indentation){ streamIndentation = streamIndentation.slice(put.indentation.length); if(!emptyElement){ output += ((active.mixed || !active.children) ? '' : '\n' + streamIndentation) + ''; } }else if(!emptyElement){ output += ''; } active = active.parentNode; } return output; } pipe.call(this, this); // add on end() function to close all the tags and close the stream this.end = function(leaveStreamOpen){ stream[leaveStreamOpen ? 'write' : 'end'](returnTo(this) + '\n'); } return this; }; prototype.children = false; prototype.attributes = false; prototype.insertBefore = function(child, reference){ child.parentNode = this; if(this.pipe){ return this.pipe(child); //return this.s(child); } var children = this.children; if(!children){ children = this.children = []; } if(reference){ for(var i = 0, l = children.length; i < l; i++){ if(reference == children[i]){ child.nextSibling = reference; if(i > 0){ children[i-1].nextSibling = child; } return children.splice(i, 0, child); } } } if(children.length > 0){ children[children.length-1].nextSibling = child; } children.push(child); }; prototype.appendChild = function(child){ if(typeof child == "string"){ this.mixed = true; } if(this.pipe){ return this.pipe(child); } var children = this.children; if(!children){ children = this.children = []; } children.push(child); }; prototype.setAttribute = function(name, value, escape){ var attributes = this.attributes; if(!attributes){ attributes = this.attributes = []; } attributes.push(' ' + name + '="' + value + '"'); }; prototype.removeAttribute = function(name, value){ var attributes = this.attributes; if(!attributes){ return; } var match = ' ' + name + '=', matchLength = match.length; for(var i = 0, l = attributes.length; i < l; i++){ if(attributes[i].slice(0, matchLength) == match){ return attributes.splice(i, 1); } } }; Object.defineProperties(prototype, { innerHTML: { get: function(){ return this.children.join(''); }, set: function(value){ this.mixed = true; if(this.pipe){ return this.pipe(value); } this.children = [value]; } } }); function DocumentFragment(){ } DocumentFragment.prototype = new Element(); DocumentFragment.prototype.toString = function(){ return this.children ? this.children.join('') : ''; }; var lessThanRegex = /