r98585 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r98584‎ | r98585 | r98586 >
Date:23:43, 30 September 2011
Author:tparscal
Status:deferred
Tags:
Comment:
Added tests and fixed some bits of code in es.DocumentModel
Modified paths:
  • /trunk/parsers/wikidom/lib/hype/models/es.DocumentModel.js (modified) (history)
  • /trunk/parsers/wikidom/tests/hype/es.DocumentModel.js (deleted) (history)
  • /trunk/parsers/wikidom/tests/hype/es.DocumentModel.test.js (added) (history)
  • /trunk/parsers/wikidom/tests/hype/es.ModelNode.test.js (modified) (history)
  • /trunk/parsers/wikidom/tests/hype/index.html (modified) (history)

Diff [purge]

Index: trunk/parsers/wikidom/tests/hype/es.DocumentModel.js
@@ -1,176 +0,0 @@
2 -/*
3 - * Sample plain object (WikiDom).
4 - *
5 - * There are two kinds of nodes in WikiDom:
6 - *
7 - * {Object} ElementNode
8 - * type: {String} Symbolic node type name
9 - * [attributes]: {Object} List of symbolic attribute name and literal value pairs
10 - * [content]: {Object} Content node (not defined if node has children)
11 - * [children]: {Object[]} Child nodes (not defined if node has content)
12 - *
13 - * {Object} ContentNode
14 - * text: {String} Plain text data of content
15 - * [annotations]: {Object[]} List of annotation objects that can be used to render text
16 - * type: {String} Symbolic name of annotation type
17 - * start: {Integer} Offset within text to begin annotation
18 - * end: {Integer} Offset within text to end annotation
19 - * [data]: {Object} Additional information, only used by more complex annotations
20 - */
21 -var obj = {
22 - 'type': 'document',
23 - 'children': [
24 - {
25 - 'type': 'paragraph',
26 - 'content': {
27 - 'text': 'abc',
28 - 'annotations': [
29 - {
30 - 'type': 'bold',
31 - 'start': 1,
32 - 'end': 2
33 - },
34 - {
35 - 'type': 'italic',
36 - 'start': 2,
37 - 'end': 3
38 - }
39 - ]
40 - }
41 - },
42 - {
43 - 'type': 'table',
44 - 'children': [
45 - {
46 - 'type': 'row',
47 - 'children': [
48 - {
49 - 'type': 'cell',
50 - 'children': [
51 - {
52 - 'type': 'paragraph',
53 - 'content': {
54 - 'text': 'a'
55 - }
56 - },
57 - {
58 - 'type': 'list',
59 - 'children': [
60 - {
61 - 'type': 'listItem',
62 - 'attributes': {
63 - 'styles': ['bullet']
64 - }
65 - 'content': {
66 - 'text': 'a'
67 - }
68 - },
69 - {
70 - 'type': 'listItem',
71 - 'attributes': {
72 - 'styles': ['bullet', 'bullet']
73 - }
74 - 'content': {
75 - 'text': 'b'
76 - }
77 - },
78 - {
79 - 'type': 'listItem',
80 - 'attributes': {
81 - 'styles': ['number']
82 - }
83 - 'content': {
84 - 'text': 'c'
85 - }
86 - },
87 - ]
88 - }
89 - ]
90 - }
91 - ]
92 - }
93 - ]
94 - },
95 - {
96 - 'type': 'paragraph',
97 - 'content': {
98 - 'text': 'a'
99 - }
100 - },
101 - ]
102 -};
103 -
104 -/*
105 - * Sample content data.
106 - *
107 - * There are three types of components in content data:
108 - *
109 - * {String} Plain text character
110 - *
111 - * {Array} Annotated character
112 - * {String} Character
113 - * {Object}... List of annotation object references
114 - *
115 - * {Object} Opening or closing structural element
116 - * type: {String} Symbolic node type name, if closing element first character will be "/"
117 - * node: {Object} Reference to model tree node
118 - * [attributes]: {Object} List of symbolic attribute name and literal value pairs
119 - */
120 -var data = [
121 - // 0 - Beginning of paragraph
122 - { 'type': 'paragraph', 'node': {} },
123 - // 1 - Plain content
124 - 'a',
125 - // 2 - Annotated content
126 - ['b', { 'type': 'bold' }],
127 - // 3 - Annotated content
128 - ['c', { 'type': 'italic' }],
129 - // 4 - End of paragraph
130 - { 'type': '/paragraph', 'node': {} }
131 - // 5 - Beginning of table
132 - { 'type': 'table', 'node': {} },
133 - // 6 - Beginning of row
134 - { 'type': 'row', 'node': {} },
135 - // 7 - Beginning of cell
136 - { 'type': 'cell', 'node': {} },
137 - // 8 - Beginning of paragraph
138 - { 'type': 'paragraph', 'node': {} },
139 - // 9 - Plain content
140 - 'a',
141 - // 10 - End of paragraph
142 - { 'type': '/paragraph', 'node': {} },
143 - // 11 - Beginning of list
144 - { 'type': 'list', 'node': {} },
145 - // 12 - Beginning of bullet list item
146 - { 'type': 'listItem', 'attributes': { 'styles': ['bullet'] }, 'node': {} },
147 - // 13 - Plain content
148 - 'a',
149 - // 14 - End of item
150 - { 'type': '/listItem', 'node': {} },
151 - // 15 - Beginning of nested bullet list item
152 - { 'type': 'listItem', 'attributes': { 'styles': ['bullet', 'bullet'] }, 'node': {} },
153 - // 16 - Plain content
154 - 'b',
155 - // 17 - End of item
156 - { 'type': '/listItem', 'node': {} },
157 - // 18 - Beginning of numbered list item
158 - { 'type': 'listItem', 'attributes': { 'styles': ['number'] }, 'node': {} },
159 - // 19 - Plain content
160 - 'c',
161 - // 20 - End of item
162 - { 'type': '/listItem', 'node': {} },
163 - // 21 - End of list
164 - { 'type': '/list', 'node': {} },
165 - // 22 - End of cell
166 - { 'type': '/cell', 'node': {} }
167 - // 23 - End of row
168 - { 'type': '/row', 'node': {} }
169 - // 24 - End of table
170 - { 'type': '/table', 'node': {} }
171 - // 25 - Beginning of paragraph
172 - { 'type': 'paragraph', 'node': {} },
173 - // 26 - Plain content
174 - 'a'
175 - // 27 - End of paragraph
176 - { 'type': '/paragraph', 'node': {} },
177 -];
Index: trunk/parsers/wikidom/tests/hype/es.ModelNode.test.js
@@ -1,4 +1,4 @@
2 -module( 'Base classes' );
 2+module( 'Bases' );
