* Moved getContent and getText from leaf nodes to document model nodes

* Renamed getContent to getContentData
* Renamed getText to getContentText
* Added getElementData
This commit is contained in:
Trevor Parscal 2011-12-05 19:41:04 +00:00
parent 4bcc31846b
commit 07af0cab63
7 changed files with 112 additions and 72 deletions

View file

@ -34,50 +34,10 @@ es.DocumentModelLeafNode.prototype.getPlainObject = function() {
if ( this.element && this.element.attributes ) {
obj.attributes = es.copyObject( this.element.attributes );
}
obj.content = es.DocumentModel.getExpandedContentData( this.getContent() );
obj.content = es.DocumentModel.getExpandedContentData( this.getContentData() );
return obj;
};
/**
* Gets the content length.
*
* FIXME: This method makes assumptions that a node with a data property is a DocumentModel, which
* may be an issue if sub-classes of DocumentModelLeafNode other than DocumentModel have a data property
* as well. A safer way of determining this would be helpful in preventing future bugs.
*
* @method
* @param {es.Range} [range] Range of content to get
* @returns {Integer} Length of content
*/
es.DocumentModelLeafNode.prototype.getContent = function( range ) {
// Find root
var root = this.data ? this : ( this.root && this.root.data ? this.root : null );
if ( root ) {
return root.getContentFromNode( this, range );
}
return [];
};
/**
* Gets plain text version of the content within a specific range.
*
* @method
* @param {es.Range} [range] Range of text to get
* @returns {String} Text within given range
*/
es.DocumentModelLeafNode.prototype.getText = function( range ) {
var content = this.getContent( range );
// Copy characters
var text = '';
for ( var i = 0, length = content.length; i < length; i++ ) {
// If not using in IE6 or IE7 (which do not support array access for strings) use this..
// text += this.data[i][0];
// Otherwise use this...
text += typeof content[i] === 'string' ? content[i] : content[i][0];
}
return text;
};
/* Inheritance */
es.extendClass( es.DocumentModelLeafNode, es.DocumentLeafNode );

View file

@ -218,6 +218,71 @@ es.DocumentModelNode.prototype.getElementAttribute = function( key ) {
return null;
};
/**
* Gets all element data, including the element opening, closing and it's contents.
*
* @method
* @returns {Array} Element data
*/
es.DocumentModelNode.prototype.getElementData = function() {
// Get reference to the document, which might be this node but otherwise should be this.root
var root = this.type === 'document' ?
this : ( this.root && this.root.type === 'document' ? this.root : null );
if ( root ) {
return root.getElementDataFromNode( this );
}
return [];
};
/**
* Gets content data within a given range.
*
* @method
* @param {es.Range} [range] Range of content to get
* @returns {Array} Content data
*/
es.DocumentModelNode.prototype.getContentData = function( range ) {
// Get reference to the document, which might be this node but otherwise should be this.root
var root = this.type === 'document' ?
this : ( this.root && this.root.type === 'document' ? this.root : null );
if ( root ) {
return root.getContentDataFromNode( this, range );
}
return [];
};
/**
* Gets plain text version of the content within a specific range.
*
* Two newlines are inserted between leaf nodes.
*
* TODO: Maybe do something more adaptive with newlines
*
* @method
* @param {es.Range} [range] Range of text to get
* @returns {String} Text within given range
*/
es.DocumentModelNode.prototype.getContentText = function( range ) {
var content = this.getContentData( range );
// Copy characters
var text = '',
element = false;
for ( var i = 0, length = content.length; i < length; i++ ) {
if ( typeof content[i] === 'object' ) {
if ( i ) {
element = true;
}
} else {
if ( element ) {
text += '\n\n';
element = false;
}
text += typeof content[i] === 'string' ? content[i] : content[i][0];
}
}
return text;
};
/* Inheritance */
es.extendClass( es.DocumentModelNode, es.DocumentNode );

View file

