| var ElementType = require("domelementtype"); | 
|   | 
| var re_whitespace = /\s+/g; | 
| var NodePrototype = require("./lib/node"); | 
| var ElementPrototype = require("./lib/element"); | 
|   | 
| function DomHandler(callback, options, elementCB){ | 
|     if(typeof callback === "object"){ | 
|         elementCB = options; | 
|         options = callback; | 
|         callback = null; | 
|     } else if(typeof options === "function"){ | 
|         elementCB = options; | 
|         options = defaultOpts; | 
|     } | 
|     this._callback = callback; | 
|     this._options = options || defaultOpts; | 
|     this._elementCB = elementCB; | 
|     this.dom = []; | 
|     this._done = false; | 
|     this._tagStack = []; | 
|     this._parser = this._parser || null; | 
| } | 
|   | 
| //default options | 
| var defaultOpts = { | 
|     normalizeWhitespace: false, //Replace all whitespace with single spaces | 
|     withStartIndices: false, //Add startIndex properties to nodes | 
|     withEndIndices: false, //Add endIndex properties to nodes | 
| }; | 
|   | 
| DomHandler.prototype.onparserinit = function(parser){ | 
|     this._parser = parser; | 
| }; | 
|   | 
| //Resets the handler back to starting state | 
| DomHandler.prototype.onreset = function(){ | 
|     DomHandler.call(this, this._callback, this._options, this._elementCB); | 
| }; | 
|   | 
| //Signals the handler that parsing is done | 
| DomHandler.prototype.onend = function(){ | 
|     if(this._done) return; | 
|     this._done = true; | 
|     this._parser = null; | 
|     this._handleCallback(null); | 
| }; | 
|   | 
| DomHandler.prototype._handleCallback = | 
| DomHandler.prototype.onerror = function(error){ | 
|     if(typeof this._callback === "function"){ | 
|         this._callback(error, this.dom); | 
|     } else { | 
|         if(error) throw error; | 
|     } | 
| }; | 
|   | 
| DomHandler.prototype.onclosetag = function(){ | 
|     //if(this._tagStack.pop().name !== name) this._handleCallback(Error("Tagname didn't match!")); | 
|      | 
|     var elem = this._tagStack.pop(); | 
|   | 
|     if(this._options.withEndIndices && elem){ | 
|         elem.endIndex = this._parser.endIndex; | 
|     } | 
|   | 
|     if(this._elementCB) this._elementCB(elem); | 
| }; | 
|   | 
| DomHandler.prototype._createDomElement = function(properties){ | 
|     if (!this._options.withDomLvl1) return properties; | 
|   | 
|     var element; | 
|     if (properties.type === "tag") { | 
|         element = Object.create(ElementPrototype); | 
|     } else { | 
|         element = Object.create(NodePrototype); | 
|     } | 
|   | 
|     for (var key in properties) { | 
|         if (properties.hasOwnProperty(key)) { | 
|             element[key] = properties[key]; | 
|         } | 
|     } | 
|   | 
|     return element; | 
| }; | 
|   | 
| DomHandler.prototype._addDomElement = function(element){ | 
|     var parent = this._tagStack[this._tagStack.length - 1]; | 
|     var siblings = parent ? parent.children : this.dom; | 
|     var previousSibling = siblings[siblings.length - 1]; | 
|   | 
|     element.next = null; | 
|   | 
|     if(this._options.withStartIndices){ | 
|         element.startIndex = this._parser.startIndex; | 
|     } | 
|     if(this._options.withEndIndices){ | 
|         element.endIndex = this._parser.endIndex; | 
|     } | 
|   | 
|     if(previousSibling){ | 
|         element.prev = previousSibling; | 
|         previousSibling.next = element; | 
|     } else { | 
|         element.prev = null; | 
|     } | 
|   | 
|     siblings.push(element); | 
|     element.parent = parent || null; | 
| }; | 
|   | 
| DomHandler.prototype.onopentag = function(name, attribs){ | 
|     var properties = { | 
|         type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag, | 
|         name: name, | 
|         attribs: attribs, | 
|         children: [] | 
|     }; | 
|   | 
|     var element = this._createDomElement(properties); | 
|   | 
|     this._addDomElement(element); | 
|   | 
|     this._tagStack.push(element); | 
| }; | 
|   | 
| DomHandler.prototype.ontext = function(data){ | 
|     //the ignoreWhitespace is officially dropped, but for now, | 
|     //it's an alias for normalizeWhitespace | 
|     var normalize = this._options.normalizeWhitespace || this._options.ignoreWhitespace; | 
|   | 
|     var lastTag; | 
|   | 
|     if(!this._tagStack.length && this.dom.length && (lastTag = this.dom[this.dom.length-1]).type === ElementType.Text){ | 
|         if(normalize){ | 
|             lastTag.data = (lastTag.data + data).replace(re_whitespace, " "); | 
|         } else { | 
|             lastTag.data += data; | 
|         } | 
|     } else { | 
|         if( | 
|             this._tagStack.length && | 
|             (lastTag = this._tagStack[this._tagStack.length - 1]) && | 
|             (lastTag = lastTag.children[lastTag.children.length - 1]) && | 
|             lastTag.type === ElementType.Text | 
|         ){ | 
|             if(normalize){ | 
|                 lastTag.data = (lastTag.data + data).replace(re_whitespace, " "); | 
|             } else { | 
|                 lastTag.data += data; | 
|             } | 
|         } else { | 
|             if(normalize){ | 
|                 data = data.replace(re_whitespace, " "); | 
|             } | 
|   | 
|             var element = this._createDomElement({ | 
|                 data: data, | 
|                 type: ElementType.Text | 
|             }); | 
|   | 
|             this._addDomElement(element); | 
|         } | 
|     } | 
| }; | 
|   | 
| DomHandler.prototype.oncomment = function(data){ | 
|     var lastTag = this._tagStack[this._tagStack.length - 1]; | 
|   | 
|     if(lastTag && lastTag.type === ElementType.Comment){ | 
|         lastTag.data += data; | 
|         return; | 
|     } | 
|   | 
|     var properties = { | 
|         data: data, | 
|         type: ElementType.Comment | 
|     }; | 
|   | 
|     var element = this._createDomElement(properties); | 
|   | 
|     this._addDomElement(element); | 
|     this._tagStack.push(element); | 
| }; | 
|   | 
| DomHandler.prototype.oncdatastart = function(){ | 
|     var properties = { | 
|         children: [{ | 
|             data: "", | 
|             type: ElementType.Text | 
|         }], | 
|         type: ElementType.CDATA | 
|     }; | 
|   | 
|     var element = this._createDomElement(properties); | 
|   | 
|     this._addDomElement(element); | 
|     this._tagStack.push(element); | 
| }; | 
|   | 
| DomHandler.prototype.oncommentend = DomHandler.prototype.oncdataend = function(){ | 
|     this._tagStack.pop(); | 
| }; | 
|   | 
| DomHandler.prototype.onprocessinginstruction = function(name, data){ | 
|     var element = this._createDomElement({ | 
|         name: name, | 
|         data: data, | 
|         type: ElementType.Directive | 
|     }); | 
|   | 
|     this._addDomElement(element); | 
| }; | 
|   | 
| module.exports = DomHandler; |