r62596 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r62595‎ | r62596 | r62597 >
Date:21:05, 16 February 2010
Author:adam
Status:ok
Tags:
Comment:
WikiEditor undo/redo improvements. More intellgient detection of undo/redo changes, fixing a bug which would remove too many items from the history when an edit would be made between states
Modified paths:
  • /trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins.combined.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php
@@ -72,7 +72,7 @@
7373 array( 'src' => 'js/plugins/jquery.namespaceSelect.js', 'version' => 1 ),
7474 array( 'src' => 'js/plugins/jquery.suggestions.js', 'version' => 7 ),
7575 array( 'src' => 'js/plugins/jquery.textSelection.js', 'version' => 27 ),
76 - array( 'src' => 'js/plugins/jquery.wikiEditor.js', 'version' => 136 ),
 76+ array( 'src' => 'js/plugins/jquery.wikiEditor.js', 'version' => 137 ),
7777 array( 'src' => 'js/plugins/jquery.wikiEditor.highlight.js', 'version' => 31 ),
7878 array( 'src' => 'js/plugins/jquery.wikiEditor.toolbar.js', 'version' => 49 ),
7979 array( 'src' => 'js/plugins/jquery.wikiEditor.dialogs.js', 'version' => 15 ),
@@ -82,10 +82,10 @@
8383 array( 'src' => 'js/plugins/jquery.wikiEditor.publish.js', 'version' => 2 ),
8484 ),
8585 'combined' => array(
86 - array( 'src' => 'js/plugins.combined.js', 'version' => 258 ),
 86+ array( 'src' => 'js/plugins.combined.js', 'version' => 259 ),
8787 ),
8888 'minified' => array(
89 - array( 'src' => 'js/plugins.combined.min.js', 'version' => 258 ),
 89+ array( 'src' => 'js/plugins.combined.min.js', 'version' => 259 ),
9090 ),
9191 ),
9292 );
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.js
@@ -231,7 +231,9 @@
232232 // Stack of states in { html: [string] } form
233233 'history': [],
234234 // Current history state position - this is number of steps backwards, so it's always -1 or less
235 - 'historyPosition': -1
 235+ 'historyPosition': -1,
 236+ /// The previous historyPosition, stored to detect if change events were due to an undo or redo action
 237+ 'oldDelayedHistoryPosition': -1
