Index: trunk/parsers/wikidom/lib/hype/es.js |
— | — | @@ -73,23 +73,48 @@ |
74 | 74 | }; |
75 | 75 | |
76 | 76 | /** |
77 | | - * Gets a recursive copy of an object's string, number and plain-object property. |
| 77 | + * Gets a deep copy of an array's string, number, array and plain-object contents. |
78 | 78 | * |
79 | 79 | * @static |
80 | 80 | * @method |
| 81 | + * @param source {Array} Array to copy |
| 82 | + * @returns {Array} Copy of source array |
| 83 | + */ |
| 84 | +es.copyArray = function( source ) { |
| 85 | + var destination = []; |
| 86 | + for ( var i = 0; i < source.length; i++ ) { |
| 87 | + sourceValue = source[i]; |
| 88 | + sourceType = typeof sourceValue; |
| 89 | + if ( sourceType === 'string' || sourceType === 'number' ) { |
| 90 | + destination.push( sourceValue ); |
| 91 | + } else if ( $.isPlainObject( sourceValue ) ) { |
| 92 | + destination.push( es.copyObject( sourceValue ) ); |
| 93 | + } else if ( $.isArray( sourceValue ) ) { |
| 94 | + destination.push( es.copyArray( sourceValue ) ); |
| 95 | + } |
| 96 | + } |
| 97 | + return destination; |
| 98 | +}; |
| 99 | + |
| 100 | +/** |
| 101 | + * Gets a deep copy of an object's string, number, array and plain-object properties. |
| 102 | + * |
| 103 | + * @static |
| 104 | + * @method |
81 | 105 | * @param source {Object} Object to copy |
82 | 106 | * @returns {Object} Copy of source object |
83 | 107 | */ |
84 | 108 | es.copyObject = function( source ) { |
85 | 109 | var destination = {}; |
86 | | - var key; |
87 | | - for ( key in source ) { |
| 110 | + for ( var key in source ) { |
88 | 111 | sourceValue = source[key]; |
89 | 112 | sourceType = typeof sourceValue; |
90 | 113 | if ( sourceType === 'string' || sourceType === 'number' ) { |
91 | 114 | destination[key] = sourceValue; |
92 | 115 | } else if ( $.isPlainObject( sourceValue ) ) { |
93 | 116 | destination[key] = es.copyObject( sourceValue ); |
| 117 | + } else if ( $.isArray( sourceValue ) ) { |
| 118 | + destination[key] = es.copyArray( sourceValue ); |
94 | 119 | } |
95 | 120 | } |
96 | 121 | return destination; |
Index: trunk/parsers/wikidom/lib/hype/models/es.DocumentModel.js |
— | — | @@ -14,43 +14,123 @@ |
15 | 15 | |
16 | 16 | /* Static Methods */ |
17 | 17 | |
18 | | -es.DocumentModel.isContent = function( content ) { |
19 | | - return typeof content === 'string' || $.isArray( content ); |
| 18 | +/** |
| 19 | + * Checks if a data at a given offset is content. |
| 20 | + * |
| 21 | + * @static |
| 22 | + * @method |
| 23 | + * @param {Integer} offset Offset in data to check |
| 24 | + * @returns {Boolean} If data at offset is content |
| 25 | + */ |
| 26 | +es.DocumentModel.isContent = function( offset ) { |
| 27 | + return typeof this.data[offset] === 'string' || $.isArray( this.data[offset] ); |
20 | 28 | }; |
21 | 29 | |
22 | | -es.DocumentModel.isElement = function( content ) { |
23 | | - return content.type !== undefined; |
| 30 | +/** |
| 31 | + * Checks if a data at a given offset is an element. |
| 32 | + * |
| 33 | + * @static |
| 34 | + * @method |
| 35 | + * @param {Integer} offset Offset in data to check |
| 36 | + * @returns {Boolean} If data at offset is an element |
| 37 | + */ |
| 38 | +es.DocumentModel.isElement = function( offset ) { |
| 39 | + // TODO: Is there a safer way to check if it's a plain object without sacrificing speed? |
| 40 | + return this.data[offset].type !== undefined; |
24 | 41 | }; |
25 | 42 | |
| 43 | +/** |
| 44 | + * Flatten a plain node object into a data array, recursively. |
| 45 | + * |
| 46 | + * TODO: where do we document this whole structure - aka "WikiDom"? |
| 47 | + * |
| 48 | + * @static |
| 49 | + * @method |
| 50 | + * @param {Object} obj Plain node object to flatten |
| 51 | + * @returns {Array} Flattened version of obj |
| 52 | + */ |
| 53 | +es.DocumentModel.flattenPlainObjectNode = function( obj ) { |
| 54 | + var i, data = []; |
| 55 | + // Open element |
| 56 | + data.push( { 'type': obj.type, 'attributes': es.copyObject( obj.attributes ), 'node': null } ); |
| 57 | + if ( obj.content !== undefined ) { |
| 58 | + // Add content |
| 59 | + data = data.concat( es.ContentModel.newFromPlainObject( obj.content ).data ); |
| 60 | + } else { |
| 61 | + // Add children - only do this if there is no content property |
| 62 | + for ( i = 0; i < obj.children.length; i++ ) { |
| 63 | + // TODO: Figure out if all this concatenating is inefficient. I think it is |
| 64 | + data = data.concat( flattenNode( obj.children[i] ) ); |
| 65 | + } |
| 66 | + } |
| 67 | + // Close element - TODO: Do we need attributes here or not? |
| 68 | + data.push( { 'type': '/' + obj.type, 'node': null } ); |
| 69 | + return data; |
| 70 | +}; |
| 71 | + |
26 | 72 | /* Methods */ |
27 | 73 | |
28 | | -es.DocumentModel.prototype.findElement = function( node, root ) { |
29 | | - for ( var i = 0; i < this.data.length; i++ ) { |
30 | | - if ( es.DocumentModel.isElement( this.data[i] ) ) { |
31 | | - if ( content.node === node ) { |
32 | | - return i; |
| 74 | +/** |
| 75 | + * Gets copy of the document data. |
| 76 | + * |
| 77 | + * @method |
| 78 | + * @param {es.Range} [range] Range of data to get, all data will be given by default |
| 79 | + * @param {Boolean} [deep=false] Whether to return a deep copy (WARNING! This may be very slow) |
| 80 | + * @returns {Array} Copy of document data |
| 81 | + */ |
| 82 | +es.DocumentModel.prototype.getData = function( range, deep ) { |
| 83 | + var start = 0, |
| 84 | + end = undefined; |
| 85 | + if ( range !== undefined ) { |
| 86 | + range.normalize(); |
| 87 | + start = Math.max( 0, Math.min( this.data.length, range.start ) ); |
| 88 | + end = Math.max( 0, Math.min( this.data.length, range.end ) ); |
| 89 | + } |
| 90 | + var data = this.data.slice( start, end ); |
| 91 | + return deep ? es.copyArray( data ) : data; |
| 92 | +}; |
| 93 | + |
| 94 | +/** |
| 95 | + * Gets the content offset of a node. |
| 96 | + * |
| 97 | + * @method |
| 98 | + * @param {es.DocumentModelNode} node Node to get offset of |
| 99 | + * @param {Boolean} deep Whether to scan recursively |
| 100 | + * @param {es.DocumentModelNode} [from=this] Node to look within |
| 101 | + * @returns {Integer|false} Offset of node or null of node was not found |
| 102 | + */ |
| 103 | +es.DocumentModel.prototype.offsetOf = function( node, deep, from ) { |
| 104 | + if ( from === undefined ) { |
| 105 | + from = this; |
| 106 | + } |
| 107 | + var offset = 0; |
| 108 | + for ( var i = 0; i < this.length; i++ ) { |
| 109 | + if ( node === this[i] ) { |
| 110 | + return offset; |
| 111 | + } |
| 112 | + if ( deep && node.length ) { |
| 113 | + var length = this.findElement( node, deep, node ); |
| 114 | + if ( length !== null ) { |
| 115 | + return offset + length; |
33 | 116 | } |
34 | | - // If we are looking for a root node, we can skip over the contents of this one |
35 | | - if ( root ) { |
36 | | - i += node.getContentLength() + 2; |
37 | | - } |
38 | 117 | } |
| 118 | + offset += node.getContentLength() + 2; |
39 | 119 | } |
40 | | - return null; |
| 120 | + return false; |
41 | 121 | }; |
42 | 122 | |
43 | 123 | /** |
44 | 124 | * Gets the element object of a node. |
45 | 125 | * |
46 | 126 | * @method |
47 | | - * @param {es.DocumentModelNode} node Reference to node object to get element object for |
48 | | - * @param {Boolean} root Whether to only scan root nodes |
| 127 | + * @param {es.DocumentModelNode} node Node to get element object for |
| 128 | + * @param {Boolean} deep Whether to scan recursively |
49 | 129 | * @returns {Object|null} Element object |
50 | 130 | */ |
51 | | -es.DocumentModel.prototype.getElement = function( node, root ) { |
52 | | - var index = this.findNode( node, root ); |
53 | | - if ( index !== null ) { |
54 | | - return this.data[index]; |
| 131 | +es.DocumentModel.prototype.getElement = function( node, deep ) { |
| 132 | + var offset = this.offsetOf( node, deep ); |
| 133 | + if ( offset !== false ) { |
| 134 | + return this.data[offset]; |
55 | 135 | } |
56 | 136 | return null; |
57 | 137 | }; |
— | — | @@ -59,14 +139,14 @@ |
60 | 140 | * Gets the content data of a node. |
61 | 141 | * |
62 | 142 | * @method |
63 | | - * @param {es.DocumentModelNode} node Reference to node object to get content data for |
64 | | - * @param {Boolean} root Whether to only scan root nodes |
| 143 | + * @param {es.DocumentModelNode} node Node to get content data for |
| 144 | + * @param {Boolean} deep Whether to scan recursively |
65 | 145 | * @returns {Array|null} List of content and elements inside node or null if node is not found |
66 | 146 | */ |
67 | | -es.DocumentModel.prototype.getContent = function( node, root ) { |
68 | | - var index = this.findNode( node, root ); |
69 | | - if ( index !== null ) { |
70 | | - return this.data.slice( index + 1, index + node.getContentLength() ); |
| 147 | +es.DocumentModel.prototype.getContent = function( node, deep ) { |
| 148 | + var offset = this.offsetOf( node, deep ); |
| 149 | + if ( offset !== false ) { |
| 150 | + return this.data.slice( offset + 1, offset + node.getContentLength() ); |
71 | 151 | } |
72 | 152 | return null; |
73 | 153 | }; |
— | — | @@ -169,33 +249,7 @@ |
170 | 250 | */ |
171 | 251 | |
172 | 252 | es.DocumentModel.newFromPlainObject = function( obj ) { |
173 | | - /* |
174 | | - * Flatten a node and its children into a data array, recursively. |
175 | | - * |
176 | | - * @param obj {Object} A plain node object //TODO where do we document this whole structure? |
177 | | - * @return {Array} Array to append the flattened version of obj to |
178 | | - */ |
179 | | - function flattenNode( obj ) { |
180 | | - var i, data = []; |
181 | | - // Open element |
182 | | - // TODO do we need to copy the attributes object or can we use a reference? |
183 | | - data.push( { 'type': obj.type, 'attributes': obj.attributes, 'node': null } ); |
184 | | - if ( obj.content !== undefined ) { |
185 | | - // Add content |
186 | | - data = data.concat( es.ContentModel.newFromPlainObject( obj.content ).data ); |
187 | | - } else { |
188 | | - // Add children. Only do this if there is no content property |
189 | | - for ( i = 0; i < obj.children.length; i++ ) { |
190 | | - //TODO figure out if all this concatting is inefficent. I think it is |
191 | | - data = data.concat( flattenNode( obj.children[i] ) ); |
192 | | - } |
193 | | - } |
194 | | - // Close element // TODO do we need attributes here or not? |
195 | | - data.push( { 'type': '/' + obj.type, 'node': null } ); |
196 | | - return data; |
197 | | - } |
198 | | - |
199 | | - return new es.DocumentModel( flattenNode( obj ) ); |
| 253 | + return new es.DocumentModel( es.DocumentModel.flattenPlainObjectNode( obj ) ); |
200 | 254 | }; |
201 | 255 | |
202 | 256 | /* |
Index: trunk/parsers/wikidom/lib/hype/bases/es.DocumentModelNode.js |
— | — | @@ -20,10 +20,6 @@ |
21 | 21 | return this.contentLength; |
22 | 22 | }; |
23 | 23 | |
24 | | -es.DocumentModelNode.getElementLength = function() { |
25 | | - return this.contentLength + 2; |
26 | | -}; |
27 | | - |
28 | 24 | /* Inheritance */ |
29 | 25 | |
30 | 26 | es.extend( es.DocumentModelNode, es.ModelNode ); |