Merge "JSHint: Added dotfiles and fixed tons of linting warnings."

This commit is contained in:
Krinkle 2012-07-19 17:09:03 +00:00 committed by Gerrit Code Review
commit ab47265c34
33 changed files with 429 additions and 299 deletions

6
.jshintignore Normal file
View file

@ -0,0 +1,6 @@
api
modules/jquery
modules/qunit
modules/rangy
modules/parser
tests/parser

43
.jshintrc Normal file
View file

@ -0,0 +1,43 @@
{
"predef": [
"ve",
"Node",
"QUnit",
"test",
"raises",
"expect",
"ok",
"equal",
"notEqual",
"strictEqual",
"strictNotEqual",
"deepEqual",
"deepNotEqual"
],
"bitwise": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"regexp": true,
"undef": true,
"strict": false,
"trailing": true,
"smarttabs": true,
"multistr": true,
"browser": true,
"node": true,
"jquery": true,
"nomen": true,
"onevar": true
}

View file

@ -20,7 +20,7 @@ ve.ce.ImageNode = function( model ) {
this.model.addListenerMethod( this, 'update', 'onUpdate' ); this.model.addListenerMethod( this, 'update', 'onUpdate' );
/* /*
this.$.on('mousedown', function() { this.$.on('mousedown', function() {
return false; return false;
}); });
*/ */

View file

@ -135,6 +135,7 @@ ve.ce.TextNode.prototype.onUpdate = function( force ) {
throw 'Can not update a text node that is not attached to a document'; throw 'Can not update a text node that is not attached to a document';
} }
if ( force === true || this.root.getSurface().render === true ) { if ( force === true || this.root.getSurface().render === true ) {
// XXX: Why is this being passed to $(), creating a new jQuery object? krinkle 20120719
var $new = $( $( '<span>' + this.getHtml() + '</span>' ).contents() ); var $new = $( $( '<span>' + this.getHtml() + '</span>' ).contents() );
if ( $new.length === 0 ) { if ( $new.length === 0 ) {
$new = $new.add( document.createTextNode( '' ) ); $new = $new.add( document.createTextNode( '' ) );
@ -144,9 +145,9 @@ ve.ce.TextNode.prototype.onUpdate = function( force ) {
if ( this.parent ) { if ( this.parent ) {
this.parent.clean(); this.parent.clean();
if ( ve.debug ) { if ( ve.debug ) {
this.parent.$.css('background-color', '#F6F6F6'); this.parent.$.css('backgroundColor', '#F6F6F6');
setTimeout( ve.proxy( function() { setTimeout( ve.proxy( function () {
this.parent.$.css('background-color', 'transparent'); this.parent.$.css('backgroundColor', 'transparent');
}, this ), 350 ); }, this ), 350 );
} }
} }
@ -177,15 +178,17 @@ ve.ce.TextNode.prototype.getHtml = function() {
leftPlain, leftPlain,
rightPlain, rightPlain,
hashStack = [], hashStack = [],
annotationStack = {}; annotationStack = {},
chr;
var replaceWithNonBreakingSpace = function( index, data ) { function replaceWithNonBreakingSpace( index, data ) {
if ( ve.isArray( data[index] ) ) { if ( ve.isArray( data[index] ) ) {
data[index][0] = '&nbsp;'; data[index][0] = '&nbsp;';
} else { } else {
data[index] = '&nbsp;'; data[index] = '&nbsp;';
} }
}; }
if ( data.length > 0 ) { if ( data.length > 0 ) {
character = data[0]; character = data[0];
if ( ve.isArray( character ) ? character[0] === ' ' : character === ' ' ) { if ( ve.isArray( character ) ? character[0] === ' ' : character === ' ' ) {
@ -212,10 +215,10 @@ ve.ce.TextNode.prototype.getHtml = function() {
} }
} }
var openAnnotations = function( annotations ) { function openAnnotations( annotations ) {
var out = '', var out = '',
annotation; annotation, hash;
for ( var hash in annotations ) { for ( hash in annotations ) {
annotation = annotations[hash]; annotation = annotations[hash];
out += typeof renderers[annotation.type].open === 'function' ? out += typeof renderers[annotation.type].open === 'function' ?
renderers[annotation.type].open( annotation.data ) : renderers[annotation.type].open( annotation.data ) :
@ -224,12 +227,12 @@ ve.ce.TextNode.prototype.getHtml = function() {
annotationStack[hash] = annotation; annotationStack[hash] = annotation;
} }
return out; return out;
}; }
var closeAnnotations = function( annotations ) { function closeAnnotations( annotations ) {
var out = '', var out = '',
annotation; annotation, hash;
for ( var hash in annotations ) { for ( hash in annotations ) {
annotation = annotations[hash]; annotation = annotations[hash];
out += typeof renderers[annotation.type].close === 'function' ? out += typeof renderers[annotation.type].close === 'function' ?
renderers[annotation.type].close( annotation.data ) : renderers[annotation.type].close( annotation.data ) :
@ -238,7 +241,7 @@ ve.ce.TextNode.prototype.getHtml = function() {
delete annotationStack[hash]; delete annotationStack[hash];
} }
return out; return out;
}; }
for ( i = 0; i < data.length; i++ ) { for ( i = 0; i < data.length; i++ ) {
right = data[i]; right = data[i];
@ -294,7 +297,7 @@ ve.ce.TextNode.prototype.getHtml = function() {
out += openAnnotations( open ); out += openAnnotations( open );
} }
var chr = rightPlain ? right : right[0]; chr = rightPlain ? right : right[0];
out += chr in htmlChars ? htmlChars[chr] : chr; out += chr in htmlChars ? htmlChars[chr] : chr;
left = right; left = right;
} }

View file

@ -56,11 +56,12 @@ if ( $.browser.msie ) {
* @throws 'Invalid attribute value' if attribute value is not a key in {domWrapperElementTypes} * @throws 'Invalid attribute value' if attribute value is not a key in {domWrapperElementTypes}
*/ */
ve.ce.BranchNode.getDomWrapperType = function( model, key ) { ve.ce.BranchNode.getDomWrapperType = function( model, key ) {
var value = model.getAttribute( key ); var types,
value = model.getAttribute( key );
if ( value === undefined ) { if ( value === undefined ) {
throw 'Undefined attribute: ' + key; throw 'Undefined attribute: ' + key;
} }
var types = ve.ce.nodeFactory.lookup( model.getType() ).domWrapperElementTypes; types = ve.ce.nodeFactory.lookup( model.getType() ).domWrapperElementTypes;
if ( types[value] === undefined ) { if ( types[value] === undefined ) {
throw 'Invalid attribute value: ' + value; throw 'Invalid attribute value: ' + value;
} }
@ -85,10 +86,12 @@ ve.ce.BranchNode.getDomWrapper = function( model, key ) {
/* Methods */ /* Methods */
ve.ce.BranchNode.prototype.addSlugs = function() { ve.ce.BranchNode.prototype.addSlugs = function() {
var i, $slug;
// Remove all slugs in this branch // Remove all slugs in this branch
this.$slugs.remove(); this.$slugs.remove();
var $slug = ve.ce.BranchNode.$slugTemplate.clone(); $slug = ve.ce.BranchNode.$slugTemplate.clone();
if ( this.canHaveGrandchildren() ) { if ( this.canHaveGrandchildren() ) {
$slug.css( 'display', 'block'); $slug.css( 'display', 'block');
@ -101,7 +104,7 @@ ve.ce.BranchNode.prototype.addSlugs = function() {
$slug.clone().appendTo( this.$ ) $slug.clone().appendTo( this.$ )
); );
} }
for ( var i = 0; i < this.children.length; i++ ) { for ( i = 0; i < this.children.length; i++ ) {
if ( this.children[i].canHaveSlug() ) { if ( this.children[i].canHaveSlug() ) {
if ( i === 0 ) { if ( i === 0 ) {
// First sluggable child (left side) // First sluggable child (left side)
@ -138,9 +141,11 @@ ve.ce.BranchNode.prototype.addSlugs = function() {
* @emits rewrap ($old, $new) * @emits rewrap ($old, $new)
*/ */
ve.ce.BranchNode.prototype.updateDomWrapper = function( key ) { ve.ce.BranchNode.prototype.updateDomWrapper = function( key ) {
var type = ve.ce.BranchNode.getDomWrapperType( this.model, key ); var $element,
type = ve.ce.BranchNode.getDomWrapperType( this.model, key );
if ( type !== this.domWrapperElementType ) { if ( type !== this.domWrapperElementType ) {
var $element = $( '<' + type + '></' + type + '>' ); $element = $( '<' + type + '></' + type + '>' );
// Copy classes // Copy classes
$element.attr( 'class', this.$.attr( 'class' ) ); $element.attr( 'class', this.$.attr( 'class' ) );
// Copy .data( 'node' ) // Copy .data( 'node' )
@ -172,21 +177,23 @@ ve.ce.BranchNode.prototype.updateDomWrapper = function( key ) {
ve.ce.BranchNode.prototype.onSplice = function( index, howmany ) { ve.ce.BranchNode.prototype.onSplice = function( index, howmany ) {
var i, var i,
length, length,
args = Array.prototype.slice.call( arguments, 0 ); args = Array.prototype.slice.call( arguments, 0 ),
$anchor,
removals,
$target;
// Convert models to views and attach them to this node // Convert models to views and attach them to this node
if ( args.length >= 3 ) { if ( args.length >= 3 ) {
for ( i = 2, length = args.length; i < length; i++ ) { for ( i = 2, length = args.length; i < length; i++ ) {
args[i] = ve.ce.nodeFactory.create( args[i].getType(), args[i] ); args[i] = ve.ce.nodeFactory.create( args[i].getType(), args[i] );
} }
} }
var removals = this.children.splice.apply( this.children, args ); removals = this.children.splice.apply( this.children, args );
for ( i = 0, length = removals.length; i < length; i++ ) { for ( i = 0, length = removals.length; i < length; i++ ) {
removals[i].detach(); removals[i].detach();
// Update DOM // Update DOM
removals[i].$.detach(); removals[i].$.detach();
} }
if ( args.length >= 3 ) { if ( args.length >= 3 ) {
var $target;
if ( index ) { if ( index ) {
// Get the element before the insertion point // Get the element before the insertion point
$anchor = this.$.children(':not(.ve-ce-slug)').eq( index - 1 ); $anchor = this.$.children(':not(.ve-ce-slug)').eq( index - 1 );
@ -205,13 +212,14 @@ ve.ce.BranchNode.prototype.onSplice = function( index, howmany ) {
}; };
ve.ce.BranchNode.prototype.hasSlugAtOffset = function( offset ) { ve.ce.BranchNode.prototype.hasSlugAtOffset = function( offset ) {
var i, nodeOffset, nodeLength;
if ( this.getLength() === 0 ) { if ( this.getLength() === 0 ) {
return true; return true;
} }
for ( var i = 0; i < this.children.length; i++ ) { for ( i = 0; i < this.children.length; i++ ) {
if ( this.children[i].canHaveSlug() ) { if ( this.children[i].canHaveSlug() ) {
var nodeOffset = this.children[i].model.getRoot().getOffsetFromNode( this.children[i].model ); nodeOffset = this.children[i].model.getRoot().getOffsetFromNode( this.children[i].model );
var nodeLength = this.children[i].model.getOuterLength(); nodeLength = this.children[i].model.getOuterLength();
if ( i === 0 ) { if ( i === 0 ) {
if ( nodeOffset === offset ) { if ( nodeOffset === offset ) {
return true; return true;

View file

@ -1,3 +1,5 @@
/*global rangy */
/** /**
* ContentEditable surface. * ContentEditable surface.
* *
@ -115,6 +117,14 @@ ve.ce.Surface.prototype.onMouseDown = function( e ) {
}; };
ve.ce.Surface.prototype.onKeyDown = function( e ) { ve.ce.Surface.prototype.onKeyDown = function( e ) {
var offset,
relativeContentOffset,
relativeStructuralOffset,
relativeStructuralOffsetNode,
hasSlug,
newOffset,
annotations,
annotation;
switch ( e.keyCode ) { switch ( e.keyCode ) {
// Indenting list items doesn't work yet, so disable tab handling for now // Indenting list items doesn't work yet, so disable tab handling for now
/* /*
@ -134,12 +144,11 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
// Left arrow // Left arrow
case 37: case 37:
if ( !e.metaKey && !e.altKey && !e.shiftKey && this.model.getSelection().getLength() === 0 ) { if ( !e.metaKey && !e.altKey && !e.shiftKey && this.model.getSelection().getLength() === 0 ) {
var offset = this.model.getSelection().start; offset = this.model.getSelection().start;
var relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, -1 ); relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, -1 );
var relativeStructuralOffset = this.documentView.model.getRelativeStructuralOffset( offset - 1, -1, true ); relativeStructuralOffset = this.documentView.model.getRelativeStructuralOffset( offset - 1, -1, true );
var relativeStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset ); relativeStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset );
var hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset ); hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset );
var newOffset;
if ( hasSlug ) { if ( hasSlug ) {
if ( relativeContentOffset > offset ) { if ( relativeContentOffset > offset ) {
newOffset = relativeStructuralOffset; newOffset = relativeStructuralOffset;
@ -158,13 +167,17 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
break; break;
// Right arrow // Right arrow
case 39: case 39:
if ( !e.metaKey && !e.altKey && !e.shiftKey && this.model.getSelection().getLength() === 0 ) { if (
var offset = this.model.getSelection().start; !e.metaKey &&
var relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, 1 ); !e.altKey &&
var relativeStructuralOffset = this.documentView.model.getRelativeStructuralOffset( offset + 1, 1, true ); !e.shiftKey &&
var relativeStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset ); this.model.getSelection().getLength() === 0
var hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset ); ) {
var newOffset; offset = this.model.getSelection().start;
relativeContentOffset = this.documentView.model.getRelativeContentOffset( offset, 1 );
relativeStructuralOffset = this.documentView.model.getRelativeStructuralOffset( offset + 1, 1, true );
relativeStructuralOffsetNode = this.documentView.documentNode.getNodeFromOffset( relativeStructuralOffset );
hasSlug = relativeStructuralOffsetNode.hasSlugAtOffset( relativeStructuralOffset );
if ( hasSlug ) { if ( hasSlug ) {
if ( relativeContentOffset < offset ) { if ( relativeContentOffset < offset ) {
newOffset = relativeStructuralOffset; newOffset = relativeStructuralOffset;
@ -201,8 +214,8 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
if ( ve.ce.Surface.isShortcutKey( e ) ) { if ( ve.ce.Surface.isShortcutKey( e ) ) {
// Ctrl+B / Cmd+B, annotate with bold // Ctrl+B / Cmd+B, annotate with bold
e.preventDefault(); e.preventDefault();
var annotations = this.documentView.model.getAnnotationsFromRange( this.model.getSelection() ), annotations = this.documentView.model.getAnnotationsFromRange( this.model.getSelection() );
annotation = {"type":"textStyle/bold"}; annotation = { 'type': 'textStyle/bold' };
this.model.annotate( annotations[ve.getHash(annotation)] ? 'clear' : 'set', annotation ); this.model.annotate( annotations[ve.getHash(annotation)] ? 'clear' : 'set', annotation );
} }
@ -212,8 +225,8 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
if ( ve.ce.Surface.isShortcutKey( e ) ) { if ( ve.ce.Surface.isShortcutKey( e ) ) {
// Ctrl+I / Cmd+I, annotate with italic // Ctrl+I / Cmd+I, annotate with italic
e.preventDefault(); e.preventDefault();
var annotations = this.documentView.model.getAnnotationsFromRange( this.model.getSelection() ), annotations = this.documentView.model.getAnnotationsFromRange( this.model.getSelection() );
annotation = {"type":"textStyle/italic"}; annotation = { 'type': 'textStyle/italic' };
this.model.annotate( annotations[ve.getHash(annotation)] ? 'clear' : 'set', annotation ); this.model.annotate( annotations[ve.getHash(annotation)] ? 'clear' : 'set', annotation );
} }
@ -256,7 +269,7 @@ ve.ce.Surface.prototype.onKeyDown = function( e ) {
}; };
ve.ce.Surface.prototype.onCutCopy = function( e ) { ve.ce.Surface.prototype.onCutCopy = function( e ) {
var _this = this, var view = this,
sel = rangy.getSelection(), sel = rangy.getSelection(),
$frag = null, $frag = null,
key = ''; key = '';
@ -264,10 +277,10 @@ ve.ce.Surface.prototype.onCutCopy = function( e ) {
this.stopPolling(); this.stopPolling();
// Create key from text and element names // Create key from text and element names
$frag = $(sel.getRangeAt(0).cloneContents()); $frag = $( sel.getRangeAt(0).cloneContents() );
$frag.contents().each(function() { $frag.contents().each( function () {
key += this.textContent || this.nodeName; key += this.textContent || this.nodeName;
}); } );
key = key.replace( /\s/gm, '' ); key = key.replace( /\s/gm, '' );
// Set surface clipboard // Set surface clipboard
@ -275,32 +288,32 @@ ve.ce.Surface.prototype.onCutCopy = function( e ) {
this.documentView.model.getData( this.model.getSelection() ) this.documentView.model.getData( this.model.getSelection() )
); );
if ( e.type == 'cut' ) { if ( e.type === 'cut' ) {
this.stopPolling(); this.stopPolling();
setTimeout( function() { setTimeout( function() {
var selection = null, var selection = null,
tx = null; tx = null;
// We don't like how browsers cut, so let's undo it and do it ourselves. // We don't like how browsers cut, so let's undo it and do it ourselves.
document.execCommand('undo', false, false); document.execCommand( 'undo', false, false );
selection = _this.model.getSelection(); selection = view.model.getSelection();
// Transact // Transact
_this.autoRender = true; view.autoRender = true;
tx = ve.dm.Transaction.newFromRemoval( _this.documentView.model, selection ); tx = ve.dm.Transaction.newFromRemoval( view.documentView.model, selection );
_this.model.change( tx, new ve.Range( selection.start ) ); view.model.change( tx, new ve.Range( selection.start ) );
_this.autoRender = false; view.autoRender = false;
_this.clearPollData(); view.clearPollData();
_this.startPolling(); view.startPolling();
}, 1 ); }, 1 );
} }
}; };
ve.ce.Surface.prototype.onPaste = function( e ) { ve.ce.Surface.prototype.onPaste = function( e ) {
var _this = this, var view = this,
selection = this.model.getSelection(), selection = this.model.getSelection(),
tx = null; tx = null;
@ -308,14 +321,14 @@ ve.ce.Surface.prototype.onPaste = function( e ) {
// Pasting into a range? Remove first. // Pasting into a range? Remove first.
if (!rangy.getSelection().isCollapsed) { if (!rangy.getSelection().isCollapsed) {
tx = ve.dm.Transaction.newFromRemoval( _this.documentView.model, selection ); tx = ve.dm.Transaction.newFromRemoval( view.documentView.model, selection );
_this.model.change( tx ); view.model.change( tx );
} }
$('#paste').html('').show().focus(); $('#paste').html('').show().focus();
setTimeout( function() { setTimeout( function() {
var key = '', var key = '',
pasteData = null, pasteData = null,
tx = null; tx = null;
@ -326,39 +339,40 @@ ve.ce.Surface.prototype.onPaste = function( e ) {
key = key.replace( /\s/gm, '' ); key = key.replace( /\s/gm, '' );
// Get linear model from surface clipboard or create array from unknown pasted content // Get linear model from surface clipboard or create array from unknown pasted content
pasteData = ( _this.clipboard[key] ) ? _this.clipboard[key] : $('#paste').text().split(''); pasteData = ( view.clipboard[key] ) ? view.clipboard[key] : $('#paste').text().split('');
// Transact // Transact
tx = ve.dm.Transaction.newFromInsertion( tx = ve.dm.Transaction.newFromInsertion(
_this.documentView.model, selection.start, pasteData view.documentView.model, selection.start, pasteData
); );
_this.model.change( tx, new ve.Range( selection.start + pasteData.length ) ); view.model.change( tx, new ve.Range( selection.start + pasteData.length ) );
_this.documentView.documentNode.$.focus(); view.documentView.documentNode.$.focus();
_this.clearPollData(); view.clearPollData();
_this.startPolling(); view.startPolling();
}, 1 ); }, 1 );
}; };
ve.ce.Surface.prototype.onKeyPress = function( e ) { ve.ce.Surface.prototype.onKeyPress = function( e ) {
var node, selection, data;
ve.log('onKeyPress'); ve.log('onKeyPress');
if ( ve.ce.Surface.isShortcutKey( e ) || e.which === 13 ) { if ( ve.ce.Surface.isShortcutKey( e ) || e.which === 13 ) {
return; return;
} }
var selection = this.model.getSelection(); selection = this.model.getSelection();
if ( if (
selection.getLength() === 0 && selection.getLength() === 0 &&
this.sluggable === true && this.sluggable === true &&
this.hasSlugAtOffset( selection.start ) this.hasSlugAtOffset( selection.start )
) { ) {
var node;
this.sluggable = false; this.sluggable = false;
this.stopPolling(); this.stopPolling();
if ( this.documentView.getNodeFromOffset( selection.start ).getLength() !== 0 ) { if ( this.documentView.getNodeFromOffset( selection.start ).getLength() !== 0 ) {
var data = [ { 'type' : 'paragraph' }, { 'type' : '/paragraph' } ]; data = [ { 'type' : 'paragraph' }, { 'type' : '/paragraph' } ];
this.model.change( this.model.change(
ve.dm.Transaction.newFromInsertion( ve.dm.Transaction.newFromInsertion(
this.documentView.model, this.documentView.model,
@ -419,7 +433,11 @@ ve.ce.Surface.prototype.clearPollData = function() {
}; };
ve.ce.Surface.prototype.pollChanges = function( async ) { ve.ce.Surface.prototype.pollChanges = function( async ) {
var delay = ve.proxy( function( async ) { var delay, node, range, rangySelection,
$anchorNode, $focusNode,
text, hash;
delay = ve.proxy( function( async ) {
if ( this.poll.polling ) { if ( this.poll.polling ) {
if ( this.poll.timeout !== null ) { if ( this.poll.timeout !== null ) {
clearTimeout( this.poll.timeout ); clearTimeout( this.poll.timeout );
@ -435,9 +453,9 @@ ve.ce.Surface.prototype.pollChanges = function( async ) {
return; return;
} }
var node = this.poll.node, node = this.poll.node;
range = this.poll.range, range = this.poll.range;
rangySelection = rangy.getSelection(); rangySelection = rangy.getSelection();
if ( if (
rangySelection.anchorNode !== this.poll.rangySelection.anchorNode || rangySelection.anchorNode !== this.poll.rangySelection.anchorNode ||
@ -452,8 +470,8 @@ ve.ce.Surface.prototype.pollChanges = function( async ) {
// TODO: Optimize for the case of collapsed (rangySelection.isCollapsed) range // TODO: Optimize for the case of collapsed (rangySelection.isCollapsed) range
var $anchorNode = $( rangySelection.anchorNode ).closest( '.ve-ce-branchNode' ), $anchorNode = $( rangySelection.anchorNode ).closest( '.ve-ce-branchNode' );
$focusNode = $( rangySelection.focusNode ).closest( '.ve-ce-branchNode' ); $focusNode = $( rangySelection.focusNode ).closest( '.ve-ce-branchNode' );
if ( $anchorNode[0] === $focusNode[0] ) { if ( $anchorNode[0] === $focusNode[0] ) {
node = $anchorNode[0]; node = $anchorNode[0];
@ -481,8 +499,8 @@ ve.ce.Surface.prototype.pollChanges = function( async ) {
} }
} else { } else {
if ( node !== null ) { if ( node !== null ) {
var text = ve.ce.getDomText( node ), text = ve.ce.getDomText( node );
hash = ve.ce.getDomHash( node ); hash = ve.ce.getDomHash( node );
if ( this.poll.text !== text || this.poll.hash !== hash ) { if ( this.poll.text !== text || this.poll.hash !== hash ) {
this.emit( 'contentChange', { this.emit( 'contentChange', {
'node': node, 'node': node,
@ -518,25 +536,27 @@ ve.ce.Surface.prototype.pollChanges = function( async ) {
}; };
ve.ce.Surface.prototype.onContentChange = function( e ) { ve.ce.Surface.prototype.onContentChange = function( e ) {
var nodeOffset = $( e.node ).data( 'node' ).model.getOffset(), var nodeOffset = $( e.node ).data( 'node' ).model.getOffset(),
offsetDiff = ( offsetDiff = (
e.old.range !== null && e.old.range !== null &&
e.new.range !== null && e['new'].range !== null &&
e.old.range.getLength() === 0 && e.old.range.getLength() === 0 &&
e.new.range.getLength() === 0 e['new'].range.getLength() === 0
) ? e.new.range.start - e.old.range.start : null, ) ? e['new'].range.start - e.old.range.start : null,
lengthDiff = e.new.text.length - e.old.text.length; lengthDiff = e['new'].text.length - e.old.text.length,
data,
annotations;
if ( if (
offsetDiff === lengthDiff && offsetDiff === lengthDiff &&
e.old.text.substring( 0, e.old.range.start - nodeOffset - 1 ) === e.old.text.substring( 0, e.old.range.start - nodeOffset - 1 ) ===
e.new.text.substring( 0, e.old.range.start - nodeOffset - 1 ) e['new'].text.substring( 0, e.old.range.start - nodeOffset - 1 )
) { ) {
var data = e.new.text.substring( data = e['new'].text.substring(
e.old.range.start - nodeOffset - 1, e.old.range.start - nodeOffset - 1,
e.new.range.start - nodeOffset - 1 e['new'].range.start - nodeOffset - 1
).split( '' ), ).split( '' );
annotations = this.model.getDocument().getAnnotationsFromOffset( e.old.range.start - 1 ); annotations = this.model.getDocument().getAnnotationsFromOffset( e.old.range.start - 1 );
if ( !ve.isEmptyObject( annotations ) ) { if ( !ve.isEmptyObject( annotations ) ) {
ve.dm.Document.addAnnotationsToData( data, annotations ); ve.dm.Document.addAnnotationsToData( data, annotations );
@ -545,28 +565,28 @@ ve.ce.Surface.prototype.onContentChange = function( e ) {
this.render = false; this.render = false;
this.model.change( this.model.change(
ve.dm.Transaction.newFromInsertion( this.documentView.model, e.old.range.start, data ), ve.dm.Transaction.newFromInsertion( this.documentView.model, e.old.range.start, data ),
e.new.range e['new'].range
); );
this.render = true; this.render = true;
} else { } else {
var fromLeft = 0, var fromLeft = 0,
fromRight = 0, fromRight = 0,
len = Math.min( e.old.text.length, e.new.text.length ); len = Math.min( e.old.text.length, e['new'].text.length );
while ( fromLeft < len && e.old.text[fromLeft] === e.new.text[fromLeft] ) { while ( fromLeft < len && e.old.text[fromLeft] === e['new'].text[fromLeft] ) {
++fromLeft; ++fromLeft;
} }
while ( while (
fromRight < len - fromLeft && fromRight < len - fromLeft &&
e.old.text[e.old.text.length - 1 - fromRight] === e.old.text[e.old.text.length - 1 - fromRight] ===
e.new.text[e.new.text.length - 1 - fromRight] e['new'].text[e['new'].text.length - 1 - fromRight]
) { ) {
++fromRight; ++fromRight;
} }
var annotations = this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 + fromLeft ), annotations = this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 + fromLeft );
data = e.new.text.substring( fromLeft, e.new.text.length - fromRight ).split( '' ); data = e['new'].text.substring( fromLeft, e['new'].text.length - fromRight ).split( '' );
if ( !ve.isEmptyObject( annotations ) ) { if ( !ve.isEmptyObject( annotations ) ) {
ve.dm.Document.addAnnotationsToData( data, annotations ); ve.dm.Document.addAnnotationsToData( data, annotations );
@ -581,12 +601,12 @@ ve.ce.Surface.prototype.onContentChange = function( e ) {
this.documentView.model, this.documentView.model,
new ve.Range( nodeOffset + 1 + fromLeft, nodeOffset + 1 + e.old.text.length - fromRight ) new ve.Range( nodeOffset + 1 + fromLeft, nodeOffset + 1 + e.old.text.length - fromRight )
), ),
e.new.range e['new'].range
); );
} }
this.model.change( this.model.change(
ve.dm.Transaction.newFromInsertion( this.documentView.model, nodeOffset + 1 + fromLeft, data ), ve.dm.Transaction.newFromInsertion( this.documentView.model, nodeOffset + 1 + fromLeft, data ),
e.new.range e['new'].range
); );
} }
this.sluggable = true; this.sluggable = true;
@ -621,7 +641,9 @@ ve.ce.Surface.prototype.handleEnter = function( e ) {
documentModel = this.model.getDocument(), documentModel = this.model.getDocument(),
emptyParagraph = [{ 'type': 'paragraph' }, { 'type': '/paragraph' }], emptyParagraph = [{ 'type': 'paragraph' }, { 'type': '/paragraph' }],
tx, tx,
advanceCursor = true; advanceCursor = true,
outerParent,
outerChildrenCount;
// Stop polling while we work // Stop polling while we work
this.stopPolling(); this.stopPolling();
@ -633,7 +655,7 @@ ve.ce.Surface.prototype.handleEnter = function( e ) {
this.model.change( tx, selection ); this.model.change( tx, selection );
} }
// Handle insertion // Handle insertion
var node = this.documentView.getNodeFromOffset( selection.from ), var node = this.documentView.getNodeFromOffset( selection.from ),
nodeModel = node.getModel(), nodeModel = node.getModel(),
cursor = selection.from, cursor = selection.from,
nodeOffset = nodeModel.getOffset(), nodeOffset = nodeModel.getOffset(),
@ -660,7 +682,7 @@ ve.ce.Surface.prototype.handleEnter = function( e ) {
} }
} else { } else {
// Split // Split
var stack = [], var stack = [],
outermostNode = null; outermostNode = null;
ve.Node.traverseUpstream( node, function( node ) { ve.Node.traverseUpstream( node, function( node ) {
@ -681,14 +703,14 @@ ve.ce.Surface.prototype.handleEnter = function( e ) {
} }
} ); } );
var outerParent = outermostNode.getModel().getParent(), outerParent = outermostNode.getModel().getParent();
outerChildrenCount = outerParent.getChildren().length outerChildrenCount = outerParent.getChildren().length;
if ( if (
outermostNode.type == 'listItem' && // this is a list item outermostNode.type === 'listItem' && // this is a list item
outerParent.getChildren()[outerChildrenCount - 1] == outermostNode.getModel() && // this is the last list item outerParent.getChildren()[outerChildrenCount - 1] === outermostNode.getModel() && // this is the last list item
outermostNode.children.length == 1 && // there is one child outermostNode.children.length === 1 && // there is one child
node.model.length == 0 // the child is empty node.model.length === 0 // the child is empty
) { ) {
// Enter was pressed in an empty list item. // Enter was pressed in an empty list item.
var list = outermostNode.getModel().getParent(); var list = outermostNode.getModel().getParent();
@ -730,7 +752,11 @@ ve.ce.Surface.prototype.handleDelete = function( backspace ) {
sourceSplitableNode, sourceSplitableNode,
targetSplitableNode, targetSplitableNode,
tx, tx,
cursorAt; cursorAt,
sourceNode,
targetNode,
sourceData,
nodeToDelete;
if ( selection.from === selection.to ) { if ( selection.from === selection.to ) {
if ( backspace || selection.to === this.model.getDocument().data.length - 1) { if ( backspace || selection.to === this.model.getDocument().data.length - 1) {
@ -741,8 +767,8 @@ ve.ce.Surface.prototype.handleDelete = function( backspace ) {
targetOffset = selection.to; targetOffset = selection.to;
} }
var sourceNode = this.documentView.getNodeFromOffset( sourceOffset, false ), sourceNode = this.documentView.getNodeFromOffset( sourceOffset, false );
targetNode = this.documentView.getNodeFromOffset( targetOffset, false ); targetNode = this.documentView.getNodeFromOffset( targetOffset, false );
if ( sourceNode.type === targetNode.type ) { if ( sourceNode.type === targetNode.type ) {
sourceSplitableNode = ve.ce.Node.getSplitableNode( sourceNode ); sourceSplitableNode = ve.ce.Node.getSplitableNode( sourceNode );
@ -771,9 +797,9 @@ ve.ce.Surface.prototype.handleDelete = function( backspace ) {
} else { } else {
// Source and target are different nodes and do not share a parent, perform tricky merge // Source and target are different nodes and do not share a parent, perform tricky merge
// Get the data for the source node // Get the data for the source node
var sourceData = this.documentView.model.getData( sourceNode.model.getRange() ); sourceData = this.documentView.model.getData( sourceNode.model.getRange() );
// Find the node that should be completely removed // Find the node that should be completely removed
var nodeToDelete = sourceNode; nodeToDelete = sourceNode;
ve.Node.traverseUpstream( nodeToDelete, function( node ) { ve.Node.traverseUpstream( nodeToDelete, function( node ) {
if ( node.getParent().children.length === 1 ) { if ( node.getParent().children.length === 1 ) {
nodeToDelete = node.getParent(); nodeToDelete = node.getParent();
@ -874,7 +900,7 @@ ve.ce.Surface.prototype.getNearestCorrectOffset = function( offset, direction )
return offset; return offset;
} }
var contentOffset = this.documentView.model.getNearestContentOffset( offset, direction ), var contentOffset = this.documentView.model.getNearestContentOffset( offset, direction ),
structuralOffset = structuralOffset =
this.documentView.model.getNearestStructuralOffset( offset, direction, true ); this.documentView.model.getNearestStructuralOffset( offset, direction, true );
@ -918,7 +944,7 @@ ve.ce.Surface.prototype.hasSlugAtOffset = function( offset ) {
* offset is the position within the element * offset is the position within the element
*/ */
ve.ce.Surface.prototype.getNodeAndOffset = function( offset ) { ve.ce.Surface.prototype.getNodeAndOffset = function( offset ) {
var node = this.documentView.getNodeFromOffset( offset ), var node = this.documentView.getNodeFromOffset( offset ),
startOffset = this.documentView.getDocumentNode().getOffsetFromNode( node ) + startOffset = this.documentView.getDocumentNode().getOffsetFromNode( node ) +
( ( node.isWrapped() ) ? 1 : 0 ), ( ( node.isWrapped() ) ? 1 : 0 ),
current = [node.$.contents(), 0], current = [node.$.contents(), 0],
@ -992,18 +1018,21 @@ ve.ce.Surface.prototype.getOffset = function( domNode, domOffset ) {
}; };
ve.ce.Surface.prototype.getOffsetFromTextNode = function( domNode, domOffset ) { ve.ce.Surface.prototype.getOffsetFromTextNode = function( domNode, domOffset ) {
var $node = $( domNode ).closest( var $node, nodeModel, current, stack, item, offset, $item;
'.ve-ce-branchNode, .ve-ce-alienBlockNode, .ve-ce-alienInlineNode'
), $node = $( domNode ).closest(
nodeModel = $node.data( 'node' ).getModel(); '.ve-ce-branchNode, .ve-ce-alienBlockNode, .ve-ce-alienInlineNode'
);
nodeModel = $node.data( 'node' ).getModel();
if ( ! $node.hasClass( 've-ce-branchNode' ) ) { if ( ! $node.hasClass( 've-ce-branchNode' ) ) {
return nodeModel.getOffset(); return nodeModel.getOffset();
} }
var current = [$node.contents(), 0],
stack = [current], current = [$node.contents(), 0];
offset = 0, stack = [current];
item, offset = 0;
$item;
while ( stack.length > 0 ) { while ( stack.length > 0 ) {
if ( current[1] >= current[0].length ) { if ( current[1] >= current[0].length ) {
stack.pop(); stack.pop();
@ -1041,7 +1070,7 @@ ve.ce.Surface.prototype.getOffsetFromTextNode = function( domNode, domOffset ) {
}; };
ve.ce.Surface.prototype.getOffsetFromElementNode = function( domNode, domOffset, addOuterLength ) { ve.ce.Surface.prototype.getOffsetFromElementNode = function( domNode, domOffset, addOuterLength ) {
var $domNode = $( domNode ), var $domNode = $( domNode ),
nodeModel, nodeModel,
node; node;

View file

@ -42,7 +42,7 @@ ve.dm.DefinitionListItemNode.converters = {
return element.attributes && ( { return element.attributes && ( {
'term': document.createElement( 'dt' ), 'term': document.createElement( 'dt' ),
'definition': document.createElement( 'dd' ) 'definition': document.createElement( 'dd' )
} )[element.attributes['style']]; } )[element.attributes.style];
}, },
'toDataElement': function( tag, element ) { 'toDataElement': function( tag, element ) {
return ( { return ( {

View file

@ -46,7 +46,7 @@ ve.dm.HeadingNode.converters = {
4: document.createElement( 'h4' ), 4: document.createElement( 'h4' ),
5: document.createElement( 'h5' ), 5: document.createElement( 'h5' ),
6: document.createElement( 'h6' ) 6: document.createElement( 'h6' )
} )[element.attributes['level']]; } )[element.attributes.level];
}, },
'toDataElement': function( tag, element ) { 'toDataElement': function( tag, element ) {
return ( { return ( {

View file

@ -42,7 +42,7 @@ ve.dm.ListNode.converters = {
return element.attributes && ( { return element.attributes && ( {
'bullet': document.createElement( 'ul' ), 'bullet': document.createElement( 'ul' ),
'number': document.createElement( 'ol' ) 'number': document.createElement( 'ol' )
} )[element.attributes['style']]; } )[element.attributes.style];
}, },
'toDataElement': function( tag, element ) { 'toDataElement': function( tag, element ) {
return ( { return ( {

View file

@ -42,7 +42,7 @@ ve.dm.TableCellNode.converters = {
return element.attributes && ( { return element.attributes && ( {
'data': document.createElement( 'td' ), 'data': document.createElement( 'td' ),
'header': document.createElement( 'th' ) 'header': document.createElement( 'th' )
} )[element.attributes['style']]; } )[element.attributes.style];
}, },
'toDataElement': function( tag, element ) { 'toDataElement': function( tag, element ) {
return ( { return ( {

View file

@ -43,7 +43,7 @@ ve.dm.TableSectionNode.converters = {
'header': document.createElement( 'thead' ), 'header': document.createElement( 'thead' ),
'body': document.createElement( 'tbody' ), 'body': document.createElement( 'tbody' ),
'footer': document.createElement( 'tfoot' ) 'footer': document.createElement( 'tfoot' )
} )[element.attributes['style']]; } )[element.attributes.style];
}, },
'toDataElement': function( tag, element ) { 'toDataElement': function( tag, element ) {
return ( { return ( {

View file

@ -373,7 +373,7 @@ ve.dm.Converter.prototype.getDataFromDom = function( domElement, annotations, da
// If we're closing a node that doesn't have any children, but could contain a paragraph, // If we're closing a node that doesn't have any children, but could contain a paragraph,
// add a paragraph. This prevents things like empty list items // add a paragraph. This prevents things like empty list items
var childTypes = ve.dm.nodeFactory.getChildNodeTypes( branchType ); var childTypes = ve.dm.nodeFactory.getChildNodeTypes( branchType );
if ( branchType !== 'paragraph' && dataElement && data[data.length - 1] == dataElement && if ( branchType !== 'paragraph' && dataElement && data[data.length - 1] === dataElement &&
!wrapping && !ve.dm.nodeFactory.canNodeContainContent( branchType ) && !wrapping && !ve.dm.nodeFactory.canNodeContainContent( branchType ) &&
!ve.dm.nodeFactory.isNodeContent( branchType ) && !ve.dm.nodeFactory.isNodeContent( branchType ) &&
( childTypes === null || $.inArray( 'paragraph', childTypes ) !== -1 ) ( childTypes === null || $.inArray( 'paragraph', childTypes ) !== -1 )
@ -606,7 +606,7 @@ ve.dm.Converter.prototype.getDomFromData = function( data ) {
.contents() .contents()
.filter( function() { .filter( function() {
// Text nodes only // Text nodes only
return this.nodeType == 3 && return this.nodeType === 3 &&
// Exclude text nodes within lists // Exclude text nodes within lists
$( this.parentNode ).closest( 'li, dd, dt' ).length === 0; $( this.parentNode ).closest( 'li, dd, dt' ).length === 0;
} ) } )

View file

@ -64,7 +64,7 @@ ve.dm.Document = function( data, parentDocument ) {
textLength = 0; textLength = 0;
} }
// Element open/close // Element open/close
if ( this.data[i].type.charAt( 0 ) != '/' ) { if ( this.data[i].type.charAt( 0 ) !== '/' ) {
// Branch or leaf node opening // Branch or leaf node opening
// Create a childless node // Create a childless node
node = ve.dm.nodeFactory.create( this.data[i].type, [], this.data[i].attributes ); node = ve.dm.nodeFactory.create( this.data[i].type, [], this.data[i].attributes );
@ -131,7 +131,7 @@ ve.dm.Document = function( data, parentDocument ) {
*/ */
ve.dm.Document.addAnnotationsToData = function( data, annotations ) { ve.dm.Document.addAnnotationsToData = function( data, annotations ) {
// Apply annotations to data // Apply annotations to data
for ( i = 0; i < data.length; i++ ) { for ( var i = 0; i < data.length; i++ ) {
if ( !ve.isArray( data[i] ) ) { if ( !ve.isArray( data[i] ) ) {
data[i] = [data[i]]; data[i] = [data[i]];
} }
@ -946,55 +946,55 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
* This function updates parentNode, parentType, openingStack and closingStack. * This function updates parentNode, parentType, openingStack and closingStack.
* *
* @param {Object|Array|String} element Linear model element * @param {Object|Array|String} element Linear model element
* @param {Number} index Index in data that this element came from. Used for error reporting only * @param {Number} index Index in data that this element came from (for error reporting only)
*/ */
function writeElement( element, index ) { function writeElement( element, index ) {
var expectedType; var expectedType;
if ( element.type === undefined ) { if ( element.type !== undefined ) {
// Content, do nothing if ( element.type.charAt( 0 ) !== '/' ) {
} else if ( element.type.charAt( 0 ) !== '/' ) { // Opening
// Opening // Check if this opening balances an earlier closing of a node
// Check if this opening balances an earlier closing of a node // that was already in the document. This is only the case if
// that was already in the document. This is only the case if // openingStack is empty (otherwise we still have unclosed nodes from
// openingStack is empty (otherwise we still have unclosed nodes from // within data) and if this opening matches the top of closingStack
// within data) and if this opening matches the top of closingStack if ( openingStack.length === 0 && closingStack.length > 0 &&
if ( openingStack.length === 0 && closingStack.length > 0 && closingStack[closingStack.length - 1] === element.type
closingStack[closingStack.length - 1] === element.type ) {
) { // The top of closingStack is now balanced out, so remove it
// The top of closingStack is now balanced out, so remove it closingStack.pop();
closingStack.pop(); } else {
} else { // This opens something new, put it on openingStack
// This opens something new, put it on openingStack openingStack.push( element );
openingStack.push( element );
}
parentType = element.type;
} else {
// Closing
// Make sure that this closing matches the currently opened node
if ( openingStack.length > 0 ) {
// The opening was on openingStack, so we're closing
// a node that was opened within data. Don't track
// that on closingStack
expectedType = openingStack.pop().type;
} else {
// openingStack is empty, so we're closing a node that
// was already in the document. This means we have to
// reopen it later, so track this on closingStack
expectedType = parentNode.getType();
closingStack.push( expectedType );
parentNode = parentNode.getParent();
if ( !parentNode ) {
throw 'Inserted data is trying to close the root node ' +
'(at index ' + index + ')';
} }
} parentType = element.type;
parentType = expectedType; } else {
// Closing
// Make sure that this closing matches the currently opened node
if ( openingStack.length > 0 ) {
// The opening was on openingStack, so we're closing
// a node that was opened within data. Don't track
// that on closingStack
expectedType = openingStack.pop().type;
} else {
// openingStack is empty, so we're closing a node that
// was already in the document. This means we have to
// reopen it later, so track this on closingStack
expectedType = parentNode.getType();
closingStack.push( expectedType );
parentNode = parentNode.getParent();
if ( !parentNode ) {
throw 'Inserted data is trying to close the root node ' +
'(at index ' + index + ')';
}
}
parentType = expectedType;
// Validate // Validate
// FIXME this breaks certain input, should fix it up, not scream and die // FIXME this breaks certain input, should fix it up, not scream and die
if ( element.type !== '/' + expectedType ) { if ( element.type !== '/' + expectedType ) {
throw 'Type mismatch, expected /' + expectedType + throw 'Type mismatch, expected /' + expectedType +
' but got ' + element.type + ' (at index ' + index + ')'; ' but got ' + element.type + ' (at index ' + index + ')';
}
} }
} }
newData.push( element ); newData.push( element );
@ -1007,7 +1007,10 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
if ( inTextNode && data[i].type !== undefined ) { if ( inTextNode && data[i].type !== undefined ) {
// We're leaving a text node, process fixupStack if needed // We're leaving a text node, process fixupStack if needed
// TODO duplicated code // TODO duplicated code
if ( fixupStack.length > 0 && fixupStack[fixupStack.length - 1].expectedType == '/text' ) { if (
fixupStack.length > 0 &&
fixupStack[fixupStack.length - 1].expectedType === '/text'
) {
popped = fixupStack.pop(); popped = fixupStack.pop();
// Go through these in reverse! // Go through these in reverse!
for ( j = popped.openings.length - 1; j >= 0; j-- ) { for ( j = popped.openings.length - 1; j >= 0; j-- ) {
@ -1017,7 +1020,8 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
writeElement( popped.reopenElements[j], i ); writeElement( popped.reopenElements[j], i );
} }
} }
parentType = openingStack.length > 0 ? openingStack[openingStack.length - 1] : parentNode.getType(); parentType = openingStack.length > 0 ?
openingStack[openingStack.length - 1] : parentNode.getType();
} }
if ( data[i].type === undefined || data[i].type.charAt( 0 ) !== '/' ) { if ( data[i].type === undefined || data[i].type.charAt( 0 ) !== '/' ) {
childType = data[i].type || 'text'; childType = data[i].type || 'text';
@ -1111,20 +1115,31 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
if ( openings.length > 0 ) { if ( openings.length > 0 ) {
// We wrapped the text node, update parentType // We wrapped the text node, update parentType
parentType = childType; parentType = childType;
fixupStack.push( { 'expectedType': '/text', 'openings': openings, 'reopenElements': reopenElements } ); fixupStack.push( {
'expectedType': '/text',
'openings': openings,
'reopenElements': reopenElements
} );
} }
// If we didn't wrap the text node, then the node we're inserting // If we didn't wrap the text node, then the node we're inserting
// into can have content, so we couldn't have closed anything // into can have content, so we couldn't have closed anything
} else { } else {
fixupStack.push( { 'expectedType': '/' + data[i].type, 'openings': openings, 'reopenElements': reopenElements } ); fixupStack.push( {
'expectedType': '/' + data[i].type,
'openings': openings,
'reopenElements': reopenElements
} );
parentType = data[i].type; parentType = data[i].type;
} }
} else { } else {
// Closing // Closing
writeElement( data[i], i ); writeElement( data[i], i );
// TODO don't close fixup stuff if the next thing immediately needs to be fixed up as well; // TODO don't close fixup stuff if the next thing immediately needs to be fixed up as
// instead, merge the two wrappers // well; instead, merge the two wrappers
if ( fixupStack.length > 0 && fixupStack[fixupStack.length - 1].expectedType == data[i].type ) { if (
fixupStack.length > 0 &&
fixupStack[fixupStack.length - 1].expectedType === data[i].type
) {
popped = fixupStack.pop(); popped = fixupStack.pop();
// Go through these in reverse! // Go through these in reverse!
for ( j = popped.openings.length - 1; j >= 0; j-- ) { for ( j = popped.openings.length - 1; j >= 0; j-- ) {
@ -1134,14 +1149,18 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
writeElement( popped.reopenElements[j], i ); writeElement( popped.reopenElements[j], i );
} }
} }
parentType = openingStack.length > 0 ? openingStack[openingStack.length - 1] : parentNode.getType(); parentType = openingStack.length > 0 ?
openingStack[openingStack.length - 1] : parentNode.getType();
} }
} }
if ( inTextNode ) { if ( inTextNode ) {
// We're leaving a text node, process fixupStack if needed // We're leaving a text node, process fixupStack if needed
// TODO duplicated code // TODO duplicated code
if ( fixupStack.length > 0 && fixupStack[fixupStack.length - 1].expectedType == '/text' ) { if (
fixupStack.length > 0 &&
fixupStack[fixupStack.length - 1].expectedType === '/text'
) {
popped = fixupStack.pop(); popped = fixupStack.pop();
// Go through these in reverse! // Go through these in reverse!
for ( j = popped.openings.length - 1; j >= 0; j-- ) { for ( j = popped.openings.length - 1; j >= 0; j-- ) {
@ -1151,7 +1170,8 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
writeElement( popped.reopenElements[j], i ); writeElement( popped.reopenElements[j], i );
} }
} }
parentType = openingStack.length > 0 ? openingStack[openingStack.length - 1] : parentNode.getType(); parentType = openingStack.length > 0 ?
openingStack[openingStack.length - 1] : parentNode.getType();
} }
// Close unclosed openings // Close unclosed openings

View file

@ -218,7 +218,7 @@ ve.dm.Node.prototype.getClonedElement = function() {
retval.attributes = ve.copyObject( this.attributes ); retval.attributes = ve.copyObject( this.attributes );
} }
return retval; return retval;
} };
/** /**
* Checks if this node can be merged with another. * Checks if this node can be merged with another.

View file

@ -80,7 +80,7 @@ ve.dm.Surface.prototype.change = function( transaction, selection ) {
if ( selection && ( !this.selection || !this.selection.equals ( selection ) ) ) { if ( selection && ( !this.selection || !this.selection.equals ( selection ) ) ) {
selection.normalize(); selection.normalize();
this.selection = selection; this.selection = selection;
this.emit ('select', this.selection.clone() ); this.emit('select', this.selection.clone() );
} }
if ( transaction ) { if ( transaction ) {
this.emit( 'transact', transaction ); this.emit( 'transact', transaction );
@ -112,7 +112,7 @@ ve.dm.Surface.prototype.breakpoint = function( selection ) {
selection: selection || this.selection.clone() selection: selection || this.selection.clone()
} ); } );
this.smallStack = []; this.smallStack = [];
this.emit ( 'history' ); this.emit( 'history' );
} }
}; };
@ -128,7 +128,7 @@ ve.dm.Surface.prototype.undo = function() {
} }
var selection = item.selection; var selection = item.selection;
selection.end -= diff; selection.end -= diff;
this.emit ( 'history' ); this.emit( 'history' );
return selection; return selection;
} }
return null; return null;
@ -136,6 +136,7 @@ ve.dm.Surface.prototype.undo = function() {
ve.dm.Surface.prototype.redo = function() { ve.dm.Surface.prototype.redo = function() {
this.breakpoint(); this.breakpoint();
var selection;
if ( this.undoIndex > 0 ) { if ( this.undoIndex > 0 ) {
if ( this.bigStack[this.bigStack.length - this.undoIndex] ) { if ( this.bigStack[this.bigStack.length - this.undoIndex] ) {
var diff = 0; var diff = 0;
@ -144,11 +145,11 @@ ve.dm.Surface.prototype.redo = function() {
this.documentModel.commit( item.stack[i] ); this.documentModel.commit( item.stack[i] );
diff += item.stack[i].lengthDifference; diff += item.stack[i].lengthDifference;
} }
var selection = item.selection; selection = item.selection;
selection.end += diff; selection.end += diff;
} }
this.undoIndex--; this.undoIndex--;
this.emit ( 'history' ); this.emit( 'history' );
return selection; return selection;
} }
return null; return null;

View file

@ -59,7 +59,8 @@ ve.dm.Transaction.newFromInsertion = function( doc, offset, insertion ) {
*/ */
ve.dm.Transaction.newFromRemoval = function( doc, range ) { ve.dm.Transaction.newFromRemoval = function( doc, range ) {
var tx = new ve.dm.Transaction(), var tx = new ve.dm.Transaction(),
data = doc.getData(); data = doc.getData(),
i;
// Normalize and validate range // Normalize and validate range
range.normalize(); range.normalize();
if ( range.start === range.end ) { if ( range.start === range.end ) {
@ -375,11 +376,9 @@ ve.dm.Transaction.newFromWrap = function( doc, range, unwrapOuter, wrapOuter, un
// TODO figure out if we should use the tree/node functions here // TODO figure out if we should use the tree/node functions here
// rather than iterating over offsets, it may or may not be faster // rather than iterating over offsets, it may or may not be faster
for ( i = range.start; i < range.end; i++ ) { for ( i = range.start; i < range.end; i++ ) {
if ( doc.data[i].type === undefined ) { if ( doc.data[i].type !== undefined ) {
// This is a content offset, skip
} else {
// This is a structural offset // This is a structural offset
if ( doc.data[i].type.charAt( 0 ) != '/' ) { if ( doc.data[i].type.charAt( 0 ) !== '/' ) {
// This is an opening element // This is an opening element
if ( depth === 0 ) { if ( depth === 0 ) {
// We are at the start of a top-level element // We are at the start of a top-level element

View file

@ -245,7 +245,7 @@ ve.dm.TransactionProcessor.processors.replace = function( op ) {
scopeEnd, scopeEnd,
opAdjustment = 0; opAdjustment = 0;
while ( true ) { while ( true ) {
if ( operation.type == 'replace' ) { if ( operation.type === 'replace' ) {
var opRemove = this.reversed ? operation.insert : operation.remove, var opRemove = this.reversed ? operation.insert : operation.remove,
opInsert = this.reversed ? operation.remove : operation.insert; opInsert = this.reversed ? operation.remove : operation.insert;
// Update the linear model for this insert // Update the linear model for this insert
@ -282,14 +282,14 @@ ve.dm.TransactionProcessor.processors.replace = function( op ) {
// only consistent if both levels are zero. // only consistent if both levels are zero.
for ( i = 0; i < opRemove.length; i++ ) { for ( i = 0; i < opRemove.length; i++ ) {
type = opRemove[i].type; type = opRemove[i].type;
if ( type === undefined ) { if ( type !== undefined ) {
// This is content, ignore if ( type.charAt( 0 ) === '/' ) {
} else if ( type.charAt( 0 ) === '/' ) { // Closing element
// Closing element removeLevel--;
removeLevel--; } else {
} else { // Opening element
// Opening element removeLevel++;
removeLevel++; }
} }
} }
// Keep track of the scope of the insertion // Keep track of the scope of the insertion
@ -298,27 +298,27 @@ ve.dm.TransactionProcessor.processors.replace = function( op ) {
// in which case it's the affected ancestor // in which case it's the affected ancestor
for ( i = 0; i < opInsert.length; i++ ) { for ( i = 0; i < opInsert.length; i++ ) {
type = opInsert[i].type; type = opInsert[i].type;
if ( type === undefined ) { if ( type !== undefined ) {
// This is content, ignore if ( type.charAt( 0 ) === '/' ) {
} else if ( type.charAt( 0 ) === '/' ) { // Closing element
// Closing element insertLevel--;
insertLevel--; if ( insertLevel < minInsertLevel ) {
if ( insertLevel < minInsertLevel ) { // Closing an unopened element at a higher
// Closing an unopened element at a higher // (more negative) level than before
// (more negative) level than before // Lazy-initialize scope
// Lazy-initialize scope scope = scope || this.document.getNodeFromOffset( prevCursor );
scope = scope || this.document.getNodeFromOffset( prevCursor ); // Push the full range of the old scope as an affected range
// Push the full range of the old scope as an affected range scopeStart = this.document.getDocumentNode().getOffsetFromNode( scope );
scopeStart = this.document.getDocumentNode().getOffsetFromNode( scope ); scopeEnd = scopeStart + scope.getOuterLength();
scopeEnd = scopeStart + scope.getOuterLength(); affectedRanges.push( new ve.Range( scopeStart, scopeEnd ) );
affectedRanges.push( new ve.Range( scopeStart, scopeEnd ) ); // Update scope
// Update scope scope = scope.getParent() || scope;
scope = scope.getParent() || scope; }
}
} else { } else {
// Opening element // Opening element
insertLevel++; insertLevel++;
}
} }
} }
// Update adjustment // Update adjustment
@ -374,7 +374,7 @@ ve.dm.TransactionProcessor.prototype.executeOperation = function( op ) {
if ( op.type in ve.dm.TransactionProcessor.processors ) { if ( op.type in ve.dm.TransactionProcessor.processors ) {
ve.dm.TransactionProcessor.processors[op.type].call( this, op ); ve.dm.TransactionProcessor.processors[op.type].call( this, op );
} else { } else {
throw 'Invalid operation error. Operation type is not supported: ' + operation.type; throw 'Invalid operation error. Operation type is not supported: ' + op.type;
} }
}; };

View file

@ -1,3 +1,5 @@
/*global mw,confirm,alert */
/** /**
* Edit page target. * Edit page target.
* *

View file

@ -1,3 +1,5 @@
/*global mw */
/** /**
* Generic target. * Generic target.
* *
@ -19,7 +21,7 @@ ve.init.Target = function( pageName ) {
this.dom = null; this.dom = null;
this.isMobileDevice = ( this.isMobileDevice = (
'ontouchstart' in window || 'ontouchstart' in window ||
( window.DocumentTouch && document instanceof DocumentTouch ) ( window.DocumentTouch && document instanceof window.DocumentTouch )
); );
}; };

View file

@ -28,10 +28,11 @@ ve.ui.LinkInspector = function( toolbar, context ) {
return; return;
} }
var surfaceModel = _this.context.getSurfaceView().getModel(), var hash,
surfaceModel = _this.context.getSurfaceView().getModel(),
annotations = _this.getSelectedLinkAnnotations(); annotations = _this.getSelectedLinkAnnotations();
// If link annotation exists, clear it. // If link annotation exists, clear it.
for ( var hash in annotations ) { for ( hash in annotations ) {
surfaceModel.annotate( 'clear', annotations[hash] ); surfaceModel.annotate( 'clear', annotations[hash] );
} }
@ -66,8 +67,9 @@ ve.ui.LinkInspector.prototype.getSelectedLinkAnnotations = function(){
}; };
ve.ui.LinkInspector.prototype.getAnnotationFromSelection = function() { ve.ui.LinkInspector.prototype.getAnnotationFromSelection = function() {
var annotations = this.getSelectedLinkAnnotations(); var hash,
for ( var hash in annotations ) { annotations = this.getSelectedLinkAnnotations();
for ( hash in annotations ) {
// Use the first one with a recognized type (there should only be one, but this is just in case) // Use the first one with a recognized type (there should only be one, but this is just in case)
if ( annotations[hash].type === 'link/wikiLink' || annotations[hash].type === 'link/extLink' ) { if ( annotations[hash].type === 'link/wikiLink' || annotations[hash].type === 'link/extLink' ) {
return annotations[hash]; return annotations[hash];
@ -78,13 +80,14 @@ ve.ui.LinkInspector.prototype.getAnnotationFromSelection = function() {
// TODO: This should probably be somewhere else but I needed this here for now. // TODO: This should probably be somewhere else but I needed this here for now.
ve.ui.LinkInspector.prototype.getSelectionText = function() { ve.ui.LinkInspector.prototype.getSelectionText = function() {
var surfaceView = this.context.getSurfaceView(), var i,
surfaceView = this.context.getSurfaceView(),
surfaceModel = surfaceView.getModel(), surfaceModel = surfaceView.getModel(),
documentModel = surfaceModel.getDocument(), documentModel = surfaceModel.getDocument(),
data = documentModel.getData( surfaceModel.getSelection() ), data = documentModel.getData( surfaceModel.getSelection() ),
str = '', str = '',
max = Math.min( data.length, 255 ); max = Math.min( data.length, 255 );
for ( var i = 0; i < max; i++ ) { for ( i = 0; i < max; i++ ) {
if ( ve.isArray( data[i] ) ) { if ( ve.isArray( data[i] ) ) {
str += data[i][0]; str += data[i][0];
} else if( typeof data[i] === 'string' ) { } else if( typeof data[i] === 'string' ) {

View file

@ -158,7 +158,7 @@ ve.FormatDropdownTool.prototype.getMatchingMenuItems = function( nodes ) {
itemLoop: itemLoop:
for ( var j = 0; j < items.length; j++ ) { for ( var j = 0; j < items.length; j++ ) {
var item = items[j]; var item = items[j];
if ( item.type == nodeType ) { if ( item.type === nodeType ) {
if ( item.attributes && nodeAttributes ) { if ( item.attributes && nodeAttributes ) {
// Compare attributes // Compare attributes
for ( var key in item.attributes ) { for ( var key in item.attributes ) {
@ -166,7 +166,7 @@ ve.FormatDropdownTool.prototype.getMatchingMenuItems = function( nodes ) {
// Node must have all the required attributes // Node must have all the required attributes
!( key in nodeAttributes ) || !( key in nodeAttributes ) ||
// Use weak comparison because numbers sometimes come through as strings // Use weak comparison because numbers sometimes come through as strings
item.attributes[key] != nodeAttributes[key] item.attributes[key] !== nodeAttributes[key]
) { ) {
// Skip to the next menu item // Skip to the next menu item
continue itemLoop; continue itemLoop;

View file

@ -23,7 +23,7 @@ ve.ui.IndentationButtonTool.prototype.onClick = function() {
for ( i = 0; i < this.nodes.length; i++ ) { for ( i = 0; i < this.nodes.length; i++ ) {
listItem = this.nodes[i].getParent(); listItem = this.nodes[i].getParent();
if ( listItems.length > 0 ) { if ( listItems.length > 0 ) {
if (listItem != listItems[listItems.length - 1]) { if (listItem !== listItems[listItems.length - 1]) {
listItems.push( listItem ); listItems.push( listItem );
} }
} else { } else {
@ -42,7 +42,8 @@ ve.ui.IndentationButtonTool.prototype.indent = function( listItems ) {
// FIXME old code, doesn't work // FIXME old code, doesn't work
var surface = this.toolbar.surfaceView, var surface = this.toolbar.surfaceView,
styles, styles,
i; i,
tx;
for ( i = 0; i < listItems.length; i++ ) { for ( i = 0; i < listItems.length; i++ ) {
styles = listItems[i].getElementAttribute( 'styles' ); styles = listItems[i].getElementAttribute( 'styles' );
@ -62,7 +63,8 @@ ve.ui.IndentationButtonTool.prototype.outdent = function( listItems ) {
// FIXME old code, doesn't work // FIXME old code, doesn't work
var surface = this.toolbar.surfaceView, var surface = this.toolbar.surfaceView,
styles, styles,
i; i,
tx;
for ( i = 0; i < listItems.length; i++ ) { for ( i = 0; i < listItems.length; i++ ) {
styles = listItems[i].getElementAttribute( 'styles' ); styles = listItems[i].getElementAttribute( 'styles' );

View file

@ -202,7 +202,6 @@ ve.ui.Context.prototype.getInspector = function( name ) {
}; };
ve.ui.Context.prototype.addInspector = function( name, inspector ) { ve.ui.Context.prototype.addInspector = function( name, inspector ) {
var _this = this;
if ( name in this.inspectors ) { if ( name in this.inspectors ) {
throw 'Duplicate inspector error. Previous registration with the same name: ' + name; throw 'Duplicate inspector error. Previous registration with the same name: ' + name;
} }
@ -210,7 +209,7 @@ ve.ui.Context.prototype.addInspector = function( name, inspector ) {
this.inspectors[name] = inspector; this.inspectors[name] = inspector;
// Iframe build code below. // Iframe build code below.
// TODO: Rework this to allow multiple inspectors // TODO: Rework this to allow multiple inspectors
$styleLink = var $styleLink =
$('<link />') $('<link />')
.attr({ .attr({
'rel': 'stylesheet', 'rel': 'stylesheet',

View file

@ -26,7 +26,6 @@ ve.ui.Toolbar = function( $container, surfaceView, config ) {
{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] } { 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }
]; ];
this.setup(); this.setup();
}; };
/* Methods */ /* Methods */
@ -44,7 +43,8 @@ ve.ui.Toolbar.prototype.updateTools = function() {
range = model.getSelection(), range = model.getSelection(),
startNode, startNode,
endNode, endNode,
_this = this; _this = this,
i;
if ( range !== null ) { if ( range !== null ) {
if ( range.from === range.to ){ if ( range.from === range.to ){

View file

@ -1,3 +1,5 @@
/*global mw */
/** /**
* VisualEditor User Interface namespace. * VisualEditor User Interface namespace.
* *

View file

@ -54,7 +54,7 @@ ve.BranchNode.prototype.indexOf = function( node ) {
* @param {ve.Node} root Node to use as root * @param {ve.Node} root Node to use as root
*/ */
ve.BranchNode.prototype.setRoot = function( root ) { ve.BranchNode.prototype.setRoot = function( root ) {
if ( root == this.root ) { if ( root === this.root ) {
// Nothing to do, don't recurse into all descendants // Nothing to do, don't recurse into all descendants
return; return;
} }
@ -72,7 +72,7 @@ ve.BranchNode.prototype.setRoot = function( root ) {
* @param {ve.Document} root Node to use as root * @param {ve.Document} root Node to use as root
*/ */
ve.BranchNode.prototype.setDocument = function( doc ) { ve.BranchNode.prototype.setDocument = function( doc ) {
if ( doc == this.doc ) { if ( doc === this.doc ) {
// Nothing to do, don't recurse into all descendants // Nothing to do, don't recurse into all descendants
return; return;
} }
@ -106,7 +106,7 @@ ve.BranchNode.prototype.getNodeFromOffset = function( offset, shallow ) {
childNode; childNode;
for ( var i = 0, length = this.children.length; i < length; i++ ) { for ( var i = 0, length = this.children.length; i < length; i++ ) {
childNode = this.children[i]; childNode = this.children[i];
if ( offset == nodeOffset ) { if ( offset === nodeOffset ) {
// The requested offset is right before childNode, // The requested offset is right before childNode,
// so it's not inside any of this's children, but inside this // so it's not inside any of this's children, but inside this
return this; return this;
@ -121,7 +121,7 @@ ve.BranchNode.prototype.getNodeFromOffset = function( offset, shallow ) {
} }
nodeOffset += nodeLength; nodeOffset += nodeLength;
} }
if ( offset == nodeOffset ) { if ( offset === nodeOffset ) {
// The requested offset is right before this.children[i], // The requested offset is right before this.children[i],
// so it's not inside any of this's children, but inside this // so it's not inside any of this's children, but inside this
return this; return this;

View file

@ -113,11 +113,11 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
// Does the node have wrapping elements around it // Does the node have wrapping elements around it
isWrapped = node.isWrapped(); isWrapped = node.isWrapped();
// Is the start between prevNode and node or between the parent's opening and node? // Is the start between prevNode and node or between the parent's opening and node?
startBetween = isWrapped ? start == left - 1 : start == left; startBetween = isWrapped ? start === left - 1 : start === left;
// Is the end between node and nextNode or between node and the parent's closing? // Is the end between node and nextNode or between node and the parent's closing?
endBetween = isWrapped ? end == right + 1 : end == right; endBetween = isWrapped ? end === right + 1 : end === right;
if ( start == end && ( startBetween || endBetween ) && node.isWrapped() ) { if ( start === end && ( startBetween || endBetween ) && node.isWrapped() ) {
// Empty range in the parent, outside of any child // Empty range in the parent, outside of any child
nodeRange = new ve.Range( currentFrame.startOffset, nodeRange = new ve.Range( currentFrame.startOffset,
currentFrame.startOffset + currentFrame.node.getLength() currentFrame.startOffset + currentFrame.node.getLength()
@ -204,8 +204,10 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
} ]; } ];
} }
} else if ( startInside ) { } else if ( startInside ) {
if ( ( mode === 'leaves' || mode === 'covered' ) if (
&& node.children && node.children.length ( mode === 'leaves' || mode === 'covered' ) &&
node.children &&
node.children.length
) { ) {
// node is a branch node and the start is inside it // node is a branch node and the start is inside it
// Descend into it // Descend into it
@ -262,8 +264,10 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
return retval; return retval;
} }
} else if ( endInside ) { } else if ( endInside ) {
if ( ( mode === 'leaves' || mode === 'covered' ) if (
&& node.children && node.children.length ( mode === 'leaves' || mode === 'covered' ) &&
node.children &&
node.children.length
) { ) {
// node is a branch node and the end is inside it // node is a branch node and the end is inside it
// Descend into it // Descend into it

View file

@ -159,7 +159,7 @@ ve.Surface.prototype.makeMainEditorToolbarFloat = function() {
return; return;
} }
var $toolbarWrapper = this.toolbarWrapper.top, var $toolbarWrapper = this.toolbarWrapper.top,
$toolbar = $toolbarWrapper.find( '.es-toolbar' ); $toolbar = $toolbarWrapper.find( '.es-toolbar' ),
$window = $( window ); $window = $( window );
$window.scroll( function() { $window.scroll( function() {

View file

@ -1,3 +1,5 @@
/*global mw */
/** /**
* VisualEditor namespace. * VisualEditor namespace.
* *

View file

@ -120,7 +120,7 @@ test( 'getHtml', 22, function() {
}], }],
'g', 'g',
'h', 'h',
'i', 'i',
{ 'type': '/paragraph' } { 'type': '/paragraph' }
], ],
'html': 'abc<b><i><u>def</u></i></b>ghi' 'html': 'abc<b><i><u>def</u></i></b>ghi'
@ -147,7 +147,7 @@ test( 'getHtml', 22, function() {
}], }],
'g', 'g',
'h', 'h',
'i', 'i',
{ 'type': '/paragraph' } { 'type': '/paragraph' }
], ],
'html': 'abc<b><i><u>d</u></i></b><i><u>e<b>f</b></u></i>ghi' 'html': 'abc<b><i><u>d</u></i></b><i><u>e<b>f</b></u></i>ghi'
@ -174,7 +174,7 @@ test( 'getHtml', 22, function() {
}], }],
'g', 'g',
'h', 'h',
'i', 'i',
{ 'type': '/paragraph' } { 'type': '/paragraph' }
], ],
'html': 'abc<i><u><b>d</b>e<b>f</b></u></i>ghi' 'html': 'abc<i><u><b>d</b>e<b>f</b></u></i>ghi'
@ -200,7 +200,7 @@ test( 'getHtml', 22, function() {
}], }],
'g', 'g',
'h', 'h',
'i', 'i',
{ 'type': '/paragraph' } { 'type': '/paragraph' }
], ],
'html': 'abc<i><u><b>d</b></u></i><u><b>ef</b></u>ghi' 'html': 'abc<i><u><b>d</b></u></i><u><b>ef</b></u>ghi'

View file

@ -102,7 +102,7 @@ test( 'annotate', 1, function() {
for ( var i = 0; i < cases.length; i++ ) { for ( var i = 0; i < cases.length; i++ ) {
surface = new ve.dm.SurfaceStub( cases[i].data ); surface = new ve.dm.SurfaceStub( cases[i].data );
surface.change( null, new ve.Range( 0, surface.getDocument().getData().length ) ); surface.change( null, new ve.Range( 0, surface.getDocument().getData().length ) );
surface.annotate( cases[i].annotate['method'], cases[i].annotate['annotation'] ); surface.annotate( cases[i].annotate.method, cases[i].annotate.annotation );
deepEqual( surface.getDocument().getData(), cases[i].expected, cases[i].msg ); deepEqual( surface.getDocument().getData(), cases[i].expected, cases[i].msg );
} }

View file

@ -148,35 +148,7 @@ test( 'traverseLeafNodes', 1, function() {
'desc': 'Passing a sibling for from results in an exception' 'desc': 'Passing a sibling for from results in an exception'
} }
]; ];
var count=0;
for(var t=0;t<tests.length;t++) {
if(tests[t].hasOwnProperty('reverse')) {
count++;
}
count++;
}
expect (count);
for ( var i = 0; i < tests.length; i++ ) {
executeTest( tests[i] );
if ( tests[i].reverse !== undefined ) {
var reversed = {
'node': tests[i].node,
'from': tests[i].from,
'callback': tests[i].callback,
'exception': tests[i].exception,
'isReversed': true,
'desc': tests[i].reverseDesc || tests[i].desc + ' (in reverse)'
};
if ( tests[i].output !== undefined && tests[i].reverse === true ) {
reversed.output = tests[i].output.reverse();
} else {
reversed.output = tests[i].reverse;
}
executeTest( reversed );
}
}
function executeTest( test ) { function executeTest( test ) {
var realLeaves = [], var realLeaves = [],
callback = function( node ) { callback = function( node ) {
@ -199,4 +171,33 @@ test( 'traverseLeafNodes', 1, function() {
ok( ve.compareArrays( realLeaves, test.output ), test.desc ); ok( ve.compareArrays( realLeaves, test.output ), test.desc );
} }
} }
} );
var count = 0;
for ( var t = 0; t < tests.length; t++ ) {
if ( tests[t].hasOwnProperty( 'reverse' ) ) {
count++;
}
count++;
}
expect( count );
for ( var i = 0; i < tests.length; i++ ) {
executeTest( tests[i] );
if ( tests[i].reverse !== undefined ) {
var reversed = {
'node': tests[i].node,
'from': tests[i].from,
'callback': tests[i].callback,
'exception': tests[i].exception,
'isReversed': true,
'desc': tests[i].reverseDesc || tests[i].desc + ' (in reverse)'
};
if ( tests[i].output !== undefined && tests[i].reverse === true ) {
reversed.output = tests[i].output.reverse();
} else {
reversed.output = tests[i].reverse;
}
executeTest( reversed );
}
}
} );

View file

@ -296,7 +296,8 @@ ve.example.getSelectNodesCases = function( doc ) {
* @returns {Object} Summary of node tree * @returns {Object} Summary of node tree
*/ */
ve.example.getNodeTreeSummary = function( node, shallow ) { ve.example.getNodeTreeSummary = function( node, shallow ) {
var summary = { var i,
summary = {
'getType': node.getType(), 'getType': node.getType(),
'getLength': node.getLength(), 'getLength': node.getLength(),
'getOuterLength': node.getOuterLength(), 'getOuterLength': node.getOuterLength(),
@ -306,7 +307,7 @@ ve.example.getNodeTreeSummary = function( node, shallow ) {
summary['children.length'] = node.children.length; summary['children.length'] = node.children.length;
if ( !shallow ) { if ( !shallow ) {
summary.children = []; summary.children = [];
for ( var i = 0; i < node.children.length; i++ ) { for ( i = 0; i < node.children.length; i++ ) {
summary.children.push( ve.example.getNodeTreeSummary( node.children[i] ) ); summary.children.push( ve.example.getNodeTreeSummary( node.children[i] ) );
} }
} }
@ -325,12 +326,13 @@ ve.example.getNodeTreeSummary = function( node, shallow ) {
* @returns {Object} Summary of selection * @returns {Object} Summary of selection
*/ */
ve.example.getNodeSelectionSummary = function( selection ) { ve.example.getNodeSelectionSummary = function( selection ) {
var summary = { var i,
summary = {
'length': selection.length 'length': selection.length
}; };
if ( selection.length ) { if ( selection.length ) {
summary.results = []; summary.results = [];
for ( var i = 0; i < selection.length; i++ ) { for ( i = 0; i < selection.length; i++ ) {
summary.results.push( { summary.results.push( {
'node': ve.example.getNodeTreeSummary( selection[i].node, true ), 'node': ve.example.getNodeTreeSummary( selection[i].node, true ),
'range': selection[i].range, 'range': selection[i].range,
@ -382,16 +384,18 @@ ve.example.getDomElementSummary = function( element ) {
* @param {ve.Node} Node at given path * @param {ve.Node} Node at given path
*/ */
ve.example.lookupNode = function( root ) { ve.example.lookupNode = function( root ) {
var node = root; var i,
for ( var i = 1; i < arguments.length; i++ ) { node = root;
for ( i = 1; i < arguments.length; i++ ) {
node = node.children[arguments[i]]; node = node.children[arguments[i]];
} }
return node; return node;
}; };
ve.example.createDomElement = function( type, attributes ) { ve.example.createDomElement = function( type, attributes ) {
var element = document.createElement( type ); var key,
for ( var key in attributes ) { element = document.createElement( type );
for ( key in attributes ) {
element.setAttribute( key, attributes[key] ); element.setAttribute( key, attributes[key] );
} }
return element; return element;