236238 };
237239
238240 /*
@@ -310,7 +312,8 @@
311313 // Only act if we are switching to a valid state
312314 if ( newPosition >= ( context.history.length * -1 ) && newPosition < 0 ) {
313315 // Make sure we run the history storing code before we make this change
314 - context.fn.updateHistory();
 316+ context.evt.delayedChange( event );
 317+ context.oldDelayedHistoryPosition = context.historyPosition;
315318 context.historyPosition = newPosition;
316319 // Change state
317320 // FIXME: Destroys event handlers, will be a problem with template folding
@@ -1387,8 +1390,7 @@
13881391 var newHTML = context.$content.html();
13891392 var newSel = context.fn.getCaretPosition();
13901393 // Was text changed? Was it because of a REDO or UNDO action?
1391 - if ( context.history.length == 0 || ( htmlChange &&
1392 - newHTML != context.history[context.history.length + context.historyPosition].html ) ) {
 1394+ if ( context.history.length == 0 || ( htmlChange && context.oldDelayedHistoryPosition == context.historyPosition ) ) {
13931395 context.fn.purgeOffsets();
13941396 context.oldDelayedHTML = newHTML;
13951397 context.oldDelayedSel = newSel;
@@ -1396,7 +1398,7 @@
13971399 // FIXME: this should really be happing on change, not on the delay
13981400 if ( context.historyPosition < -1 ) {
13991401 //clear out the extras
1400 - context.history.splice( context.history.length + context.historyPosition );
 1402+ context.history.splice( context.history.length + context.historyPosition + 1 );
14011403 context.historyPosition = -1;
14021404 }
14031405 context.history.push( { 'html': newHTML, 'sel': newSel } );
@@ -1409,6 +1411,8 @@
14101412 context.oldDelayedSel = newSel;
14111413 context.history[context.history.length + context.historyPosition].sel = newSel;
14121414 }
 1415+ // synch our old delayed history position until the next undo/redo action
 1416+ context.oldDelayedHistoryPosition = context.historyPosition;
14131417 }
14141418 };
14151419
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.js
@@ -6664,7 +6664,9 @@
66656665 // Stack of states in { html: [string] } form
66666666 'history': [],
66676667 // Current history state position - this is number of steps backwards, so it's always -1 or less
6668 - 'historyPosition': -1
 6668+ 'historyPosition': -1,
 6669+ /// The previous historyPosition, stored to detect if change events were due to an undo or redo action
 6670+ 'oldDelayedHistoryPosition': -1
66696671 };
66706672
66716673 /*
@@ -6743,7 +6745,8 @@
67446746 // Only act if we are switching to a valid state
67456747 if ( newPosition >= ( context.history.length * -1 ) && newPosition < 0 ) {
67466748 // Make sure we run the history storing code before we make this change
6747 - context.fn.updateHistory();
 6749+ context.evt.delayedChange( event );
 6750+ context.oldDelayedHistoryPosition = context.historyPosition;
67486751 context.historyPosition = newPosition;
67496752 // Change state
67506753 // FIXME: Destroys event handlers, will be a problem with template folding
@@ -6823,9 +6826,10 @@
68246827 }
68256828 setTimeout( function() {
68266829 // Unwrap the span found in webkit copies
 6830+ context.$content.find( 'link, style, meta' ).remove(); //MS Word
68276831 context.$content.find( 'span.Apple-style-span' ).each( function() {
68286832 $( this.childNodes ).insertBefore( this );
6829 - } ).remove();
 6833+ } ).remove(); //Apple Richtext
68306834 var $selection = context.$content.find( ':not(.wikiEditor)' );
68316835 while ( $selection.length && $selection.length > 0 ) {
68326836 var $currentElement = $selection.eq( 0 );
@@ -7819,8 +7823,7 @@
78207824 var newHTML = context.$content.html();
78217825 var newSel = context.fn.getCaretPosition();
78227826 // Was text changed? Was it because of a REDO or UNDO action?
7823 - if ( context.history.length == 0 || ( htmlChange &&
7824 - newHTML != context.history[context.history.length + context.historyPosition].html ) ) {
 7827+ if ( context.history.length == 0 || ( htmlChange && context.oldDelayedHistoryPosition == context.historyPosition ) ) {
78257828 context.fn.purgeOffsets();
78267829 context.oldDelayedHTML = newHTML;
78277830 context.oldDelayedSel = newSel;
@@ -7828,7 +7831,7 @@
78297832 // FIXME: this should really be happing on change, not on the delay
78307833 if ( context.historyPosition < -1 ) {
78317834 //clear out the extras
7832 - context.history.splice( context.history.length + context.historyPosition );
 7835+ context.history.splice( context.history.length + context.historyPosition + 1 );
78337836 context.historyPosition = -1;
78347837 }
78357838 context.history.push( { 'html': newHTML, 'sel': newSel } );
@@ -7841,6 +7844,8 @@
78427845 context.oldDelayedSel = newSel;
78437846 context.history[context.history.length + context.historyPosition].sel = newSel;
78447847 }
 7848+ // synch our old delayed history position until the next undo/redo action
 7849+ context.oldDelayedHistoryPosition = context.historyPosition;
78457850 }
78467851 };
78477852
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js
@@ -443,10 +443,10 @@
444444 return cacheSupport(true);},'autoMsg':function(object,property){if(typeof property=='object'){for(var i in property){if(property[i]in object||property[i]+'Msg'in object){property=property[i];break;}}}
445445 if(property in object){return object[property];}else if(property+'Msg'in object){if(typeof object[property+'Msg']=='object'){return mw.usability.getMsg.apply(mw.usability,object[property+'Msg']);}else{return mw.usability.getMsg(object[property+'Msg']);}}else{return'';}},'autoLang':function(object,lang){return object[lang||wgUserLanguage]||object['default']||object;},'autoIcon':function(icon,path,lang){var src=$.wikiEditor.autoLang(icon,lang);path=path||$.wikiEditor.imgPath;if(src.substr(0,7)!='http://'&&src.substr(0,8)!='https://'&&src[0]!='/'){src=path+src;}
446446 return src+'?'+wgWikiEditorIconVersion;}};$.fn.wikiEditor=function(){if(!$j.wikiEditor.isSupported()){return $(this);}
447 -var context=$(this).data('wikiEditor-context');if(typeof context=='undefined'){context={'$textarea':$(this),'views':{},'modules':{},'data':{},'instance':$.wikiEditor.instances.push($(this))-1,'offsets':null,'htmlToTextMap':{},'oldHTML':null,'oldDelayedHTML':null,'oldDelayedSel':null,'savedSelection':null,'history':[],'historyPosition':-1};context.api={'addModule':function(context,data){var modules={};if(typeof data=='string'){modules[data]={};}else if(typeof data=='object'){modules=data;}
 447+var context=$(this).data('wikiEditor-context');if(typeof context=='undefined'){context={'$textarea':$(this),'views':{},'modules':{},'data':{},'instance':$.wikiEditor.instances.push($(this))-1,'offsets':null,'htmlToTextMap':{},'oldHTML':null,'oldDelayedHTML':null,'oldDelayedSel':null,'savedSelection':null,'history':[],'historyPosition':-1,'oldDelayedHistoryPosition':-1};context.api={'addModule':function(context,data){var modules={};if(typeof data=='string'){modules[data]={};}else if(typeof data=='object'){modules=data;}
448448 for(var module in modules){if(typeof module=='string'&&$.wikiEditor.isSupported(module)){if('api'in $.wikiEditor.modules[module]){for(var call in $.wikiEditor.modules[module].api){if(!(call in context.api)){context.api[call]=$.wikiEditor.modules[module].api[call];}}}
449449 if('fn'in $.wikiEditor.modules[module]&&'create'in $.wikiEditor.modules[module].fn){context.modules[module]={};$.wikiEditor.modules[module].fn.create(context,modules[module]);}}}}};context.evt={'keydown':function(event){switch(event.which){case 90:if((event.ctrlKey||event.metaKey)&&context.history.length){var newPosition;if(event.shiftKey){newPosition=context.historyPosition+1;}else{newPosition=context.historyPosition-1;}
450 -if(newPosition>=(context.history.length*-1)&&newPosition<0){context.fn.updateHistory();context.historyPosition=newPosition;context.$content.html(context.history[context.history.length+context.historyPosition].html);context.fn.purgeOffsets();if(context.history[context.history.length+context.historyPosition].sel){context.fn.setSelection({start:context.history[context.history.length+context.historyPosition].sel[0],end:context.history[context.history.length+context.historyPosition].sel[1]});}}
 450+if(newPosition>=(context.history.length*-1)&&newPosition<0){context.evt.delayedChange(event);context.oldDelayedHistoryPosition=context.historyPosition;context.historyPosition=newPosition;context.$content.html(context.history[context.history.length+context.historyPosition].html);context.fn.purgeOffsets();if(context.history[context.history.length+context.historyPosition].sel){context.fn.setSelection({start:context.history[context.history.length+context.historyPosition].sel[0],end:context.history[context.history.length+context.historyPosition].sel[1]});}}
451451 return false;}
452452 break;case 9:if(event.ctrlKey||event.altKey||event.shiftKey){return true;}else{var $tabindexList=$j('[tabindex]:visible').sort(function(a,b){return a.tabIndex-b.tabIndex;});for(var i=0;i<$tabindexList.length;i++){if($tabindexList.eq(i).attr('id')==context.$iframe.attr('id')){$tabindexList.get(i+1).focus();break;}}
453453 return false;}
@@ -456,7 +456,7 @@
457457 switch(event.which){case 8:break;}
458458 return true;},'delayedChange':function(event){event.data.scope='division';var newHTML=context.$content.html();if(context.oldDelayedHTML!=newHTML){context.oldDelayedHTML=newHTML;event.data.scope='realchange';}
459459 context.fn.updateHistory(event.data.scope=='realchange');return true;},'paste':function(event){context.$content.find(':not(.wikiEditor)').addClass('wikiEditor');if($.layout.name!=='webkit'){context.$content.addClass('pasting');}
460 -setTimeout(function(){context.$content.find('span.Apple-style-span').each(function(){$(this.childNodes).insertBefore(this);}).remove();var $selection=context.$content.find(':not(.wikiEditor)');while($selection.length&&$selection.length>0){var $currentElement=$selection.eq(0);while(!$currentElement.parent().is('body')&&!$currentElement.parent().is('.wikiEditor')){$currentElement=$currentElement.parent();}
 460+setTimeout(function(){context.$content.find('link, style, meta').remove();context.$content.find('span.Apple-style-span').each(function(){$(this.childNodes).insertBefore(this);}).remove();var $selection=context.$content.find(':not(.wikiEditor)');while($selection.length&&$selection.length>0){var $currentElement=$selection.eq(0);while(!$currentElement.parent().is('body')&&!$currentElement.parent().is('.wikiEditor')){$currentElement=$currentElement.parent();}
461461 var text=$currentElement.text();if($currentElement.is('br')){$currentElement.addClass('wikiEditor');}else if($currentElement.is('span')&&text.length==0){$currentElement.remove();}else{$newElement=$('<p></p>').addClass('wikiEditor').insertAfter($currentElement);if(text.length){$newElement.text(text);}else{$newElement.append($('<br>').addClass('wikiEditor'));}
462462 $currentElement.remove();}
463463 $selection=context.$content.find(':not(.wikiEditor)');}
@@ -553,8 +553,9 @@
554554 pos=nextPos+(leavingP?1:0);if(t.node.nodeName=='#text'){lastTextNode=t.node;lastTextNodeDepth=t.depth;}
555555 t=nextT;}},'saveSelection':function(){if(!$.browser.msie){return;}
556556 context.$iframe[0].contentWindow.focus();context.savedSelection=context.$iframe[0].contentWindow.document.selection.createRange();},'restoreSelection':function(){if(!$.browser.msie||context.savedSelection===null){return;}
557 -context.$iframe[0].contentWindow.focus();context.savedSelection.select();context.savedSelection=null;},'updateHistory':function(htmlChange){var newHTML=context.$content.html();var newSel=context.fn.getCaretPosition();if(context.history.length==0||(htmlChange&&newHTML!=context.history[context.history.length+context.historyPosition].html)){context.fn.purgeOffsets();context.oldDelayedHTML=newHTML;context.oldDelayedSel=newSel;if(context.historyPosition<-1){context.history.splice(context.history.length+context.historyPosition);context.historyPosition=-1;}
558 -context.history.push({'html':newHTML,'sel':newSel});while(context.history.length>10){context.history.shift();}}else if(context.oldDelayedSel!=newSel){context.oldDelayedSel=newSel;context.history[context.history.length+context.historyPosition].sel=newSel;}}};context.$textarea.wrapAll($('<div></div>').addClass('wikiEditor-ui')).wrapAll($('<div></div>').addClass('wikiEditor-ui-view wikiEditor-ui-view-wikitext')).wrapAll($('<div></div>').addClass('wikiEditor-ui-left')).wrapAll($('<div></div>').addClass('wikiEditor-ui-bottom')).wrapAll($('<div></div>').addClass('wikiEditor-ui-text'));context.$ui=context.$textarea.parent().parent().parent().parent().parent();context.$wikitext=context.$textarea.parent().parent().parent().parent();context.$wikitext.before($('<div></div>').addClass('wikiEditor-ui-controls').append($('<div></div>').addClass('wikiEditor-ui-tabs').hide()).append($('<div></div>').addClass('wikiEditor-ui-buttons'))).before($('<div style="clear:both;"></div>'));context.$controls=context.$ui.find('.wikiEditor-ui-buttons').hide();context.$buttons=context.$ui.find('.wikiEditor-ui-buttons');context.$tabs=context.$ui.find('.wikiEditor-ui-tabs');context.$ui.after($('<div style="clear:both;"></div>'));context.$wikitext.append($('<div></div>').addClass('wikiEditor-ui-right'));context.$wikitext.find('.wikiEditor-ui-left').prepend($('<div></div>').addClass('wikiEditor-ui-top'));context.view='wikitext';$(window).resize(function(event){context.fn.trigger('resize',event);});context.fn.setupIframe=function(){context.$iframe=$('<iframe></iframe>').attr({'frameBorder':0,'border':0,'tabindex':1,'src':wgScriptPath+'/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.html?'+'instance='+context.instance+'&ts='+(new Date()).getTime()+'&is=content','id':'wikiEditor-iframe-'+context.instance}).css({'backgroundColor':'white','width':'100%','height':context.$textarea.height(),'display':'none','overflow-y':'scroll','overflow-x':'hidden'}).insertAfter(context.$textarea).load(function(){if(!this.isSecondRun){context.$iframe[0].contentWindow.document.designMode='on';if($.browser.msie){this.isSecondRun=true;return;}}
 557+context.$iframe[0].contentWindow.focus();context.savedSelection.select();context.savedSelection=null;},'updateHistory':function(htmlChange){var newHTML=context.$content.html();var newSel=context.fn.getCaretPosition();if(context.history.length==0||(htmlChange&&context.oldDelayedHistoryPosition==context.historyPosition)){context.fn.purgeOffsets();context.oldDelayedHTML=newHTML;context.oldDelayedSel=newSel;if(context.historyPosition<-1){context.history.splice(context.history.length+context.historyPosition+1);context.historyPosition=-1;}
 558+context.history.push({'html':newHTML,'sel':newSel});while(context.history.length>10){context.history.shift();}}else if(context.oldDelayedSel!=newSel){context.oldDelayedSel=newSel;context.history[context.history.length+context.historyPosition].sel=newSel;}
 559+context.oldDelayedHistoryPosition=context.historyPosition;}};context.$textarea.wrapAll($('<div></div>').addClass('wikiEditor-ui')).wrapAll($('<div></div>').addClass('wikiEditor-ui-view wikiEditor-ui-view-wikitext')).wrapAll($('<div></div>').addClass('wikiEditor-ui-left')).wrapAll($('<div></div>').addClass('wikiEditor-ui-bottom')).wrapAll($('<div></div>').addClass('wikiEditor-ui-text'));context.$ui=context.$textarea.parent().parent().parent().parent().parent();context.$wikitext=context.$textarea.parent().parent().parent().parent();context.$wikitext.before($('<div></div>').addClass('wikiEditor-ui-controls').append($('<div></div>').addClass('wikiEditor-ui-tabs').hide()).append($('<div></div>').addClass('wikiEditor-ui-buttons'))).before($('<div style="clear:both;"></div>'));context.$controls=context.$ui.find('.wikiEditor-ui-buttons').hide();context.$buttons=context.$ui.find('.wikiEditor-ui-buttons');context.$tabs=context.$ui.find('.wikiEditor-ui-tabs');context.$ui.after($('<div style="clear:both;"></div>'));context.$wikitext.append($('<div></div>').addClass('wikiEditor-ui-right'));context.$wikitext.find('.wikiEditor-ui-left').prepend($('<div></div>').addClass('wikiEditor-ui-top'));context.view='wikitext';$(window).resize(function(event){context.fn.trigger('resize',event);});context.fn.setupIframe=function(){context.$iframe=$('<iframe></iframe>').attr({'frameBorder':0,'border':0,'tabindex':1,'src':wgScriptPath+'/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.html?'+'instance='+context.instance+'&ts='+(new Date()).getTime()+'&is=content','id':'wikiEditor-iframe-'+context.instance}).css({'backgroundColor':'white','width':'100%','height':context.$textarea.height(),'display':'none','overflow-y':'scroll','overflow-x':'hidden'}).insertAfter(context.$textarea).load(function(){if(!this.isSecondRun){context.$iframe[0].contentWindow.document.designMode='on';if($.browser.msie){this.isSecondRun=true;return;}}
559560 context.$content=$(context.$iframe[0].contentWindow.document.body);var html=context.$textarea.val().replace(/&esc;/g,'&esc;esc;').replace(/\<p\>/g,'&esc;&lt;p&gt;').replace(/\<\/p\>/g,'&esc;&lt;/p&gt;').replace(/\<span class="wikiEditor-tab"\>\<\/span\>/g,'&esc;&lt;span&nbsp;class=&quot;wikiEditor-tab&quot;&gt;&lt;/span&gt;').replace(/&nbsp;/g,'&esc;&amp;nbsp;');if($.browser.msie){html=html.replace(/\t/g,'<span class="wikiEditor-tab"></span>');if($.browser.versionNumber<=7){html=html.replace(/ /g,"&nbsp;");}else{html=html.replace(/(^|\n) /g,"$1&nbsp;");}}
560561 html=$('<div />').text('<p>'+html.replace(/\r?\n/g,'</p><p>')+'</p>').html().replace(/&amp;nbsp;/g,'&nbsp;').replace(/&lt;p&gt;/g,'<p>').replace(/&lt;\/p&gt;/g,'</p>').replace(/&lt;span( |&nbsp;)class=("|&quot;)wikiEditor-tab("|&quot;)&gt;&lt;\/span&gt;/g,'<span class="wikiEditor-tab"></span>').replace(/<p><\/p>/g,'<p><br></p>').replace(/&amp;esc;&amp;amp;nbsp;/g,'&amp;nbsp;').replace(/&amp;esc;&amp;lt;p&amp;gt;/g,'&lt;p&gt;').replace(/&amp;esc;&amp;lt;\/p&amp;gt;/g,'&lt;/p&gt;').replace(/&amp;esc;&amp;lt;span&amp;nbsp;class=&amp;quot;wikiEditor-tab&amp;quot;&amp;gt;&amp;lt;\/span&amp;gt;/g,'&lt;span class="wikiEditor-tab"&gt;&lt;\/span&gt;').replace(/&amp;esc;esc;/g,'&amp;esc;');context.$content.html(html);if($('body').is('.rtl')){context.$content.addClass('rtl').attr('dir','rtl');}
561562 context.$textarea.attr('disabled',true);context.$textarea.hide();context.$iframe.show();context.fn.trigger('ready');context.oldHTML=context.oldDelayedHTML=context.$content.html();$(context.$iframe[0].contentWindow.document).bind('keydown',function(event){return context.fn.trigger('keydown',event);}).bind('paste',function(event){return context.fn.trigger('paste',event);}).bind('keyup paste mouseup cut encapsulateSelection',function(event){return context.fn.trigger('change',event);}).delayedBind(250,'keyup paste mouseup cut encapsulateSelection',function(event){context.fn.trigger('delayedChange',event);});});context.$textarea.closest('form').submit(function(){context.$textarea.attr('disabled',false);context.$textarea.val(context.$textarea.textSelection('getContents'));});context.fallbackWindowOnBeforeUnload=window.onbeforeunload;window.onbeforeunload=function(){context.$textarea.val(context.$textarea.textSelection('getContents'));if(context.fallbackWindowOnBeforeUnload){return context.fallbackWindowOnBeforeUnload();}};};}

Status & tagging log