@ -612,6 +612,21 @@ es.DocumentModel.prototype.getElementFromNode = function( node ) {
return null;
};
/**
* Gets the element data of a node.
*
* @method
* @param {es.DocumentModelNode} node Node to get element data for
*/
es.DocumentModel.prototype.getContentDataFromNode = function( node ) {
var length = node.getElementLength();
var offset = this.getOffsetFromNode( node );
if ( offset !== -1 ) {
return this.data.slice( offset, offset + length );
}
return null;
};
/**
* Gets the content data of a node.
*
@ -619,7 +634,7 @@ es.DocumentModel.prototype.getElementFromNode = function( node ) {
* @param {es.DocumentModelNode} node Node to get content data for
* @returns {Array|null} List of content and elements inside node or null if node is not found
*/
es.DocumentModel.prototype.getContentFromNode = function( node, range ) {
es.DocumentModel.prototype.getContentDataFromNode = function( node, range ) {
var length = node.getContentLength();
if ( range ) {
range.normalize();
@ -1201,7 +1216,7 @@ es.DocumentModel.prototype.prepareLeafConversion = function( range, type, attrib
txs.push( this.prepareInsertion(
nodeOffset,
[ { 'type': type, 'attributes': attributes } ]
.concat( nodes[i].getContent() )
.concat( nodes[i].getContentData() )
.concat( [ { 'type': '/' + type } ] )
) );
}

View file

@ -500,7 +500,7 @@ es.ContentView.prototype.scanBoundaries = function() {
* the words.
*/
// Get and cache a copy of all content, the make a plain-text version of the cached content
var data = this.contentCache = this.model.getContent(),
var data = this.contentCache = this.model.getContentData(),
text = '';
for ( var i = 0, length = data.length; i < length; i++ ) {
text += typeof data[i] === 'string' ? data[i] : data[i][0];
@ -712,7 +712,7 @@ es.ContentView.prototype.appendLine = function( range, wordOffset, fractional )
$line[0].innerHTML = this.getHtml( range );
// Overwrite/append line information
this.lines[rs.line] = {
'text': this.model.getText( range ),
'text': this.model.getContentText( range ),
'range': range,
'width': $line.outerWidth(),
'height': $line.outerHeight(),

View file

@ -589,7 +589,7 @@ es.SurfaceView.prototype.handleDelete = function( backspace ) {
this.model.transact( tx, true );
} else {
tx = this.model.getDocument().prepareInsertion(
targetOffset, sourceNode.model.getContent()
targetOffset, sourceNode.model.getContentData()
);
this.model.transact( tx, true );

View file

@ -85,50 +85,50 @@ test( 'es.DocumentModel.getRelativeContentOffset', 7, function() {
);
} );
test( 'es.DocumentModel.getContent', 6, function() {
test( 'es.DocumentModel.getContentData', 6, function() {
var documentModel = es.DocumentModel.newFromPlainObject( esTest.obj ),
childNodes = documentModel.getChildren();
// Test 1
deepEqual(
childNodes[0].getContent( new es.Range( 1, 3 ) ),
childNodes[0].getContentData( new es.Range( 1, 3 ) ),
[
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
],
'getContent can return an ending portion of the content'
'getContentData can return an ending portion of the content'
);
// Test 2
deepEqual(
childNodes[0].getContent( new es.Range( 0, 2 ) ),
childNodes[0].getContentData( new es.Range( 0, 2 ) ),
['a', ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }]],
'getContent can return a beginning portion of the content'
'getContentData can return a beginning portion of the content'
);
// Test 3
deepEqual(
childNodes[0].getContent( new es.Range( 1, 2 ) ),
childNodes[0].getContentData( new es.Range( 1, 2 ) ),
[['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }]],
'getContent can return a middle portion of the content'
'getContentData can return a middle portion of the content'
);
// Test 4
try {
childNodes[0].getContent( new es.Range( -1, 3 ) );
childNodes[0].getContentData( new es.Range( -1, 3 ) );
} catch ( negativeIndexError ) {
ok( true, 'getContent throws exceptions when given a range with start < 0' );
ok( true, 'getContentData throws exceptions when given a range with start < 0' );
}
// Test 5
try {
childNodes[0].getContent( new es.Range( 0, 4 ) );
childNodes[0].getContentData( new es.Range( 0, 4 ) );
} catch ( outOfRangeError ) {
ok( true, 'getContent throws exceptions when given a range with end > length' );
ok( true, 'getContentData throws exceptions when given a range with end > length' );
}
// Test 6
deepEqual( childNodes[2].getContent(), ['h'], 'Content can be extracted from nodes' );
deepEqual( childNodes[2].getContentData(), ['h'], 'Content can be extracted from nodes' );
} );
test( 'es.DocumentModel.getIndexOfAnnotation', 3, function() {

View file

@ -94,7 +94,7 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 6
deepEqual(
documentModel.getChildren()[0].getContent(),
documentModel.getChildren()[0].getContentData(),
[
'a',
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
@ -120,7 +120,7 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 8
deepEqual(
documentModel.getChildren()[0].getContent(),
documentModel.getChildren()[0].getContentData(),
[
'a',
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
@ -145,7 +145,7 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 10
deepEqual(
documentModel.getChildren()[0].getContent(),
documentModel.getChildren()[0].getContentData(),
['a'],
'commit keeps model tree up to date with removals'
);
@ -166,7 +166,7 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 12
deepEqual(
documentModel.getChildren()[0].getContent(),
documentModel.getChildren()[0].getContentData(),
[
'a',
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
@ -197,14 +197,14 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 14
deepEqual(
documentModel.getChildren()[0].getContent(),
documentModel.getChildren()[0].getContentData(),
['a'],
'commit keeps model tree up to date with paragraph split (paragraph 1)'
);
// Test 15
deepEqual(
documentModel.getChildren()[1].getContent(),
documentModel.getChildren()[1].getContentData(),
[
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
@ -228,7 +228,7 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 17
deepEqual(
documentModel.getChildren()[0].getContent(),
documentModel.getChildren()[0].getContentData(),
[
'a',
['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
@ -272,21 +272,21 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 21
deepEqual(
documentModel.children[1].children[0].children[0].children[1].children[0].children[0].getContent(),
documentModel.children[1].children[0].children[0].children[1].children[0].children[0].getContentData(),
[ 'f' ],
'removal keeps model tree up to date with list item merge (first list item)'
);
// Test 22
deepEqual(
documentModel.children[1].children[0].children[0].children[1].children[1].children[0].getContent(),
documentModel.children[1].children[0].children[0].children[1].children[1].children[0].getContentData(),
[ 'g' ],
'removal keeps model tree up to date with list item merge (second list item)'
);
// Test 23
deepEqual(
documentModel.children[2].getContent(),
documentModel.children[2].getContentData(),
[ 'h' ],
'rollback keeps model tree up to date with list item split (final paragraph)'
);
@ -322,28 +322,28 @@ test( 'es.TransactionProcessor', 29, function() {
// Test 26
deepEqual(
documentModel.children[1].children[0].children[0].children[1].children[0].children[0].getContent(),
documentModel.children[1].children[0].children[0].children[1].children[0].children[0].getContentData(),
[ 'e' ],
'rollback keeps model tree up to date with list item split (first list item)'
);
// Test 27
deepEqual(
documentModel.children[1].children[0].children[0].children[1].children[1].children[0].getContent(),
documentModel.children[1].children[0].children[0].children[1].children[1].children[0].getContentData(),
[ 'f' ],
'rollback keeps model tree up to date with list item split (second list item)'
);
// Test 28
deepEqual(
documentModel.children[1].children[0].children[0].children[1].children[2].children[0].getContent(),
documentModel.children[1].children[0].children[0].children[1].children[2].children[0].getContentData(),
[ 'g' ],
'rollback keeps model tree up to date with list item split (third list item)'
);
// Test 29
deepEqual(
documentModel.children[2].getContent(),
documentModel.children[2].getContentData(),
[ 'h' ],
'rollback keeps model tree up to date with list item split (final paragraph)'
);