diff --git a/demos/ce/index.php b/demos/ce/index.php index 13de83ee36..75dddb787d 100644 --- a/demos/ce/index.php +++ b/demos/ce/index.php @@ -125,8 +125,7 @@ include( '../../modules/sandbox/base.php' ); - - + diff --git a/modules/ve/ce/ve.ce.Surface.js b/modules/ve/ce/ve.ce.Surface.js index 4b6a300cba..cf7cc55de8 100644 --- a/modules/ve/ce/ve.ce.Surface.js +++ b/modules/ve/ce/ve.ce.Surface.js @@ -28,18 +28,30 @@ ve.ce.Surface = function( $container, model ) { this.clipboard = {}; this.autoRender = false; + // Content Observer + this.surfaceObserver = new ve.ce.SurfaceObserver( this.documentView ); + this.surfaceObserver.on( 'cursor', function( info ) { + //console.log("cursor", info); + } ) + // Events this.documentView.$.bind( { 'focus': function( e ) { + _this.surfaceObserver.updateCursor( true ); _this.documentOnFocus(); $document.unbind( '.ce-surfaceView' ); $document.bind( { 'keydown.ce-surfaceView': function( e ) { + _this.surfaceObserver.updateCursor( true ); return _this.onKeyDown( e ); + }, + 'mousemove.ce-surfaceView': function( e ) { + _this.surfaceObserver.updateCursor( true ); } } ); }, 'blur': function( e ) { + _this.surfaceObserver.updateCursor( true ); _this.documentOnBlur(); $document.unbind( '.ce-surfaceView' ); } @@ -53,7 +65,8 @@ ve.ce.Surface = function( $container, model ) { _this.onPaste( e ); } ) .on( 'mousedown', function( e ) { - return _this.onMouseDown( e ); + _this.surfaceObserver.updateCursor( true ); + return _this.onMouseDown( e ); } ) .on( 'compositionstart', function( e ) { console.log('comp start'); @@ -324,6 +337,7 @@ ve.ce.Surface.prototype.clearPollData = function() { }; ve.ce.Surface.prototype.pollContent = function() { + return; var localOffset, text, hash; if ( this.poll.compositionStart !== null && this.poll.compositionEnd !== null ) { @@ -634,6 +648,10 @@ ve.ce.Surface.prototype.showSelection = function( range ) { }; ve.ce.Surface.prototype.getLeafNode = function( elem ) { + return ve.ce.Surface.getLeafNode( elem ); +}; + +ve.ce.Surface.getLeafNode = function( elem ) { var $node = $( elem ); while( !$node.hasClass( 'ce-leafNode' ) ) { $node = $node.parent(); diff --git a/modules/ve/ce/ve.ce.SurfaceObserver.js b/modules/ve/ce/ve.ce.SurfaceObserver.js new file mode 100644 index 0000000000..c0418e2720 --- /dev/null +++ b/modules/ve/ce/ve.ce.SurfaceObserver.js @@ -0,0 +1,166 @@ +ve.ce.SurfaceObserver = function( documentView ) { + // Inheritance + ve.EventEmitter.call( this ); + + this.documentView = documentView; + + this.anchorNode = null; + this.anchorOffset = null; + this.focusNode = null; + this.focusOffset = null; + this.range = null; + + this.$node = null; + this.interval = null; + this.frequency = 100; + this.prevText = null; + this.prevHash = null; + this.prevRange = null; + + var _this = this; + + this.on( 'select', function( range ) { + if ( range !== null && range.getLength() === 0 ) { + var node = _this.documentView.getNodeFromOffset( range.start ); + _this.setNode( node.$ ); + } else { + _this.stop(); + } + } ); +}; + +ve.ce.SurfaceObserver.prototype.setNode = function( $node ) { + if ( this.$node !== $node ) { + this.stop(); + + this.$node = $node; + this.prevText = ve.ce.Surface.getDOMText2( this.$node[0] ); + this.prevHash = ve.ce.Surface.getDOMHash( this.$node[0] ); + + this.start(); + } +}; + +ve.ce.SurfaceObserver.prototype.stop = function() { + if ( this.interval !== null ) { + clearInterval( this.interval ); + this.interval = null; + this.poll(); + } +}; + +ve.ce.SurfaceObserver.prototype.start = function() { + this.poll(); + var _this = this; + setTimeout( function() {_this.poll(); }, 0); + this.interval = setInterval( function() { _this.poll(); }, this.frequency ); +}; + +ve.ce.SurfaceObserver.prototype.poll = function() { + var text = ve.ce.Surface.getDOMText2( this.$node[0] ); + var hash = ve.ce.Surface.getDOMHash( this.$node[0] ); + + if ( text !== this.prevText || hash !== this.prevHash ) { + console.log(1); + this.emit('change', { + $node: this.$node, + prevText: this.prevText, + text: text, + prevHash: this.prevHash, + hash: hash + } ); + this.prevText = text; + this.prevHash = hash; + } +}; + +ve.ce.SurfaceObserver.prototype.updateCursor = function( async ) { + if ( async ) { + var _this = this; + setTimeout( function() { + _this.updateCursor ( false ); + }, 0 ); + } else { + if ( !this.documentView.$.is(':focus') ) { + if ( + this.anchorNode !== null || + this.anchorOffset !== null || + this.focusNode !== null || + this.focusOffset !== null + ) { + this.anchorNode = this.anchorOffset = this.focusNode = this.focusOffset = null; + this.range = null; + this.emit( 'select', this.range ); + } + } else { + var rangySel = rangy.getSelection(); + if ( + rangySel.anchorNode !== this.anchorNode || + rangySel.anchorOffset !== this.anchorOffset || + rangySel.focusNode !== this.focusNode || + rangySel.focusOffset !== this.focusOffset + ) { + this.anchorNode = rangySel.anchorNode; + this.anchorOffset = rangySel.anchorOffset; + this.focusNode = rangySel.focusNode; + this.focusOffset = rangySel.focusOffset; + if ( rangySel.isCollapsed ) { + this.range = new ve.Range( this.getOffset( this.anchorNode, this.anchorOffset ) ); + } else { + this.range = new ve.Range( + this.getOffset( this.anchorNode, this.anchorOffset ), + this.getOffset( this.focusNode, this.focusOffset ) + ); + } + this.emit( 'select', this.range ); + } + } + } +}; + +ve.ce.SurfaceObserver.prototype.getOffset = function( selectionNode, selectionOffset ) { + var $leafNode = ve.ce.Surface.getLeafNode( selectionNode ), + current = [$leafNode.contents(), 0], + stack = [current], + offset = 0; + + 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 ) { + if ( item === selectionNode ) { + offset += selectionOffset; + break; + } else { + offset += item.textContent.length; + } + } else if ( item.nodeType === 1 ) { + if ( $( item ).attr( 'contentEditable' ) === 'false' ) { + offset += 1; + } else { + if ( item === selectionNode ) { + offset += selectionOffset; + break; + } + stack.push( [$item.contents(), 0] ); + current[1]++; + current = stack[stack.length-1]; + continue; + } + } + current[1]++; + } + return this.documentView.getOffsetFromNode( + $leafNode.data( 'view' ) + ) + 1 + offset; +}; + +/* Inheritance */ + +ve.extendClass( ve.ce.SurfaceObserver , ve.EventEmitter ); \ No newline at end of file