33
44 test( 'es.ModelNode', 17, function() {
55 // Example data (integers) is used for simplicity of testing
Index: trunk/parsers/wikidom/tests/hype/index.html
@@ -10,13 +10,16 @@
1111 <h2 id="qunit-banner"></h2>
1212 <h2 id="qunit-userAgent"></h2>
1313 <ol id="qunit-tests"></ol>
 14+ <script src="../../lib/jquery.js"></script>
 15+ <script src="../../lib/qunit.js"></script>
1416 <script src="../../lib/hype/es.js"></script>
1517 <script src="../../lib/synth/bases/es.AggregateArray.js"></script>
1618 <script src="../../lib/hype/bases/es.EventEmitter.js"></script>
1719 <script src="../../lib/hype/bases/es.ModelNode.js"></script>
1820 <script src="../../lib/hype/bases/es.ViewNode.js"></script>
19 - <script src="../../lib/jquery.js"></script>
20 - <script src="../../lib/qunit.js"></script>
 21+ <script src="../../lib/hype/bases/es.DocumentModelNode.js"></script>
 22+ <script src="../../lib/hype/models/es.DocumentModel.js"></script>
2123 <script src="es.ModelNode.test.js"></script>
 24+ <script src="es.DocumentModel.test.js"></script>
2225 </body>
2326 </html>
Index: trunk/parsers/wikidom/tests/hype/es.DocumentModel.test.js
@@ -0,0 +1,188 @@
 2+module( 'Models' );
 3+
 4+/*
 5+ * Sample plain object (WikiDom).
 6+ *
 7+ * There are two kinds of nodes in WikiDom:
 8+ *
 9+ * {Object} ElementNode
 10+ * type: {String} Symbolic node type name
 11+ * [attributes]: {Object} List of symbolic attribute name and literal value pairs
 12+ * [content]: {Object} Content node (not defined if node has children)
 13+ * [children]: {Object[]} Child nodes (not defined if node has content)
 14+ *
 15+ * {Object} ContentNode
 16+ * text: {String} Plain text data of content
 17+ * [annotations]: {Object[]} List of annotation objects that can be used to render text
 18+ * type: {String} Symbolic name of annotation type
 19+ * start: {Integer} Offset within text to begin annotation
 20+ * end: {Integer} Offset within text to end annotation
 21+ * [data]: {Object} Additional information, only used by more complex annotations
 22+ */
 23+var obj = {
 24+ 'type': 'document',
 25+ 'children': [
 26+ {
 27+ 'type': 'paragraph',
 28+ 'content': {
 29+ 'text': 'abc',
 30+ 'annotations': [
 31+ {
 32+ 'type': 'bold',
 33+ 'start': 1,
 34+ 'end': 2
 35+ },
 36+ {
 37+ 'type': 'italic',
 38+ 'start': 2,
 39+ 'end': 3
 40+ }
 41+ ]
 42+ }
 43+ },
 44+ {
 45+ 'type': 'table',
 46+ 'children': [
 47+ {
 48+ 'type': 'row',
 49+ 'children': [
 50+ {
 51+ 'type': 'cell',
 52+ 'children': [
 53+ {
 54+ 'type': 'paragraph',
 55+ 'content': {
 56+ 'text': 'a'
 57+ }
 58+ },
 59+ {
 60+ 'type': 'list',
 61+ 'children': [
 62+ {
 63+ 'type': 'listItem',
 64+ 'attributes': {
 65+ 'styles': ['bullet']
 66+ },
 67+ 'content': {
 68+ 'text': 'a'
 69+ }
 70+ },
 71+ {
 72+ 'type': 'listItem',
 73+ 'attributes': {
 74+ 'styles': ['bullet', 'bullet']
 75+ },
 76+ 'content': {
 77+ 'text': 'b'
 78+ }
 79+ },
 80+ {
 81+ 'type': 'listItem',
 82+ 'attributes': {
 83+ 'styles': ['number']
 84+ },
 85+ 'content': {
 86+ 'text': 'c'
 87+ }
 88+ }
 89+ ]
 90+ }
 91+ ]
 92+ }
 93+ ]
 94+ }
 95+ ]
 96+ },
 97+ {
 98+ 'type': 'paragraph',
 99+ 'content': {
 100+ 'text': 'a'
 101+ }
 102+ }
 103+ ]
 104+};
 105+
 106+/*
 107+ * Sample content data.
 108+ *
 109+ * There are three types of components in content data:
 110+ *
 111+ * {String} Plain text character
 112+ *
 113+ * {Array} Annotated character
 114+ * {String} Character
 115+ * {Object}... List of annotation object references
 116+ *
 117+ * {Object} Opening or closing structural element
 118+ * type: {String} Symbolic node type name, if closing element first character will be "/"
 119+ * node: {Object} Reference to model tree node
 120+ * [attributes]: {Object} List of symbolic attribute name and literal value pairs
 121+ */
 122+var data = [
 123+ // 0 - Beginning of paragraph
 124+ { 'type': 'document' },
 125+ // 0 - Beginning of paragraph
 126+ { 'type': 'paragraph' },
 127+ // 1 - Plain content
 128+ 'a',
 129+ // 2 - Annotated content
 130+ ['b', { 'type': 'bold' }],
 131+ // 3 - Annotated content
 132+ ['c', { 'type': 'italic' }],
 133+ // 4 - End of paragraph
 134+ { 'type': '/paragraph' },
 135+ // 5 - Beginning of table
 136+ { 'type': 'table' },
 137+ // 6 - Beginning of row
 138+ { 'type': 'row' },
 139+ // 7 - Beginning of cell
 140+ { 'type': 'cell' },
 141+ // 8 - Beginning of paragraph
 142+ { 'type': 'paragraph' },
 143+ // 9 - Plain content
 144+ 'a',
 145+ // 10 - End of paragraph
 146+ { 'type': '/paragraph' },
 147+ // 11 - Beginning of list
 148+ { 'type': 'list' },
 149+ // 12 - Beginning of bullet list item
 150+ { 'type': 'listItem', 'attributes': { 'styles': ['bullet'] } },
 151+ // 13 - Plain content
 152+ 'a',
 153+ // 14 - End of item
 154+ { 'type': '/listItem' },
 155+ // 15 - Beginning of nested bullet list item
 156+ { 'type': 'listItem', 'attributes': { 'styles': ['bullet', 'bullet'] } },
 157+ // 16 - Plain content
 158+ 'b',
 159+ // 17 - End of item
 160+ { 'type': '/listItem' },
 161+ // 18 - Beginning of numbered list item
 162+ { 'type': 'listItem', 'attributes': { 'styles': ['number'] } },
 163+ // 19 - Plain content
 164+ 'c',
 165+ // 20 - End of item
 166+ { 'type': '/listItem' },
 167+ // 21 - End of list
 168+ { 'type': '/list' },
 169+ // 22 - End of cell
 170+ { 'type': '/cell' },
 171+ // 23 - End of row
 172+ { 'type': '/row' },
 173+ // 24 - End of table
 174+ { 'type': '/table' },
 175+ // 25 - Beginning of paragraph
 176+ { 'type': 'paragraph' },
 177+ // 26 - Plain content
 178+ 'a',
 179+ // 27 - End of paragraph
 180+ { 'type': '/paragraph' },
 181+ // 27 - End of paragraph
 182+ { 'type': '/document' }
 183+];
 184+
 185+test( 'es.ModelNode', function() {
 186+ var documentModel = es.DocumentModel.newFromPlainObject( obj );
 187+
 188+ deepEqual( documentModel.getData(), data, 'Flattening plain objects results in correct data' );
 189+} );
Index: trunk/parsers/wikidom/lib/hype/models/es.DocumentModel.js
@@ -51,10 +51,75 @@
5252 * @returns {es.DocumentModel} Document model created from obj
5353 */
5454 es.DocumentModel.newFromPlainObject = function( obj ) {
55 - return new es.DocumentModel( es.DocumentModel.flattenPlainObjectNode( obj ) );
 55+ return new es.DocumentModel( es.DocumentModel.flattenPlainObjectElementNode( obj ) );
5656 };
5757
 58+
5859 /**
 60+ * Creates an es.ContentModel object from a plain content object.
 61+ *
 62+ * A plain content object contains plain text and a series of annotations to be applied to ranges of
 63+ * the text.
 64+ *
 65+ * @example
 66+ * {
 67+ * 'text': '1234',
 68+ * 'annotations': [
 69+ * // Makes "23" bold
 70+ * {
 71+ * 'type': 'bold',
 72+ * 'range': {
 73+ * 'start': 1,
 74+ * 'end': 3
 75+ * }
 76+ * }
 77+ * ]
 78+ * }
 79+ *
 80+ * @static
 81+ * @method
 82+ * @param {Object} obj Plain content object, containing a "text" property and optionally
 83+ * an "annotations" property, the latter of which being an array of annotation objects including
 84+ * range information
 85+ * @returns {Array}
 86+ */
 87+es.DocumentModel.flattenPlainObjectContentNode = function( obj ) {
 88+ if ( !$.isPlainObject( obj ) ) {
 89+ // Use empty content
 90+ return [];
 91+ } else {
 92+ // Convert string to array of characters
 93+ var data = obj.text.split('');
 94+ // Render annotations
 95+ if ( $.isArray( obj.annotations ) ) {
 96+ $.each( obj.annotations, function( i, src ) {
 97+ // Build simplified annotation object
 98+ var dst = { 'type': src.type };
 99+ if ( 'data' in src ) {
 100+ dst.data = es.copyObject( src.data );
 101+ }
 102+ // Apply annotation to range
 103+ if ( src.start < 0 ) {
 104+ // TODO: This is invalid data! Throw error?
 105+ src.start = 0;
 106+ }
 107+ if ( src.end > data.length ) {
 108+ // TODO: This is invalid data! Throw error?
 109+ src.end = data.length;
 110+ }
 111+ for ( var i = src.start; i < src.end; i++ ) {
 112+ // Auto-convert to array
 113+ typeof data[i] === 'string' && ( data[i] = [data[i]] );
 114+ // Append
 115+ data[i].push( dst );
 116+ }
 117+ } );
 118+ }
 119+ return data;
 120+ }
 121+};
 122+
 123+/**
59124 * Flatten a plain node object into a data array, recursively.
60125 *
61126 * TODO: where do we document this whole structure - aka "WikiDom"?
@@ -64,22 +129,27 @@
65130 * @param {Object} obj Plain node object to flatten
66131 * @returns {Array} Flattened version of obj
67132 */
68 -es.DocumentModel.flattenPlainObjectNode = function( obj ) {
69 - var i, data = [];
 133+es.DocumentModel.flattenPlainObjectElementNode = function( obj ) {
 134+ var i,
 135+ data = [],
 136+ element = { 'type': obj.type };
 137+ if ( $.isPlainObject( obj.attributes ) ) {
 138+ element.attributes = es.copyObject( obj.attributes );
 139+ }
70140 // Open element
71 - data.push( { 'type': obj.type, 'attributes': es.copyObject( obj.attributes ), 'node': null } );
72 - if ( obj.content !== undefined ) {
 141+ data.push( element );
 142+ if ( $.isPlainObject( obj.content ) ) {
73143 // Add content
74 - data = data.concat( es.ContentModel.newFromPlainObject( obj.content ).data );
75 - } else {
 144+ data = data.concat( es.DocumentModel.flattenPlainObjectContentNode( obj.content ) );
 145+ } else if ( $.isArray( obj.children ) ) {
76146 // Add children - only do this if there is no content property
77147 for ( i = 0; i < obj.children.length; i++ ) {
78148 // TODO: Figure out if all this concatenating is inefficient. I think it is
79 - data = data.concat( flattenNode( obj.children[i] ) );
 149+ data = data.concat( es.DocumentModel.flattenPlainObjectElementNode( obj.children[i] ) );
80150 }
81151 }
82152 // Close element - TODO: Do we need attributes here or not?
83 - data.push( { 'type': '/' + obj.type, 'node': null } );
 153+ data.push( { 'type': '/' + obj.type } );
84154 return data;
85155 };
86156

Status & tagging log