Index: trunk/parsers/wikidom/tests/hype/es.DocumentModel.test.js |
— | — | @@ -204,5 +204,5 @@ |
205 | 205 | |
206 | 206 | deepEqual( documentModel.getData(), data, 'Flattening plain objects results in correct data' ); |
207 | 207 | deepEqual( documentModel.slice( 0 ), tree, 'Nodes contain correct lengths' ); |
208 | | - equal( documentModel[2].getContent(), ['a'], 'Content can be extracted from document' ); |
| 208 | + deepEqual( documentModel[2].getContent(), ['a'], 'Content can be extracted from document' ); |
209 | 209 | } ); |
Index: trunk/parsers/wikidom/lib/hype/models/es.DocumentModel.js |
— | — | @@ -201,28 +201,36 @@ |
202 | 202 | var currentNode = this; |
203 | 203 | for ( var i = 0, length = this.data.length; i < length; i++ ) { |
204 | 204 | if ( this.data[i].type !== undefined ) { |
205 | | - // It's an element |
| 205 | + // It's an element, figure out it's type |
206 | 206 | var type = this.data[i].type, |
207 | 207 | open = type[0] !== '/'; |
| 208 | + // Trim the "/" off the beginning of closing tag types |
208 | 209 | if ( !open ) { |
209 | 210 | type = type.substr( 1 ); |
210 | 211 | } |
211 | | - if ( !( type in es.DocumentModel.nodeModels ) ) { |
212 | | - throw 'Unsuported element error. No class registered for element type: ' + type; |
213 | | - } |
214 | 212 | if ( open ) { |
| 213 | + // Validate the element type |
| 214 | + if ( !( type in es.DocumentModel.nodeModels ) ) { |
| 215 | + throw 'Unsuported element error. No class registered for element type: ' + type; |
| 216 | + } |
| 217 | + // Create a model node for the element |
215 | 218 | var newNode = new es.DocumentModel.nodeModels[this.data[i].type](); |
| 219 | + // Add the new model node as a child |
216 | 220 | currentNode.push( newNode ); |
| 221 | + // Descend into the new model node |
217 | 222 | currentNode = newNode; |
218 | 223 | } else { |
| 224 | + // Return to the parent node |
219 | 225 | currentNode = currentNode.getParent(); |
220 | 226 | } |
221 | 227 | } else { |
222 | | - // It's content |
| 228 | + // It's content, let's start tracking the length |
223 | 229 | var start = i; |
| 230 | + // Move forward to the next object, tracking the length as we go |
224 | 231 | while ( this.data[i].type === undefined && i < length ) { |
225 | 232 | i++; |
226 | 233 | } |
| 234 | + // Now we know how long the current node is |
227 | 235 | currentNode.setContentLength( i - start ); |
228 | 236 | i--; |
229 | 237 | } |
— | — | @@ -255,6 +263,8 @@ |
256 | 264 | * This method is pretty expensive. If you need to get different slices of the same content, get |
257 | 265 | * the content first, then slice it up locally. |
258 | 266 | * |
| 267 | + * TODO: Rewrite this method to not use recursion, because the function call overhead is expensive |
| 268 | + * |
259 | 269 | * @method |
260 | 270 | * @param {es.DocumentModelNode} node Node to get offset of |
261 | 271 | * @param {Boolean} [deep=false] Whether to scan recursively |
— | — | @@ -315,6 +325,7 @@ |
316 | 326 | }; |
317 | 327 | } |
318 | 328 | var offset = this.offsetOf( node, true ); |
| 329 | + console.log( offset ); |
319 | 330 | if ( offset !== -1 ) { |
320 | 331 | return this.data.slice( offset + 1, offset + node.getContentLength() + 1 ); |
321 | 332 | } |
Index: trunk/parsers/wikidom/lib/hype/bases/es.DocumentModelNode.js |
— | — | @@ -11,7 +11,7 @@ |
12 | 12 | */ |
13 | 13 | es.DocumentModelNode = function( contents ) { |
14 | 14 | // Extension |
15 | | - var node = $.extend( new es.ModelNode( $.isArray( contents ) ? contents : [] ), this ); |
| 15 | + var node = $.extend( new es.ModelNode(), this ); |
16 | 16 | |
17 | 17 | // Observe add and remove operations to keep lengths up to date |
18 | 18 | node.addListenerMethods( node, { |
— | — | @@ -23,18 +23,15 @@ |
24 | 24 | } ); |
25 | 25 | |
26 | 26 | // Properties |
| 27 | + node.contentLength = 0; |
27 | 28 | if ( typeof contents === 'number' ) { |
28 | 29 | if ( contents < 0 ) { |
29 | 30 | throw 'Invalid content length error. Content length can not be less than 0.'; |
30 | 31 | } |
31 | 32 | node.contentLength = contents; |
32 | | - } else { |
33 | | - node.contentLength = 0; |
34 | | - // If contents was an array, some items were added, which we need to account for |
35 | | - if ( node.length ) { |
36 | | - for ( var i = 0; i < node.length; i++ ) { |
37 | | - node.contentLength += node[i].getElementLength(); |
38 | | - } |
| 33 | + } else if ( $.isArray( contents ) ) { |
| 34 | + for ( var i = 0; i < contents.length; i++ ) { |
| 35 | + node.push( contents[i] ); |
39 | 36 | } |
40 | 37 | } |
41 | 38 | |
Index: trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js |
— | — | @@ -71,22 +71,35 @@ |
72 | 72 | }; |
73 | 73 | |
74 | 74 | /** |
| 75 | + * Add a listener, mapped to a method on a target object. |
| 76 | + * |
| 77 | + * @method |
| 78 | + * @param target {Object} Object to call methods on when events occur |
| 79 | + * @param event {String} Name of event to trigger on |
| 80 | + * @param method {String} Name of method to call |
| 81 | + * @returns {es.EventEmitter} This object |
| 82 | + */ |
| 83 | +es.EventEmitter.prototype.addListenerMethod = function( target, event, method ) { |
| 84 | + return this.addListener( event, function() { |
| 85 | + if ( typeof target[method] === 'function' ) { |
| 86 | + target[method].apply( target, Array.prototype.slice.call( arguments, 0 ) ); |
| 87 | + } else { |
| 88 | + throw 'Listener method error. Target has no such method: ' + method; |
| 89 | + } |
| 90 | + } ); |
| 91 | +}; |
| 92 | + |
| 93 | +/** |
75 | 94 | * Add multiple listeners, each mapped to a method on a target object. |
76 | 95 | * |
77 | 96 | * @method |
78 | | - * @param context {Object} Object to call methods on when events occur |
| 97 | + * @param target {Object} Object to call methods on when events occur |
79 | 98 | * @param methods {Object} List of event/method name pairs |
80 | 99 | * @returns {es.EventEmitter} This object |
81 | 100 | */ |
82 | | -es.EventEmitter.prototype.addListenerMethods = function( context, methods ) { |
| 101 | +es.EventEmitter.prototype.addListenerMethods = function( target, methods ) { |
83 | 102 | for ( var event in methods ) { |
84 | | - this.addListener( event, function() { |
85 | | - if ( methods[event] in context ) { |
86 | | - context[methods[event]].apply( context, Array.prototype.slice( arguments, 1 ) ); |
87 | | - } else { |
88 | | - throw 'Listener method error. Context has no such method: ' + methods[event]; |
89 | | - } |
90 | | - } ); |
| 103 | + this.addListenerMethod( target, event, methods[event] ); |
91 | 104 | } |
92 | 105 | return this; |
93 | 106 | }; |
— | — | @@ -110,7 +123,7 @@ |
111 | 124 | var eventEmitter = this; |
112 | 125 | return this.addListener( type, function listenerWrapper() { |
113 | 126 | eventEmitter.removeListener( type, listenerWrapper ); |
114 | | - listener.apply( eventEmitter, arguments ); |
| 127 | + listener.apply( eventEmitter, Array.prototype.slice.call( arguments, 0 ) ); |
115 | 128 | } ); |
116 | 129 | }; |
117 | 130 | |