https://www.mediawiki.org/wiki/Special:Code/MediaWiki/112755
Revision: 112755
Author: inez
Date: 2012-03-01 01:28:39 +0000 (Thu, 01 Mar 2012)
Log Message:
-----------
Migreate text input method from playground to ce and ce demo
Modified Paths:
--------------
trunk/extensions/VisualEditor/modules/ve/ce/ve.es.Surface.js
Modified: trunk/extensions/VisualEditor/modules/ve/ce/ve.es.Surface.js
===================================================================
--- trunk/extensions/VisualEditor/modules/ve/ce/ve.es.Surface.js
2012-03-01 01:21:55 UTC (rev 112754)
+++ trunk/extensions/VisualEditor/modules/ve/ce/ve.es.Surface.js
2012-03-01 01:28:39 UTC (rev 112755)
@@ -19,66 +19,74 @@
this.model = model;
this.documentView = new ve.es.DocumentNode( this.model.getDocument(),
this );
this.contextView = null;
- this.clipboard = {};
this.$ = $container
.addClass( 'es-surfaceView' )
.append( this.documentView.$ );
this.emitUpdateTimeout = undefined;
- this.node = null;
// Events
this.documentView.$.bind( {
'focus': function( e ) {
+ _this.documentOnFocus();
$document.unbind( '.ce-surfaceView' );
$document.bind( {
'keydown.ce-surfaceView': function( e ) {
- return _this.onKeyDown( e );
+// return _this.onKeyDown( e );
},
} );
- _this.setNode();
},
'blur': function( e ) {
+ _this.documentOnBlur();
$document.unbind( '.ce-surfaceView' );
}
} );
this.$.mousedown( function(e) {
- return _this.onMouseDown( e );
+// return _this.onMouseDown( e );
} );
+ // Initialization
+ this.documentView.renderContent();
+
// Prevent dragging text
this.$.bind('dragover drop', function(e) {
e.preventDefault();
});
- /*
- this.model.getDocument().on( 'update', function() {
- _this.emitUpdate( 25 );
- } );
+ this.poll = {
+ interval: null,
+ frequency: 100,
+ node: null,
+ prevText: null,
+ prevHash: null,
+ prevOffset: null,
+ compositionStart: null,
+ compositionEnd: null
+ };
- this.$.mousedown( function( e ) {
- return _this.onMouseDown( e );
+ document.addEventListener( 'compositionstart', function( e ) {
+ _this.onCompositionStart( e );
} );
-
- this.$.mouseup( function( e ) {
- //var selection = _this.getSelection();
- //_this.showFakeCursorAt(selection.start);
+ document.addEventListener( 'compositionend', function( e ) {
+ _this.onCompositionEnd( e );
} );
+};
- this.$.on('paste', function( e ) {
- _this.onPaste( e );
- } );
+/* Methods */
- this.$.on('cut copy', function( e ) {
- _this.onCutCopy( e );
- } );
- */
+ve.es.Surface.prototype.onCompositionStart = function( e ) {
+ this.stopPolling();
- // Initialization
- this.documentView.renderContent();
+ var rangySel = rangy.getSelection();
+ this.poll.compositionStart = this.getOffset( rangySel.anchorNode,
rangySel.anchorOffset, false );
};
-/* Methods */
+ve.es.Surface.prototype.onCompositionEnd = function( e ) {
+ var rangySel = rangy.getSelection();
+ this.poll.compositionEnd = this.getOffset( rangySel.focusNode,
rangySel.focusOffset, false );
+
+ this.startPolling();
+};
ve.es.Surface.prototype.attachContextView = function( contextView ) {
this.contextView = contextView;
@@ -88,104 +96,117 @@
return this.model;
};
-ve.es.Surface.prototype.onKeyDown = function( e ) {
- switch ( e.keyCode ) {
- // Page up
- case 33:
- // Page down
- case 34:
- // Home
- case 36:
- // End
- case 35:
- // Up arrow
- case 38:
- // Down arrow
- case 40:
- this.setNode();
- break;
- // Left arrow
- case 37:
- this.setNode( 'left' );
- break;
- // Right arrow
- case 39:
- this.setNode( 'right' );
- break;
+ve.es.Surface.prototype.documentOnFocus = function() {
+ this.startPolling();
+};
+
+ve.es.Surface.prototype.documentOnBlur = function() {
+ this.stopPolling();
+};
+
+ve.es.Surface.prototype.startPolling = function() {
+ if ( this.poll.interval === null ) {
+ var _this = this;
+ this.poll.interval = setInterval( function() {
+ _this.pollContent();
+ }, this.poll.frequency );
+ this.pollContent();
}
};
-ve.es.Surface.prototype.onMouseDown = function( e ) {
- this.setNode();
+ve.es.Surface.prototype.stopPolling = function() {
+ if ( this.poll.interval !== null ) {
+ clearInterval( this.poll.interval );
+ this.poll.interval = null;
+ }
};
-ve.es.Surface.prototype.setNode = function( direction ) {
- var _this = this;
-
- setTimeout( function() {
- var rangySelection = rangy.getSelection();
+ve.es.Surface.prototype.pollContent = function() {
+ if ( this.poll.compositionStart !== null && this.poll.compositionEnd
!== null ) {
+ var text = ve.es.Surface.getDOMText2( this.poll.node ),
+ hash = ve.es.Surface.getDOMHash( this.poll.node ),
+ localOffset = this.poll.compositionEnd;
+ this.poll.compositionStart = null;
+ this.poll.compositionEnd = null;
+ } else {
+ var rangySel = rangy.getSelection();
- if ( rangySelection.anchorNode === _this.node ) {
+ if ( rangySel.anchorNode === null ) {
return;
}
- if ( rangySelection.anchorNode.nodeType !== 3 ) {
- if ( _this.node === null ) {
- throw "Value of this.node shouldn't be a null";
- }
- if ( direction !== 'left' && direction !== 'right' ) {
- throw "At this point value of direction should
be 'left' or 'right'";
- }
- var oldOffset = _this.getOffset( _this.node, 0 ),
- newOffset;
+ var node = this.getLeafNode( rangySel.anchorNode )[0],
+ text = ve.es.Surface.getDOMText2( node ),
+ hash = ve.es.Surface.getDOMHash( node );
- if ( direction === 'left' ) {
- newOffset =
_this.documentView.model.getRelativeContentOffset( oldOffset, -1 );
- } else if ( direction === 'right' ) {
- newOffset =
_this.documentView.model.getRelativeContentOffset( oldOffset +
_this.node.length, 1 );
- }
- _this.showCursorAt( newOffset );
- _this.setNode();
+ if ( rangySel.anchorNode !== rangySel.focusNode ||
rangySel.anchorOffset !== rangySel.focusOffset ) {
+ var localOffset = null;
+ } else {
+ var localOffset = this.getOffset( rangySel.anchorNode,
rangySel.anchorOffset, false );
+ }
+
+ if ( node !== this.poll.node ) {
+ this.poll.node = node;
+ this.poll.prevText = text;
+ this.poll.prevHash = hash;
+ this.poll.prevOffset = localOffset;
return;
}
+ }
- _this.node = rangySelection.anchorNode;
-
- console.log(_this.node);
- }, 0 );
-};
+ if ( text !== this.poll.prevText ) {
+ var nodeOffset = this.documentView.getOffsetFromNode( $(
this.poll.node ).data( 'view' ) ),
+ lengthDiff = text.length - this.poll.prevText.length,
+ offsetDiff = ( localOffset !== null &&
this.poll.prevOffset !== null ) ? localOffset - this.poll.prevOffset : null;
-ve.es.Surface.prototype.getSelection = function() {
- var selection = rangy.getSelection();
-
- if ( selection.anchorNode === selection.focusNode &&
selection.anchorOffset === selection.focusOffset ) {
- // cursor
- var offset = this.getOffset( selection.anchorNode,
selection.anchorOffset );
- return new ve.Range( offset, offset );
- } else {
- // selection
- var offset1 = this.getOffset( selection.anchorNode,
selection.anchorOffset );
- var offset2 = this.getOffset( selection.focusNode,
selection.focusOffset );
- return new ve.Range( offset1, offset2 );
+ if ( lengthDiff === offsetDiff && this.poll.prevText.substring(
0, this.poll.prevOffset ) === text.substring( 0, this.poll.prevOffset ) ) {
+ var newData = text.substring( this.poll.prevOffset,
localOffset ).split( '' );
+ var annotations =
this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 +
this.poll.prevOffset - 1 );
+ ve.dm.DocumentNode.addAnnotationsToData( newData,
annotations );
+ this.model.transact(
this.documentView.model.prepareInsertion(
+ nodeOffset + 1 + this.poll.prevOffset,
+ newData
+ ) );
+ } else {
+ var sameFromLeft = 0,
+ sameFromRight = 0,
+ l = text.length > this.poll.prevText.length ?
this.poll.prevText.length : text.length;
+ while ( sameFromLeft < l &&
this.poll.prevText[sameFromLeft] == text[sameFromLeft] ) {
+ ++sameFromLeft;
+ }
+ l = l - sameFromLeft;
+ while ( sameFromRight < l &&
this.poll.prevText[this.poll.prevText.length - 1 - sameFromRight] ==
text[text.length - 1 - sameFromRight] ) {
+ ++sameFromRight;
+ }
+ this.model.transact(
this.documentView.model.prepareRemoval( new ve.Range(
+ nodeOffset + 1 + sameFromLeft,
+ nodeOffset + 1 + this.poll.prevText.length -
sameFromRight
+ ) ) );
+ var newData = text.substring( sameFromLeft, text.length
- sameFromRight ).split( '' );
+ var annotations =
this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 +
sameFromLeft - 1 );
+ ve.dm.DocumentNode.addAnnotationsToData( newData,
annotations );
+ this.model.transact(
this.documentView.model.prepareInsertion(
+ nodeOffset + 1 + sameFromLeft,
+ newData
+ ) );
+ }
+ this.poll.prevText = text;
}
-};
-
-ve.es.Surface.prototype.getOffset = function( localNode, localOffset ) {
- var $node = $( localNode );
-
- if ( $node.hasClass( 'ce-leafNode' ) ) {
- return this.documentView.getOffsetFromNode( $node.data('view')
) + 1;
+ if ( hash !== this.poll.prevHash ) {
+ // TODO: redisplay cursor in correct position (with setTimeout)
+ this.getLeafNode( this.poll.node ).data( 'view'
).renderContent();
+ this.poll.prevHash = hash;
}
- while( !$node.hasClass( 'ce-leafNode' ) ) {
- $node = $node.parent();
- }
-
- var current = [$node.contents(), 0];
- var stack = [current];
-
- var offset = 0;
-
+ this.poll.prevOffset = localOffset;
+};
+
+ve.es.Surface.prototype.getOffset = function( elem, offset, global ) {
+ var $leafNode = this.getLeafNode( elem ),
+ current = [$leafNode.contents(), 0],
+ stack = [current],
+ localOffset = 0;
+
while ( stack.length > 0 ) {
if ( current[1] >= current[0].length ) {
stack.pop();
@@ -196,18 +217,18 @@
var $item = current[0].eq( current[1] );
if ( item.nodeType === 3 ) {
- if ( item === localNode ) {
- offset += localOffset;
+ if ( item === elem ) {
+ localOffset += offset;
break;
} else {
- offset += item.textContent.length;
+ localOffset += item.textContent.length;
}
} else if ( item.nodeType === 1 ) {
- if ( $( item ).attr('contentEditable') === "false" ) {
+ if ( $( item ).attr( 'contentEditable' ) === "false" ) {
offset += 1;
} else {
- if ( item === localNode ) {
- offset += localOffset;
+ if ( item === elem ) {
+ localOffset += offset;
break;
}
@@ -219,8 +240,11 @@
}
current[1]++;
}
-
- return this.documentView.getOffsetFromNode( $node.data('view') ) + 1 +
offset;
+ if ( global === true ) {
+ return this.documentView.getOffsetFromNode( $leafNode.data(
'view' ) ) + 1 + localOffset;
+ } else {
+ return localOffset;
+ }
};
ve.es.Surface.prototype.showCursorAt = function( offset ) {
@@ -271,232 +295,61 @@
sel.addRange(range);
};
-/*
-ve.es.Surface.prototype.onCutCopy = function( e ) {
- var _this = this,
- key = rangy.getSelection().getRangeAt(0).toString().replace(/(
|\r\n|\n|\r|\t)/gm,"");
-
- _this.clipboard[key] = ve.copyArray( _this.documentView.model.getData(
_this.getSelection() ) );
-
- if ( event.type == 'cut' ) {
- setTimeout( function() {
- document.execCommand('undo', false, false);
-
- var selection = _this.getSelection();
- var tx = _this.model.getDocument().prepareRemoval(
selection );
- _this.model.transact( tx );
- _this.showCursorAt( selection.start );
- }, 1 );
+ve.es.Surface.prototype.getLeafNode = function( elem ) {
+ var $node = $( elem );
+ while( !$node.hasClass( 'ce-leafNode' ) ) {
+ $node = $node.parent();
}
+ return $node;
};
-
-ve.es.Surface.prototype.onPaste = function( e ) {
- var _this = this,
- insertionPoint = _this.getSelection().start;
-
- $('#paste').html('').show().css('top',
$(window).scrollTop()).css('left', $(window).scrollLeft()).focus();
-
- setTimeout( function() {
- var key = $('#paste').hide().text().replace(/(
|\r\n|\n|\r|\t)/gm,"");
-
- if ( _this.clipboard[key] ) {
- var tx = _this.documentView.model.prepareInsertion(
insertionPoint, _this.clipboard[key]);
- _this.model.transact( tx );
- _this.showCursorAt(insertionPoint +
_this.clipboard[key].length);
- } else {
- alert('i can only handle copy/paste from hybrid
surface. sorry. :(');
- }
- }, 1 );
+ve.es.Surface.getDOMText2 = function( elem ) {
+ // TODO: there must be some better way to write this regex replace
+ var regex = new RegExp("[" + String.fromCharCode(32) +
String.fromCharCode(160) + "]", "g");
+ return ve.es.Surface.getDOMText( elem ).replace( regex, " " );
};
-
-ve.es.Surface.prototype.onMouseDown = function( e ) {
- if ( this.worker !== null ) {
- clearInterval( this.worker );
- }
- var _this = this;
-
- setTimeout( function() {
- _this.node = rangy.getSelection().anchorNode;
- var prevText = _this.node.textContent;
- _this.worker = setInterval( function() {
+ve.es.Surface.getDOMText = function( elem ) {
+ var nodeType = elem.nodeType,
+ ret = '';
- if ( ( _this.node.previousSibling !== null &&
_this.node.previousSibling.nodeType === 3 ) || ( _this.node.nextSibling !==
null && _this.node.nextSibling.nodeType === 3 ) ) {
- console.log("!");
- var start = _this.getSelection().start;
- _this.node.parentNode.normalize();
- _this.showCursorAt( start );
- _this.node = rangy.getSelection().anchorNode;
- }
-
-
- var text = _this.node.textContent;
-
- if ( text === prevText ) {
- return;
- }
-
- var nodeOffset = _this.getOffset( _this.node, 0 );
-
- var sameFromLeft = 0,
- sameFromRight = 0,
- l = prevText.length;
-
- while ( sameFromLeft < l && prevText[sameFromLeft] ==
text[sameFromLeft] ) {
- ++sameFromLeft;
- }
- if ( prevText.length > sameFromLeft ) {
- l = l - sameFromLeft;
- while ( sameFromRight < l && prevText[prevText.length - 1 -
sameFromRight] == text[text.length - 1 - sameFromRight] ) {
- ++sameFromRight;
- }
- }
-
- if ( sameFromLeft + sameFromRight !== prevText.length ) {
- // delete
- var range = new ve.Range( nodeOffset + sameFromLeft, nodeOffset
+ prevText.length - sameFromRight );
- var tx = _this.model.getDocument().prepareRemoval( range );
- _this.model.transact( tx );
+ if ( nodeType === 1 || nodeType === 9 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( /\r\n/g, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += ve.es.Surface.getDOMText( elem );
}
-
- if ( sameFromLeft + sameFromRight !== text.length ) {
- // insert
- var data = text.split('').slice(sameFromLeft, text.length -
sameFromRight);
- var annotations =
_this.model.getDocument().getAnnotationsFromOffset( nodeOffset + sameFromLeft -
1 );
- ve.dm.DocumentNode.addAnnotationsToData( data, annotations );
- var tx = _this.documentView.model.prepareInsertion( nodeOffset
+ sameFromLeft, data);
- _this.model.transact( tx );
- }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
- prevText = text;
- }, 50 );
- }, 1 );
+ return ret;
};
-ve.es.Surface.prototype.emitUpdate = function( delay ) {
- if ( delay ) {
- if ( this.emitUpdateTimeout !== undefined ) {
- return;
- }
- var _this = this;
- this.emitUpdateTimeout = setTimeout( function() {
- _this.emit( 'update' );
- _this.emitUpdateTimeout = undefined;
- }, delay );
- } else {
- this.emit( 'update' );
- }
-};
+ve.es.Surface.getDOMHash = function( elem ) {
+ var nodeType = elem.nodeType,
+ nodeName = elem.nodeName,
+ ret = '';
-ve.es.Surface.prototype.showCursorAt = function( offset ) {
- var $node = this.documentView.getNodeFromOffset( offset ).$;
- var current = [$node.contents(), 0];
- var stack = [current];
- var node;
- var localOffset;
-
- var index = 1 + this.documentView.getOffsetFromNode( $node.data('view')
);
-
- while ( stack.length > 0 ) {
- if ( current[1] >= current[0].length ) {
- stack.pop();
- current = stack[ stack.length - 1 ];
- continue;
- }
- var item = current[0][current[1]];
- var $item = current[0].eq( current[1] );
-
- if ( item.nodeType === 3 ) {
- var length = item.textContent.length;
- if ( offset >= index && offset <= index + length ) {
- node = item;
- localOffset = offset - index;
- break;
- } else {
- index += length;
- }
- } else if ( item.nodeType === 1 ) {
- if ( $( item ).attr('contentEditable') === "false" ) {
- index += 1;
- } else {
- stack.push( [$item.contents(), 0] );
- current[1]++;
- current = stack[stack.length-1];
- continue;
- }
- }
- current[1]++;
+ if ( nodeType === 3 || nodeType === 4 ) {
+ return '#';
+ } else if ( nodeType === 1 || nodeType === 9 ) {
+ ret += '<' + nodeName + '>';
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += ve.es.Surface.getDOMHash( elem );
+ }
+ ret += '</' + nodeName + '>';
}
- var range = document.createRange();
- range.collapsed = true;
- range.setStart(node, localOffset);
-
- var sel = window.getSelection();
- sel.removeAllRanges();
- sel.addRange(range);
+ return ret;
};
-ve.es.Surface.prototype.showFakeCursorAt = function( offset ) {
- var _this = this;
-
- var $node = _this.documentView.getNodeFromOffset( offset ).$;
- var current = [$node.contents(), 0];
- var stack = [current];
- var node;
- var localOffset;
-
- var index = 1 + _this.documentView.getOffsetFromNode(
$node.data('view') );
-
- while ( stack.length > 0 ) {
- if ( current[1] >= current[0].length ) {
- stack.pop();
- current = stack[ stack.length - 1 ];
- continue;
- }
- var item = current[0][current[1]];
- var $item = current[0].eq( current[1] );
-
- if ( item.nodeType === 3 ) {
- var length = item.textContent.length;
- if ( offset >= index && offset <= index + length ) {
- node = item;
- localOffset = offset - index;
- break;
- } else {
- index += length;
- }
- } else if ( item.nodeType === 1 ) {
- if ( $( item ).attr('contentEditable') === "false" ) {
- index += 1;
- } else {
- stack.push( [$item.contents(), 0] );
- current[1]++;
- current = stack[stack.length-1];
- continue;
- }
- }
- current[1]++;
- }
-
- // Delay by 3 seconds - just for demo
- setTimeout(function() {
- var sel = rangy.getSelection();
- var range1 = sel.getRangeAt(0);
- var range2 = rangy.createRange();
-
- range2.setStart(node, localOffset);
-
- sel.setSingleRange(range2);
-
- var position = rangy.getSelection().getStartDocumentPos();
- $('#fake-cursor').css('top', position.y).css('left',
position.x);
-
- sel.setSingleRange(range1);
- }, 3000);
-}
-*/
-
/* Inheritance */
ve.extendClass( ve.es.Surface, ve.EventEmitter );
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs