mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 18:39:52 +00:00
f06952f2f3
Refactor: * ve.indexOf Renamed from ve.inArray. This was named after the jQuery method which in turn has a longer story about why it is so unfortunately named. It doesn't return a boolean, but an index. Hence the native method being called indexOf as well. * ve.bind Renamed from ve.proxy. I considered making it use Function.prototype.bind if available. As it performs better than $.proxy (which doesn't use to the native bind if available). However since bind needs to be bound itself in order to use it detached, it turns out with the "call()" and "bind()" it is slower than the $.proxy shim: http://jsperf.com/function-bind-shim-perf It would've been like this: ve.bind = Function.prototype.bind ? Function.prototype.call.bind( Function.prototype.bind ) : $.proxy; But instead sticking to ve.bind = $.proxy; * ve.extendObject Documented the parts of jQuery.extend that we use. This makes it easier to replace in the future. Documentation: * Added function documentation blocks. * Added annotations to functions that we will be able to remove in the future in favour of the native methods. With "@until + when/how". In this case "ES5". Meaning, whenever we drop support for browsers that don't support ES5. Although in the developer community ES5 is still fairly fresh, browsers have been aware for it long enough that thee moment we're able to drop it may be sooner than we think. The only blocker so far is IE8. The rest of the browsers have had it long enough that the traffic we need to support of non-IE supports it. Misc.: * Removed 'node: true' from .jshintrc since Parsoid is no longer in this repo and thus no more nodejs files. - This unraveled two lint errors: Usage of 'module' and 'console'. (both were considered 'safe globals' due to nodejs, but not in browser code). * Replaced usage (before renaming): - $.inArray -> ve.inArray - Function.prototype.bind -> ve.proxy - Array.isArray -> ve.isArray - [].indexOf -> ve.inArray - $.fn.bind/live/delegate/unbind/die/delegate -> $.fn.on/off Change-Id: Idcf1fa6a685b6ed3d7c99ffe17bd57a7bc586a2c
181 lines
4.5 KiB
JavaScript
181 lines
4.5 KiB
JavaScript
/**
|
|
* VisualEditor data model Surface class.
|
|
*
|
|
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* DataModel surface.
|
|
*
|
|
* @class
|
|
* @constructor
|
|
* @extends {ve.EventEmitter}
|
|
* @param {ve.dm.Document} doc Document model to create surface for
|
|
*/
|
|
ve.dm.Surface = function ( doc ) {
|
|
// Inheritance
|
|
ve.EventEmitter.call( this );
|
|
// Properties
|
|
this.documentModel = doc;
|
|
this.selection = null;
|
|
this.smallStack = [];
|
|
this.bigStack = [];
|
|
this.undoIndex = 0;
|
|
this.historyTrackingInterval = null;
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
ve.dm.Surface.prototype.startHistoryTracking = function () {
|
|
this.historyTrackingInterval = setInterval( ve.bind( this.breakpoint, this ), 750 );
|
|
};
|
|
|
|
ve.dm.Surface.prototype.stopHistoryTracking = function () {
|
|
clearInterval( this.historyTrackingInterval );
|
|
};
|
|
|
|
ve.dm.Surface.prototype.purgeHistory = function () {
|
|
this.selection = null;
|
|
this.smallStack = [];
|
|
this.bigStack = [];
|
|
this.undoIndex = 0;
|
|
};
|
|
|
|
ve.dm.Surface.prototype.getHistory = function () {
|
|
if ( this.smallStack.length > 0 ) {
|
|
return this.bigStack.slice( 0 ).concat( [{ 'stack': this.smallStack.slice(0) }] );
|
|
} else {
|
|
return this.bigStack.slice( 0 );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets the document model of the surface.
|
|
*
|
|
* @method
|
|
* @returns {ve.dm.DocumentNode} Document model of the surface
|
|
*/
|
|
ve.dm.Surface.prototype.getDocument = function () {
|
|
return this.documentModel;
|
|
};
|
|
|
|
/**
|
|
* Gets the selection
|
|
*
|
|
* @method
|
|
* @returns {ve.Range} Current selection
|
|
*/
|
|
ve.dm.Surface.prototype.getSelection = function () {
|
|
return this.selection;
|
|
};
|
|
|
|
/**
|
|
* Gets a fragment from this document and selection.
|
|
*
|
|
* @method
|
|
* @returns {ve.dm.SurfaceFragment} Surface fragment
|
|
*/
|
|
ve.dm.Surface.prototype.getFragment = function () {
|
|
return new ve.dm.SurfaceFragment( this );
|
|
};
|
|
|
|
/**
|
|
* Applies a series of transactions to the content data and sets the selection.
|
|
*
|
|
* @method
|
|
* @param {ve.dm.Transaction|null} transaction Transaction to apply to the document
|
|
* @param {ve.Range|undefined} selection
|
|
*/
|
|
ve.dm.Surface.prototype.change = function ( transaction, selection ) {
|
|
if ( transaction ) {
|
|
this.bigStack = this.bigStack.slice( 0, this.bigStack.length - this.undoIndex );
|
|
this.undoIndex = 0;
|
|
this.smallStack.push( transaction );
|
|
ve.dm.TransactionProcessor.commit( this.getDocument(), transaction );
|
|
}
|
|
if ( selection && ( !this.selection || !this.selection.equals ( selection ) ) ) {
|
|
selection.normalize();
|
|
this.selection = selection;
|
|
this.emit('select', this.selection.clone() );
|
|
}
|
|
if ( transaction ) {
|
|
this.emit( 'transact', transaction );
|
|
}
|
|
this.emit( 'change', transaction, selection );
|
|
};
|
|
|
|
/**
|
|
* Applies an annotation to the current selection
|
|
*
|
|
* @method
|
|
* @param {String} annotation action: toggle, clear, set
|
|
* @param {Object} annotation object to apply.
|
|
*/
|
|
ve.dm.Surface.prototype.annotate = function ( method, annotation ) {
|
|
var tx,
|
|
selection = this.getSelection();
|
|
if ( selection.getLength() ) {
|
|
selection = this.getDocument().trimOuterSpaceFromRange( selection );
|
|
tx = ve.dm.Transaction.newFromAnnotation(
|
|
this.getDocument(), selection, method, annotation
|
|
);
|
|
this.change( tx, selection );
|
|
}
|
|
};
|
|
|
|
ve.dm.Surface.prototype.breakpoint = function ( selection ) {
|
|
if ( this.smallStack.length > 0 ) {
|
|
this.bigStack.push( {
|
|
stack: this.smallStack,
|
|
selection: selection || this.selection.clone()
|
|
} );
|
|
this.smallStack = [];
|
|
this.emit( 'history' );
|
|
}
|
|
};
|
|
|
|
ve.dm.Surface.prototype.undo = function () {
|
|
var diff, item, i, selection;
|
|
this.breakpoint();
|
|
this.undoIndex++;
|
|
if ( this.bigStack[this.bigStack.length - this.undoIndex] ) {
|
|
diff = 0;
|
|
item = this.bigStack[this.bigStack.length - this.undoIndex];
|
|
for ( i = item.stack.length - 1; i >= 0; i-- ) {
|
|
this.documentModel.rollback( item.stack[i] );
|
|
diff += item.stack[i].lengthDifference;
|
|
}
|
|
selection = item.selection;
|
|
selection.end -= diff;
|
|
this.emit( 'history' );
|
|
return selection;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
ve.dm.Surface.prototype.redo = function () {
|
|
var selection, diff, item, i;
|
|
this.breakpoint();
|
|
if ( this.undoIndex > 0 ) {
|
|
if ( this.bigStack[this.bigStack.length - this.undoIndex] ) {
|
|
diff = 0;
|
|
item = this.bigStack[this.bigStack.length - this.undoIndex];
|
|
for ( i = 0; i < item.stack.length; i++ ) {
|
|
this.documentModel.commit( item.stack[i] );
|
|
diff += item.stack[i].lengthDifference;
|
|
}
|
|
selection = item.selection;
|
|
selection.end += diff;
|
|
}
|
|
this.undoIndex--;
|
|
this.emit( 'history' );
|
|
return selection;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.extendClass( ve.dm.Surface, ve.EventEmitter );
|