Index: trunk/extensions/VisualEditor/modules/es/es.js |
— | — | @@ -44,6 +44,8 @@ |
45 | 45 | |
46 | 46 | es.isPlainObject = $.isPlainObject; |
47 | 47 | |
| 48 | +es.isEmptyObject = $.isEmptyObject; |
| 49 | + |
48 | 50 | es.isArray = $.isArray; |
49 | 51 | |
50 | 52 | /** |
Index: trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js |
— | — | @@ -12,7 +12,7 @@ |
13 | 13 | */ |
14 | 14 | es.DocumentModel = function( data, attributes ) { |
15 | 15 | // Inheritance |
16 | | - es.DocumentModelNode.call( this, null, data ? data.length : 0 ); |
| 16 | + es.DocumentModelNode.call( this, 'document', null, data ? data.length : 0 ); |
17 | 17 | |
18 | 18 | // Properties |
19 | 19 | this.data = es.isArray( data ) ? data : []; |
— | — | @@ -483,6 +483,83 @@ |
484 | 484 | }; |
485 | 485 | |
486 | 486 | /** |
| 487 | + * Get a plain object representation of content data. |
| 488 | + * |
| 489 | + * @method |
| 490 | + * @returns {Object} Plain object representation |
| 491 | + */ |
| 492 | +es.DocumentModel.expandContentData = function( data ) { |
| 493 | + var stack = []; |
| 494 | + // Text and annotations |
| 495 | + function start( offset, annotation ) { |
| 496 | + stack.push( es.extendObject( true, {}, annotation, { 'range': { 'start': offset } } ) ); |
| 497 | + } |
| 498 | + function end( offset, annotation ) { |
| 499 | + for ( var i = stack.length - 1; i >= 0; i-- ) { |
| 500 | + if ( !stack[i].range.end ) { |
| 501 | + if ( annotation ) { |
| 502 | + if ( stack[i].type === annotation.type && |
| 503 | + es.compareObjects( stack[i].data, annotation.data ) ) { |
| 504 | + stack[i].range.end = offset; |
| 505 | + break; |
| 506 | + } |
| 507 | + } else { |
| 508 | + stack[i].range.end = offset; |
| 509 | + } |
| 510 | + } |
| 511 | + } |
| 512 | + } |
| 513 | + var left = '', |
| 514 | + right, |
| 515 | + leftPlain, |
| 516 | + rightPlain, |
| 517 | + obj = { 'text': '' }, |
| 518 | + offset = 0, |
| 519 | + i, |
| 520 | + j; |
| 521 | + for ( i = 0; i < data.length; i++ ) { |
| 522 | + right = data[i]; |
| 523 | + leftPlain = typeof left === 'string'; |
| 524 | + rightPlain = typeof right === 'string'; |
| 525 | + // Open or close annotations |
| 526 | + if ( !leftPlain && rightPlain ) { |
| 527 | + // [formatted][plain] pair, close any annotations for left |
| 528 | + end( i - offset ); |
| 529 | + } else if ( leftPlain && !rightPlain ) { |
| 530 | + // [plain][formatted] pair, open any annotations for right |
| 531 | + for ( j = 1; j < right.length; j++ ) { |
| 532 | + start( i - offset, right[j] ); |
| 533 | + } |
| 534 | + } else if ( !leftPlain && !rightPlain ) { |
| 535 | + // [formatted][formatted] pair, open/close any differences |
| 536 | + for ( j = 1; j < left.length; j++ ) { |
| 537 | + if ( es.DocumentModel.getIndexOfAnnotation( data[i] , left[j], true ) === -1 ) { |
| 538 | + end( i - offset, left[j] ); |
| 539 | + } |
| 540 | + } |
| 541 | + for ( j = 1; j < right.length; j++ ) { |
| 542 | + if ( es.DocumentModel.getIndexOfAnnotation( data[i - 1], right[j], true ) === -1 ) { |
| 543 | + start( i - offset, right[j] ); |
| 544 | + } |
| 545 | + } |
| 546 | + } |
| 547 | + obj.text += rightPlain ? right : right[0]; |
| 548 | + left = right; |
| 549 | + } |
| 550 | + if ( data.length ) { |
| 551 | + end( i - offset ); |
| 552 | + } |
| 553 | + if ( stack.length ) { |
| 554 | + obj.annotation = stack; |
| 555 | + } |
| 556 | + // Copy attributes if there are any set |
| 557 | + if ( !es.isEmptyObject( this.attributes ) ) { |
| 558 | + obj.attributes = es.extendObject( true, {}, this.attributes ); |
| 559 | + } |
| 560 | + return obj; |
| 561 | +}; |
| 562 | + |
| 563 | +/** |
487 | 564 | * Checks if a data at a given offset is content. |
488 | 565 | * |
489 | 566 | * @example Content data: |
Index: trunk/extensions/VisualEditor/modules/es/models/es.HeadingModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.HeadingModel = function( element, length ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, length ); |
| 13 | + es.DocumentModelNode.call( this, 'heading', element, length ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/models/es.TableRowModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.TableRowModel = function( element, contents ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, contents ); |
| 13 | + es.DocumentModelNode.call( this, 'tableRow', element, contents ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/models/es.ParagraphModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.ParagraphModel = function( element, length ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, length ); |
| 13 | + es.DocumentModelNode.call( this, 'paragraph', element, length ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/models/es.TableCellModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.TableCellModel = function( element, contents ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, contents ); |
| 13 | + es.DocumentModelNode.call( this, 'tableCell', element, contents ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/models/es.TableModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.TableModel = function( element, contents ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, contents ); |
| 13 | + es.DocumentModelNode.call( this, 'table', element, contents ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/models/es.ListItemModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.ListItemModel = function( element, length ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, length ); |
| 13 | + es.DocumentModelNode.call( this, 'listItem', element, length ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/models/es.ListModel.js |
— | — | @@ -9,7 +9,7 @@ |
10 | 10 | */ |
11 | 11 | es.ListModel = function( element, contents ) { |
12 | 12 | // Inheritance |
13 | | - es.DocumentModelNode.call( this, element, contents ); |
| 13 | + es.DocumentModelNode.call( this, 'list', element, contents ); |
14 | 14 | }; |
15 | 15 | |
16 | 16 | /* Methods */ |
Index: trunk/extensions/VisualEditor/modules/es/bases/es.Document.js |
— | — | @@ -1,82 +0,0 @@ |
2 | | -/** |
3 | | - * Ordered collection of blocks. |
4 | | - * |
5 | | - * @class |
6 | | - * @constructor |
7 | | - * @extends {es.EventEmitter} |
8 | | - * @param blocks {Array} List of blocks |
9 | | - * @property blocks {Array} |
10 | | - * @property width {Integer} |
11 | | - */ |
12 | | -es.Document = function( blocks ) { |
13 | | - es.DomContainer.call( this, 'document', 'blocks', blocks ); |
14 | | - this.width = null; |
15 | | -}; |
16 | | - |
17 | | -/* Static Members */ |
18 | | - |
19 | | -/** |
20 | | - * List of registered document serializers. |
21 | | - */ |
22 | | -es.Document.serializers = {}; |
23 | | - |
24 | | -/* Static Methods */ |
25 | | - |
26 | | -/** |
27 | | - * Creates new es.Document from a WikiDom Document object |
28 | | - * |
29 | | - * @method |
30 | | - * @param {Object} WikiDom document object |
31 | | - * @returns {es.Document} EditSurface document object |
32 | | - */ |
33 | | -es.Document.newFromWikiDomDocument = function( wikidomDocument ) { |
34 | | - var blocks = []; |
35 | | - if ( $.isArray( wikidomDocument.blocks ) ) { |
36 | | - for ( var i = 0; i < wikidomDocument.blocks.length; i++ ) { |
37 | | - blocks.push( es.Block.newFromWikiDomBlock( wikidomDocument.blocks[i] ) ); |
38 | | - } |
39 | | - } |
40 | | - return new es.Document( blocks ); |
41 | | -}; |
42 | | - |
43 | | -/* Methods */ |
44 | | - |
45 | | -es.Document.prototype.serialize = function( serializer, context, options ) { |
46 | | - if ( serializer in es.Document.serializers ) { |
47 | | - return es.Document.serializers[serializer]( this.getWikiDomDocument(), context, options ); |
48 | | - } |
49 | | -}; |
50 | | - |
51 | | -es.Document.prototype.getSerializers = function() { |
52 | | - return es.Document.serializers; |
53 | | -}; |
54 | | - |
55 | | -/** |
56 | | - * Forces all blocks in the document to render. |
57 | | - * |
58 | | - * @method |
59 | | - */ |
60 | | -es.Document.prototype.renderBlocks = function() { |
61 | | - // Bypass rendering when width has not changed |
62 | | - var width = this.$.innerWidth(); |
63 | | - if ( this.width === width ) { |
64 | | - return; |
65 | | - } |
66 | | - this.width = width; |
67 | | - // Render blocks |
68 | | - for ( var i = 0; i < this.blocks.length; i++ ) { |
69 | | - this.blocks[i].renderContent(); |
70 | | - } |
71 | | -}; |
72 | | - |
73 | | -es.Document.prototype.getWikiDomDocument = function() { |
74 | | - var wikidom = { blocks: [ ] }; |
75 | | - for ( var i = 0; i < this.blocks.length; i++ ) { |
76 | | - wikidom.blocks.push( this.blocks[i].getWikiDom() ); |
77 | | - } |
78 | | - return wikidom; |
79 | | -}; |
80 | | - |
81 | | -/* Inheritance */ |
82 | | - |
83 | | -$.extend( es, es.Document, es.DomContainer ); |
Index: trunk/extensions/VisualEditor/modules/es/bases/es.Document.Serializer.js |
— | — | @@ -1,69 +0,0 @@ |
2 | | -/** |
3 | | - * Creates content serializer. |
4 | | - * |
5 | | - * Base object for all serializers, providing basic shared functionality and stubs for required |
6 | | - * implementations. |
7 | | - * |
8 | | - * @class |
9 | | - * @constructor |
10 | | - * @param context {es.WikiContext} Context of the wiki the document is a part of |
11 | | - * @property context {es.WikiContext} Context of the wiki the document is a part of |
12 | | - */ |
13 | | -es.Document.Serializer = function( context ) { |
14 | | - this.context = context; |
15 | | -}; |
16 | | - |
17 | | -/* Static Methods */ |
18 | | - |
19 | | -es.Document.Serializer.repeatString = function( pattern, count ) { |
20 | | - if ( count < 1 ) { |
21 | | - return ''; |
22 | | - } |
23 | | - var result = ''; |
24 | | - while ( count > 0 ) { |
25 | | - if ( count & 1 ) { result += pattern; } |
26 | | - count >>= 1; |
27 | | - pattern += pattern; |
28 | | - } |
29 | | - return result; |
30 | | -}; |
31 | | - |
32 | | -es.Document.Serializer.escapeXmlText = function( text ) { |
33 | | - return text |
34 | | - .replace( /&/g, '&' ) |
35 | | - .replace( /</g, '<' ) |
36 | | - .replace( />/g, '>' ) |
37 | | - .replace( /"/g, '"' ) |
38 | | - .replace( /'/g, ''' ); |
39 | | -}; |
40 | | - |
41 | | -es.Document.Serializer.buildXmlAttributes = function( attributes, prespace ) { |
42 | | - var attr = []; |
43 | | - var name; |
44 | | - if ( attributes ) { |
45 | | - for ( name in attributes ) { |
46 | | - attr.push( name + '="' + attributes[name] + '"' ); |
47 | | - } |
48 | | - } |
49 | | - return ( prespace && attr.length ? ' ' : '' ) + attr.join( ' ' ); |
50 | | -}; |
51 | | - |
52 | | -es.Document.Serializer.buildXmlOpeningTag = function( tag, attributes ) { |
53 | | - return '<' + tag + es.Document.Serializer.buildXmlAttributes( attributes, true ) + '>'; |
54 | | -}; |
55 | | - |
56 | | -es.Document.Serializer.buildXmlClosingTag = function( tag ) { |
57 | | - return '</' + tag + '>'; |
58 | | -}; |
59 | | - |
60 | | -es.Document.Serializer.buildXmlTag = function( tag, attributes, value, escape ) { |
61 | | - if ( value === false ) { |
62 | | - return '<' + tag + es.Document.Serializer.buildXmlAttributes( attributes, true ) + ' />'; |
63 | | - } else { |
64 | | - if ( escape ) { |
65 | | - value = wiki.util.xml.esc( value ); |
66 | | - } |
67 | | - return '<' + tag + es.Document.Serializer.buildXmlAttributes( attributes, true ) + '>' + |
68 | | - value + '</' + tag + '>'; |
69 | | - } |
70 | | -}; |
Index: trunk/extensions/VisualEditor/modules/es/bases/es.DocumentModelNode.js |
— | — | @@ -5,13 +5,14 @@ |
6 | 6 | * nodes to be used as nodes in a space partitioning tree. |
7 | 7 | * |
8 | 8 | * @class |
| 9 | + * @abstract |
9 | 10 | * @constructor |
10 | 11 | * @extends {es.DocumentNode} |
11 | 12 | * @extends {es.EventEmitter} |
12 | 13 | * @param {Integer|Array} contents Either Length of content or array of child nodes to append |
13 | 14 | * @property {Integer} contentLength Length of content |
14 | 15 | */ |
15 | | -es.DocumentModelNode = function( element, contents ) { |
| 16 | +es.DocumentModelNode = function( type, element, contents ) { |
16 | 17 | // Inheritance |
17 | 18 | es.DocumentNode.call( this ); |
18 | 19 | es.EventEmitter.call( this ); |
— | — | @@ -21,8 +22,9 @@ |
22 | 23 | this.emitUpdate = function() { |
23 | 24 | _this.emit( 'update' ); |
24 | 25 | }; |
25 | | - |
| 26 | + |
26 | 27 | // Properties |
| 28 | + this.type = type; |
27 | 29 | this.parent = null; |
28 | 30 | this.root = this; |
29 | 31 | this.element = element || null; |
— | — | @@ -55,6 +57,30 @@ |
56 | 58 | /* Methods */ |
57 | 59 | |
58 | 60 | /** |
| 61 | + * Gets a plain object representation of the document's data. |
| 62 | + * |
| 63 | + * The resulting object is compatible with es.DocumentModel.newFromPlainObject. |
| 64 | + * |
| 65 | + * @method |
| 66 | + * @returns {Object} Plain object representation |
| 67 | + */ |
| 68 | +es.DocumentModelNode.prototype.getPlainObject = function() { |
| 69 | + var obj = { 'type': this.type }; |
| 70 | + if ( this.element && this.element.attributes ) { |
| 71 | + obj.attributes = es.copyObject( this.element.attributes ); |
| 72 | + } |
| 73 | + if ( this.children.length ) { |
| 74 | + obj.children = []; |
| 75 | + for ( var i = 0; i < this.children.length; i++ ) { |
| 76 | + obj.children.push( this.children[i].getPlainObject() ); |
| 77 | + } |
| 78 | + } else if ( this.getContentLength() ) { |
| 79 | + obj.content = es.DocumentModel.expandContentData( this.getContent() ); |
| 80 | + } |
| 81 | + return obj; |
| 82 | +}; |
| 83 | + |
| 84 | +/** |
59 | 85 | * Adds a node to the end of this node's children. |
60 | 86 | * |
61 | 87 | * @method |
— | — | @@ -370,7 +396,7 @@ |
371 | 397 | * @returns {Mixed} Value of attribute, or null if no such attribute exists |
372 | 398 | */ |
373 | 399 | es.DocumentModelNode.prototype.getElementAttribute = function( key ) { |
374 | | - if ( this.element.attributes && key in this.element.attributes ) { |
| 400 | + if ( this.element && this.element.attributes && key in this.element.attributes ) { |
375 | 401 | return this.element.attributes[key]; |
376 | 402 | } |
377 | 403 | return null; |