1 var ElementType = require("domelementtype");
3 var re_whitespace = /\s+/g;
4 var NodePrototype = require("./lib/node");
5 var ElementPrototype = require("./lib/element");
7 function DomHandler(callback, options, elementCB){
8 if(typeof callback === "object"){
12 } else if(typeof options === "function"){
14 options = defaultOpts;
16 this._callback = callback;
17 this._options = options || defaultOpts;
18 this._elementCB = elementCB;
22 this._parser = this._parser || null;
27 normalizeWhitespace: false, //Replace all whitespace with single spaces
28 withStartIndices: false, //Add startIndex properties to nodes
29 withEndIndices: false, //Add endIndex properties to nodes
32 DomHandler.prototype.onparserinit = function(parser){
33 this._parser = parser;
36 //Resets the handler back to starting state
37 DomHandler.prototype.onreset = function(){
38 DomHandler.call(this, this._callback, this._options, this._elementCB);
41 //Signals the handler that parsing is done
42 DomHandler.prototype.onend = function(){
43 if(this._done) return;
46 this._handleCallback(null);
49 DomHandler.prototype._handleCallback =
50 DomHandler.prototype.onerror = function(error){
51 if(typeof this._callback === "function"){
52 this._callback(error, this.dom);
54 if(error) throw error;
58 DomHandler.prototype.onclosetag = function(){
59 //if(this._tagStack.pop().name !== name) this._handleCallback(Error("Tagname didn't match!"));
61 var elem = this._tagStack.pop();
63 if(this._options.withEndIndices && elem){
64 elem.endIndex = this._parser.endIndex;
67 if(this._elementCB) this._elementCB(elem);
70 DomHandler.prototype._createDomElement = function(properties){
71 if (!this._options.withDomLvl1) return properties;
74 if (properties.type === "tag") {
75 element = Object.create(ElementPrototype);
77 element = Object.create(NodePrototype);
80 for (var key in properties) {
81 if (properties.hasOwnProperty(key)) {
82 element[key] = properties[key];
89 DomHandler.prototype._addDomElement = function(element){
90 var parent = this._tagStack[this._tagStack.length - 1];
91 var siblings = parent ? parent.children : this.dom;
92 var previousSibling = siblings[siblings.length - 1];
96 if(this._options.withStartIndices){
97 element.startIndex = this._parser.startIndex;
99 if(this._options.withEndIndices){
100 element.endIndex = this._parser.endIndex;
104 element.prev = previousSibling;
105 previousSibling.next = element;
110 siblings.push(element);
111 element.parent = parent || null;
114 DomHandler.prototype.onopentag = function(name, attribs){
116 type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag,
122 var element = this._createDomElement(properties);
124 this._addDomElement(element);
126 this._tagStack.push(element);
129 DomHandler.prototype.ontext = function(data){
130 //the ignoreWhitespace is officially dropped, but for now,
131 //it's an alias for normalizeWhitespace
132 var normalize = this._options.normalizeWhitespace || this._options.ignoreWhitespace;
136 if(!this._tagStack.length && this.dom.length && (lastTag = this.dom[this.dom.length-1]).type === ElementType.Text){
138 lastTag.data = (lastTag.data + data).replace(re_whitespace, " ");
140 lastTag.data += data;
144 this._tagStack.length &&
145 (lastTag = this._tagStack[this._tagStack.length - 1]) &&
146 (lastTag = lastTag.children[lastTag.children.length - 1]) &&
147 lastTag.type === ElementType.Text
150 lastTag.data = (lastTag.data + data).replace(re_whitespace, " ");
152 lastTag.data += data;
156 data = data.replace(re_whitespace, " ");
159 var element = this._createDomElement({
161 type: ElementType.Text
164 this._addDomElement(element);
169 DomHandler.prototype.oncomment = function(data){
170 var lastTag = this._tagStack[this._tagStack.length - 1];
172 if(lastTag && lastTag.type === ElementType.Comment){
173 lastTag.data += data;
179 type: ElementType.Comment
182 var element = this._createDomElement(properties);
184 this._addDomElement(element);
185 this._tagStack.push(element);
188 DomHandler.prototype.oncdatastart = function(){
192 type: ElementType.Text
194 type: ElementType.CDATA
197 var element = this._createDomElement(properties);
199 this._addDomElement(element);
200 this._tagStack.push(element);
203 DomHandler.prototype.oncommentend = DomHandler.prototype.oncdataend = function(){
204 this._tagStack.pop();
207 DomHandler.prototype.onprocessinginstruction = function(name, data){
208 var element = this._createDomElement({
211 type: ElementType.Directive
214 this._addDomElement(element);
217 module.exports = DomHandler;