r112755 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r112754‎ | r112755 | r112756 >
Date:01:28, 1 March 2012
Author:inez
Status:deferred
Tags:
Comment:
Migreate text input method from playground to ce and ce demo
Modified paths:
  • /trunk/extensions/VisualEditor/modules/ve/ce/ve.es.Surface.js (modified) (history)

Diff [purge]

Index: trunk/extensions/VisualEditor/modules/ve/ce/ve.es.Surface.js
@@ -19,66 +19,74 @@
2020 this.model = model;
2121 this.documentView = new ve.es.DocumentNode( this.model.getDocument(), this );
2222 this.contextView = null;
23 - this.clipboard = {};
2423 this.$ = $container
2524 .addClass( 'es-surfaceView' )
2625 .append( this.documentView.$ );
2726 this.emitUpdateTimeout = undefined;
28 - this.node = null;
2927
3028 // Events
3129 this.documentView.$.bind( {
3230 'focus': function( e ) {
 31+ _this.documentOnFocus();
3332 $document.unbind( '.ce-surfaceView' );
3433 $document.bind( {
3534 'keydown.ce-surfaceView': function( e ) {
36 - return _this.onKeyDown( e );
 35+// return _this.onKeyDown( e );
3736 },
3837 } );
39 - _this.setNode();
4038 },
4139 'blur': function( e ) {
 40+ _this.documentOnBlur();
4241 $document.unbind( '.ce-surfaceView' );
4342 }
4443 } );
4544
4645 this.$.mousedown( function(e) {
47 - return _this.onMouseDown( e );
 46+// return _this.onMouseDown( e );
4847 } );
4948
 49+ // Initialization
 50+ this.documentView.renderContent();
 51+
5052 // Prevent dragging text
5153 this.$.bind('dragover drop', function(e) {
5254 e.preventDefault();
5355 });
5456
55 - /*
56 - this.model.getDocument().on( 'update', function() {
57 - _this.emitUpdate( 25 );
58 - } );
 57+ this.poll = {
 58+ interval: null,
 59+ frequency: 100,
 60+ node: null,
 61+ prevText: null,
 62+ prevHash: null,
 63+ prevOffset: null,
 64+ compositionStart: null,
 65+ compositionEnd: null
 66+ };
5967
60 - this.$.mousedown( function( e ) {
61 - return _this.onMouseDown( e );
 68+ document.addEventListener( 'compositionstart', function( e ) {
 69+ _this.onCompositionStart( e );
6270 } );
63 -
64 - this.$.mouseup( function( e ) {
65 - //var selection = _this.getSelection();
66 - //_this.showFakeCursorAt(selection.start);
 71+ document.addEventListener( 'compositionend', function( e ) {
 72+ _this.onCompositionEnd( e );
6773 } );
 74+};
6875
69 - this.$.on('paste', function( e ) {
70 - _this.onPaste( e );
71 - } );
 76+/* Methods */
7277
73 - this.$.on('cut copy', function( e ) {
74 - _this.onCutCopy( e );
75 - } );
76 - */
 78+ve.es.Surface.prototype.onCompositionStart = function( e ) {
 79+ this.stopPolling();
7780
78 - // Initialization
79 - this.documentView.renderContent();
 81+ var rangySel = rangy.getSelection();
 82+ this.poll.compositionStart = this.getOffset( rangySel.anchorNode, rangySel.anchorOffset, false );
8083 };
8184
82 -/* Methods */
 85+ve.es.Surface.prototype.onCompositionEnd = function( e ) {
 86+ var rangySel = rangy.getSelection();
 87+ this.poll.compositionEnd = this.getOffset( rangySel.focusNode, rangySel.focusOffset, false );
 88+
 89+ this.startPolling();
 90+};
8391
8492 ve.es.Surface.prototype.attachContextView = function( contextView ) {
8593 this.contextView = contextView;
@@ -88,104 +96,117 @@
8997 return this.model;
9098 };
9199
92 -ve.es.Surface.prototype.onKeyDown = function( e ) {
93 - switch ( e.keyCode ) {
94 - // Page up
95 - case 33:
96 - // Page down
97 - case 34:
98 - // Home
99 - case 36:
100 - // End
101 - case 35:
102 - // Up arrow
103 - case 38:
104 - // Down arrow
105 - case 40:
106 - this.setNode();
107 - break;
108 - // Left arrow
109 - case 37:
110 - this.setNode( 'left' );
111 - break;
112 - // Right arrow
113 - case 39:
114 - this.setNode( 'right' );
115 - break;
 100+ve.es.Surface.prototype.documentOnFocus = function() {
 101+ this.startPolling();
 102+};
 103+
 104+ve.es.Surface.prototype.documentOnBlur = function() {
 105+ this.stopPolling();
 106+};
 107+
 108+ve.es.Surface.prototype.startPolling = function() {
 109+ if ( this.poll.interval === null ) {
 110+ var _this = this;
 111+ this.poll.interval = setInterval( function() {
 112+ _this.pollContent();
 113+ }, this.poll.frequency );
 114+ this.pollContent();
116115 }
117116 };
118117
119 -ve.es.Surface.prototype.onMouseDown = function( e ) {
120 - this.setNode();
 118+ve.es.Surface.prototype.stopPolling = function() {
 119+ if ( this.poll.interval !== null ) {
 120+ clearInterval( this.poll.interval );
 121+ this.poll.interval = null;
 122+ }
121123 };
122124
123 -ve.es.Surface.prototype.setNode = function( direction ) {
124 - var _this = this;
125 -
126 - setTimeout( function() {
127 - var rangySelection = rangy.getSelection();
 125+ve.es.Surface.prototype.pollContent = function() {
 126+ if ( this.poll.compositionStart !== null && this.poll.compositionEnd !== null ) {
 127+ var text = ve.es.Surface.getDOMText2( this.poll.node ),
 128+ hash = ve.es.Surface.getDOMHash( this.poll.node ),
 129+ localOffset = this.poll.compositionEnd;
 130+ this.poll.compositionStart = null;
 131+ this.poll.compositionEnd = null;
 132+ } else {
 133+ var rangySel = rangy.getSelection();
128134
129 - if ( rangySelection.anchorNode === _this.node ) {
 135+ if ( rangySel.anchorNode === null ) {
130136 return;
131137 }
132138
133 - if ( rangySelection.anchorNode.nodeType !== 3 ) {
134 - if ( _this.node === null ) {
135 - throw "Value of this.node shouldn't be a null";
136 - }
137 - if ( direction !== 'left' && direction !== 'right' ) {
138 - throw "At this point value of direction should be 'left' or 'right'";
139 - }
140 - var oldOffset = _this.getOffset( _this.node, 0 ),
141 - newOffset;
 139+ var node = this.getLeafNode( rangySel.anchorNode )[0],
 140+ text = ve.es.Surface.getDOMText2( node ),
 141+ hash = ve.es.Surface.getDOMHash( node );
142142
143 - if ( direction === 'left' ) {
144 - newOffset = _this.documentView.model.getRelativeContentOffset( oldOffset, -1 );
145 - } else if ( direction === 'right' ) {
146 - newOffset = _this.documentView.model.getRelativeContentOffset( oldOffset + _this.node.length, 1 );
147 - }
148 - _this.showCursorAt( newOffset );
149 - _this.setNode();
 143+ if ( rangySel.anchorNode !== rangySel.focusNode || rangySel.anchorOffset !== rangySel.focusOffset ) {
 144+ var localOffset = null;
 145+ } else {
 146+ var localOffset = this.getOffset( rangySel.anchorNode, rangySel.anchorOffset, false );
 147+ }
 148+
 149+ if ( node !== this.poll.node ) {
 150+ this.poll.node = node;
 151+ this.poll.prevText = text;
 152+ this.poll.prevHash = hash;
 153+ this.poll.prevOffset = localOffset;
150154 return;
151155 }
 156+ }
152157
153 - _this.node = rangySelection.anchorNode;
154 -
155 - console.log(_this.node);
156 - }, 0 );
157 -};
 158+ if ( text !== this.poll.prevText ) {
 159+ var nodeOffset = this.documentView.getOffsetFromNode( $( this.poll.node ).data( 'view' ) ),
 160+ lengthDiff = text.length - this.poll.prevText.length,
 161+ offsetDiff = ( localOffset !== null && this.poll.prevOffset !== null ) ? localOffset - this.poll.prevOffset : null;
158162
159 -ve.es.Surface.prototype.getSelection = function() {
160 - var selection = rangy.getSelection();
161 -
162 - if ( selection.anchorNode === selection.focusNode && selection.anchorOffset === selection.focusOffset ) {
163 - // cursor
164 - var offset = this.getOffset( selection.anchorNode, selection.anchorOffset );
165 - return new ve.Range( offset, offset );
166 - } else {
167 - // selection
168 - var offset1 = this.getOffset( selection.anchorNode, selection.anchorOffset );
169 - var offset2 = this.getOffset( selection.focusNode, selection.focusOffset );
170 - return new ve.Range( offset1, offset2 );
 163+ if ( lengthDiff === offsetDiff && this.poll.prevText.substring( 0, this.poll.prevOffset ) === text.substring( 0, this.poll.prevOffset ) ) {
 164+ var newData = text.substring( this.poll.prevOffset, localOffset ).split( '' );
 165+ var annotations = this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 + this.poll.prevOffset - 1 );
 166+ ve.dm.DocumentNode.addAnnotationsToData( newData, annotations );
 167+ this.model.transact( this.documentView.model.prepareInsertion(
 168+ nodeOffset + 1 + this.poll.prevOffset,
 169+ newData
 170+ ) );
 171+ } else {
 172+ var sameFromLeft = 0,
 173+ sameFromRight = 0,
 174+ l = text.length > this.poll.prevText.length ? this.poll.prevText.length : text.length;
 175+ while ( sameFromLeft < l && this.poll.prevText[sameFromLeft] == text[sameFromLeft] ) {
 176+ ++sameFromLeft;
 177+ }
 178+ l = l - sameFromLeft;
 179+ while ( sameFromRight < l && this.poll.prevText[this.poll.prevText.length - 1 - sameFromRight] == text[text.length - 1 - sameFromRight] ) {
 180+ ++sameFromRight;
 181+ }
 182+ this.model.transact( this.documentView.model.prepareRemoval( new ve.Range(
 183+ nodeOffset + 1 + sameFromLeft,
 184+ nodeOffset + 1 + this.poll.prevText.length - sameFromRight
 185+ ) ) );
 186+ var newData = text.substring( sameFromLeft, text.length - sameFromRight ).split( '' );
 187+ var annotations = this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 + sameFromLeft - 1 );
 188+ ve.dm.DocumentNode.addAnnotationsToData( newData, annotations );
 189+ this.model.transact( this.documentView.model.prepareInsertion(
 190+ nodeOffset + 1 + sameFromLeft,
 191+ newData
 192+ ) );
 193+ }
 194+ this.poll.prevText = text;
171195 }
172 -};
173 -
174 -ve.es.Surface.prototype.getOffset = function( localNode, localOffset ) {
175 - var $node = $( localNode );
176 -
177 - if ( $node.hasClass( 'ce-leafNode' ) ) {
178 - return this.documentView.getOffsetFromNode( $node.data('view') ) + 1;
 196+ if ( hash !== this.poll.prevHash ) {
 197+ // TODO: redisplay cursor in correct position (with setTimeout)
 198+ this.getLeafNode( this.poll.node ).data( 'view' ).renderContent();
 199+ this.poll.prevHash = hash;
179200 }
180201
181 - while( !$node.hasClass( 'ce-leafNode' ) ) {
182 - $node = $node.parent();
183 - }
184 -
185 - var current = [$node.contents(), 0];
186 - var stack = [current];
187 -
188 - var offset = 0;
189 -
 202+ this.poll.prevOffset = localOffset;
 203+};
 204+
 205+ve.es.Surface.prototype.getOffset = function( elem, offset, global ) {
 206+ var $leafNode = this.getLeafNode( elem ),
 207+ current = [$leafNode.contents(), 0],
 208+ stack = [current],
 209+ localOffset = 0;
 210+
190211 while ( stack.length > 0 ) {
191212 if ( current[1] >= current[0].length ) {
192213 stack.pop();
@@ -196,18 +217,18 @@
197218 var $item = current[0].eq( current[1] );
198219
199220 if ( item.nodeType === 3 ) {
200 - if ( item === localNode ) {
201 - offset += localOffset;
 221+ if ( item === elem ) {
 222+ localOffset += offset;
202223 break;
203224 } else {
204 - offset += item.textContent.length;
 225+ localOffset += item.textContent.length;
205226 }
206227 } else if ( item.nodeType === 1 ) {
207 - if ( $( item ).attr('contentEditable') === "false" ) {
 228+ if ( $( item ).attr( 'contentEditable' ) === "false" ) {
208229 offset += 1;
209230 } else {
210 - if ( item === localNode ) {
211 - offset += localOffset;
 231+ if ( item === elem ) {
 232+ localOffset += offset;
212233 break;
213234 }
214235
@@ -219,8 +240,11 @@
220241 }
221242 current[1]++;
222243 }
223 -
224 - return this.documentView.getOffsetFromNode( $node.data('view') ) + 1 + offset;
 244+ if ( global === true ) {
 245+ return this.documentView.getOffsetFromNode( $leafNode.data( 'view' ) ) + 1 + localOffset;
 246+ } else {
 247+ return localOffset;
 248+ }
225249 };
226250
227251 ve.es.Surface.prototype.showCursorAt = function( offset ) {
@@ -271,232 +295,61 @@
272296 sel.addRange(range);
273297 };
274298
275 -/*
276 -ve.es.Surface.prototype.onCutCopy = function( e ) {
277 - var _this = this,
278 - key = rangy.getSelection().getRangeAt(0).toString().replace(/( |\r\n|\n|\r|\t)/gm,"");
279 -
280 - _this.clipboard[key] = ve.copyArray( _this.documentView.model.getData( _this.getSelection() ) );
281 -
282 - if ( event.type == 'cut' ) {
283 - setTimeout( function() {
284 - document.execCommand('undo', false, false);
285 -
286 - var selection = _this.getSelection();
287 - var tx = _this.model.getDocument().prepareRemoval( selection );
288 - _this.model.transact( tx );
289 - _this.showCursorAt( selection.start );
290 - }, 1 );
 299+ve.es.Surface.prototype.getLeafNode = function( elem ) {
 300+ var $node = $( elem );
 301+ while( !$node.hasClass( 'ce-leafNode' ) ) {
 302+ $node = $node.parent();
291303 }
 304+ return $node;
292305 };
293 -
294 -ve.es.Surface.prototype.onPaste = function( e ) {
295 - var _this = this,
296 - insertionPoint = _this.getSelection().start;
297 -
298 - $('#paste').html('').show().css('top', $(window).scrollTop()).css('left', $(window).scrollLeft()).focus();
299 -
300 - setTimeout( function() {
301 - var key = $('#paste').hide().text().replace(/( |\r\n|\n|\r|\t)/gm,"");
302 -
303 - if ( _this.clipboard[key] ) {
304 - var tx = _this.documentView.model.prepareInsertion( insertionPoint, _this.clipboard[key]);
305 - _this.model.transact( tx );
306 - _this.showCursorAt(insertionPoint + _this.clipboard[key].length);
307 - } else {
308 - alert('i can only handle copy/paste from hybrid surface. sorry. :(');
309 - }
310306
311 - }, 1 );
 307+ve.es.Surface.getDOMText2 = function( elem ) {
 308+ // TODO: there must be some better way to write this regex replace
 309+ var regex = new RegExp("[" + String.fromCharCode(32) + String.fromCharCode(160) + "]", "g");
 310+ return ve.es.Surface.getDOMText( elem ).replace( regex, " " );
312311 };
313 -
314 -ve.es.Surface.prototype.onMouseDown = function( e ) {
315 - if ( this.worker !== null ) {
316 - clearInterval( this.worker );
317 - }
318312
319 - var _this = this;
320 -
321 - setTimeout( function() {
322 - _this.node = rangy.getSelection().anchorNode;
323 - var prevText = _this.node.textContent;
324 - _this.worker = setInterval( function() {
 313+ve.es.Surface.getDOMText = function( elem ) {
 314+ var nodeType = elem.nodeType,
 315+ ret = '';
325316
326 - if ( ( _this.node.previousSibling !== null && _this.node.previousSibling.nodeType === 3 ) || ( _this.node.nextSibling !== null && _this.node.nextSibling.nodeType === 3 ) ) {
327 - console.log("!");
328 - var start = _this.getSelection().start;
329 - _this.node.parentNode.normalize();
330 - _this.showCursorAt( start );
331 - _this.node = rangy.getSelection().anchorNode;
332 - }
333 -
334 -
335 - var text = _this.node.textContent;
336 -
337 - if ( text === prevText ) {
338 - return;
339 - }
340 -
341 - var nodeOffset = _this.getOffset( _this.node, 0 );
342 -
343 - var sameFromLeft = 0,
344 - sameFromRight = 0,
345 - l = prevText.length;
346 -
347 - while ( sameFromLeft < l && prevText[sameFromLeft] == text[sameFromLeft] ) {
348 - ++sameFromLeft;
349 - }
350 - if ( prevText.length > sameFromLeft ) {
351 - l = l - sameFromLeft;
352 - while ( sameFromRight < l && prevText[prevText.length - 1 - sameFromRight] == text[text.length - 1 - sameFromRight] ) {
353 - ++sameFromRight;
354 - }
355 - }
356 -
357 - if ( sameFromLeft + sameFromRight !== prevText.length ) {
358 - // delete
359 - var range = new ve.Range( nodeOffset + sameFromLeft, nodeOffset + prevText.length - sameFromRight );
360 - var tx = _this.model.getDocument().prepareRemoval( range );
361 - _this.model.transact( tx );
 317+ if ( nodeType === 1 || nodeType === 9 ) {
 318+ // Use textContent || innerText for elements
 319+ if ( typeof elem.textContent === 'string' ) {
 320+ return elem.textContent;
 321+ } else if ( typeof elem.innerText === 'string' ) {
 322+ // Replace IE's carriage returns
 323+ return elem.innerText.replace( /\r\n/g, '' );
 324+ } else {
 325+ // Traverse it's children
 326+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
 327+ ret += ve.es.Surface.getDOMText( elem );
362328 }
363 -
364 - if ( sameFromLeft + sameFromRight !== text.length ) {
365 - // insert
366 - var data = text.split('').slice(sameFromLeft, text.length - sameFromRight);
367 - var annotations = _this.model.getDocument().getAnnotationsFromOffset( nodeOffset + sameFromLeft - 1 );
368 - ve.dm.DocumentNode.addAnnotationsToData( data, annotations );
369 - var tx = _this.documentView.model.prepareInsertion( nodeOffset + sameFromLeft, data);
370 - _this.model.transact( tx );
371 - }
 329+ }
 330+ } else if ( nodeType === 3 || nodeType === 4 ) {
 331+ return elem.nodeValue;
 332+ }
372333
373 - prevText = text;
374 - }, 50 );
375 - }, 1 );
 334+ return ret;
376335 };
377336
378 -ve.es.Surface.prototype.emitUpdate = function( delay ) {
379 - if ( delay ) {
380 - if ( this.emitUpdateTimeout !== undefined ) {
381 - return;
382 - }
383 - var _this = this;
384 - this.emitUpdateTimeout = setTimeout( function() {
385 - _this.emit( 'update' );
386 - _this.emitUpdateTimeout = undefined;
387 - }, delay );
388 - } else {
389 - this.emit( 'update' );
390 - }
391 -};
 337+ve.es.Surface.getDOMHash = function( elem ) {
 338+ var nodeType = elem.nodeType,
 339+ nodeName = elem.nodeName,
 340+ ret = '';
392341
393 -ve.es.Surface.prototype.showCursorAt = function( offset ) {
394 - var $node = this.documentView.getNodeFromOffset( offset ).$;
395 - var current = [$node.contents(), 0];
396 - var stack = [current];
397 - var node;
398 - var localOffset;
399 -
400 - var index = 1 + this.documentView.getOffsetFromNode( $node.data('view') );
401 -
402 - while ( stack.length > 0 ) {
403 - if ( current[1] >= current[0].length ) {
404 - stack.pop();
405 - current = stack[ stack.length - 1 ];
406 - continue;
407 - }
408 - var item = current[0][current[1]];
409 - var $item = current[0].eq( current[1] );
410 -
411 - if ( item.nodeType === 3 ) {
412 - var length = item.textContent.length;
413 - if ( offset >= index && offset <= index + length ) {
414 - node = item;
415 - localOffset = offset - index;
416 - break;
417 - } else {
418 - index += length;
419 - }
420 - } else if ( item.nodeType === 1 ) {
421 - if ( $( item ).attr('contentEditable') === "false" ) {
422 - index += 1;
423 - } else {
424 - stack.push( [$item.contents(), 0] );
425 - current[1]++;
426 - current = stack[stack.length-1];
427 - continue;
428 - }
429 - }
430 - current[1]++;
 342+ if ( nodeType === 3 || nodeType === 4 ) {
 343+ return '#';
 344+ } else if ( nodeType === 1 || nodeType === 9 ) {
 345+ ret += '<' + nodeName + '>';
 346+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
 347+ ret += ve.es.Surface.getDOMHash( elem );
 348+ }
 349+ ret += '</' + nodeName + '>';
431350 }
432 - var range = document.createRange();
433 - range.collapsed = true;
434 - range.setStart(node, localOffset);
435 -
436 - var sel = window.getSelection();
437 - sel.removeAllRanges();
438 - sel.addRange(range);
 351+ return ret;
439352 };
440353
441 -ve.es.Surface.prototype.showFakeCursorAt = function( offset ) {
442 - var _this = this;
443 -
444 - var $node = _this.documentView.getNodeFromOffset( offset ).$;
445 - var current = [$node.contents(), 0];
446 - var stack = [current];
447 - var node;
448 - var localOffset;
449 -
450 - var index = 1 + _this.documentView.getOffsetFromNode( $node.data('view') );
451 -
452 - while ( stack.length > 0 ) {
453 - if ( current[1] >= current[0].length ) {
454 - stack.pop();
455 - current = stack[ stack.length - 1 ];
456 - continue;
457 - }
458 - var item = current[0][current[1]];
459 - var $item = current[0].eq( current[1] );
460 -
461 - if ( item.nodeType === 3 ) {
462 - var length = item.textContent.length;
463 - if ( offset >= index && offset <= index + length ) {
464 - node = item;
465 - localOffset = offset - index;
466 - break;
467 - } else {
468 - index += length;
469 - }
470 - } else if ( item.nodeType === 1 ) {
471 - if ( $( item ).attr('contentEditable') === "false" ) {
472 - index += 1;
473 - } else {
474 - stack.push( [$item.contents(), 0] );
475 - current[1]++;
476 - current = stack[stack.length-1];
477 - continue;
478 - }
479 - }
480 - current[1]++;
481 - }
482 -
483 - // Delay by 3 seconds - just for demo
484 - setTimeout(function() {
485 - var sel = rangy.getSelection();
486 - var range1 = sel.getRangeAt(0);
487 - var range2 = rangy.createRange();
488 -
489 - range2.setStart(node, localOffset);
490 -
491 - sel.setSingleRange(range2);
492 -
493 - var position = rangy.getSelection().getStartDocumentPos();
494 - $('#fake-cursor').css('top', position.y).css('left', position.x);
495 -
496 - sel.setSingleRange(range1);
497 - }, 3000);
498 -}
499 -*/
500 -
501354 /* Inheritance */
502355
503356 ve.extendClass( ve.es.Surface, ve.EventEmitter );

Status & tagging log