r56809 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r56808‎ | r56809 | r56810 >
Date:12:19, 23 September 2009
Author:catrope
Status:ok
Tags:
Comment:
NavigableTOC: Instead of rebuilding the section tree after every keyup, rebuild it only after the user has stopped typing for 1000 ms. Also fix the delayedBind plugin so it works properly with multiple calls and multiple events in one call.
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.delayedBind.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.toc.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php
@@ -60,23 +60,23 @@
6161 array( 'src' => 'js/plugins/jquery.autoEllipse.js', 'version' => 1 ),
6262 array( 'src' => 'js/plugins/jquery.browser.js', 'version' => 3 ),
6363 array( 'src' => 'js/plugins/jquery.cookie.js', 'version' => 3 ),
64 - array( 'src' => 'js/plugins/jquery.delayedBind.js', 'version' => 0 ),
 64+ array( 'src' => 'js/plugins/jquery.delayedBind.js', 'version' => 1 ),
6565 array( 'src' => 'js/plugins/jquery.namespaceSelect.js', 'version' => 1 ),
6666 array( 'src' => 'js/plugins/jquery.suggestions.js', 'version' => 4 ),
6767 array( 'src' => 'js/plugins/jquery.textSelection.js', 'version' => 10 ),
6868 array( 'src' => 'js/plugins/jquery.wikiEditor.js', 'version' => 5 ),
6969 array( 'src' => 'js/plugins/jquery.wikiEditor.toolbar.js', 'version' => 11 ),
7070 array( 'src' => 'js/plugins/jquery.wikiEditor.dialogs.js', 'version' => 3 ),
71 - array( 'src' => 'js/plugins/jquery.wikiEditor.toc.js', 'version' => 6 ),
 71+ array( 'src' => 'js/plugins/jquery.wikiEditor.toc.js', 'version' => 7 ),
7272 // FIXME: jQuery UI doesn't belong here, should move to no_js2
7373 // once we figure out how to do jQuery UI properly in JS2
7474 array( 'src' => 'js/js2/jquery-ui-1.7.2.js', 'version' => '1.7.2y' ),
7575 ),
7676 'combined' => array(
77 - array( 'src' => 'js/plugins.combined.js', 'version' => 24 ),
 77+ array( 'src' => 'js/plugins.combined.js', 'version' => 25 ),
7878 ),
7979 'minified' => array(
80 - array( 'src' => 'js/plugins.combined.min.js', 'version' => 24 ),
 80+ array( 'src' => 'js/plugins.combined.min.js', 'version' => 25 ),
8181 ),
8282 ),
8383 );
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.toc.js
@@ -43,7 +43,7 @@
4444 $.wikiEditor.modules.toc.fn.build( context );
4545 $.wikiEditor.modules.toc.fn.update( context );
4646 context.$textarea
47 - .bind( 'keyup encapsulateSelection',
 47+ .delayedBind( 1000, 'keyup encapsulateSelection',
4848 function( event ) {
4949 var context = $(this).data( 'wikiEditor-context' );
5050 $(this).eachAsync( {
@@ -55,7 +55,7 @@
5656 } );
5757 }
5858 )
59 - .bind( 'mouseup scrollToPosition focus',
 59+ .bind( 'mouseup scrollToPosition focus keyup encapsulateSelection',
6060 function( event ) {
6161 var context = $(this).data( 'wikiEditor-context' );
6262 $(this).eachAsync( {
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.delayedBind.js
@@ -1,40 +1,69 @@
22 (function( $ ) {
 3+/**
 4+ * Function that escapes spaces in event names. This is needed because
 5+ * "_delayedBind-foo bar-1000" refers to two events
 6+ */
 7+function encodeEvent( event ) {
 8+ return event.replace( /-/g, '--' ).replace( / /g, '-' );
 9+}
 10+
311 $.fn.extend( {
412 /**
513 * Bind a callback to an event in a delayed fashion.
614 * In detail, this means that the callback will be called a certain
715 * time after the event fires, but the timer is reset every time
816 * the event fires.
 17+ * @param timeout Number of milliseconds to wait
918 * @param event Name of the event (string)
 19+ * @param data Data to pass to the event handler (optional)
1020 * @param callback Function to call
11 - * @param timeout Number of milliseconds to wait
1221 */
13 - delayedBind: function( event, callback, timeout ) {
 22+ // TODO: Handle data param properly
 23+ delayedBind: function( timeout, event, data, callback ) {
 24+ var encEvent = encodeEvent( event );
1425 return this.each( function() {
1526 var that = this;
16 - $(this).bind( event, function() {
17 - var timerID = $(this).data( '_delayedBindTimerID-' + event );
18 - var args = arguments;
19 - // Cancel the running timer
20 - if ( typeof timerID != 'undefined' )
21 - clearTimeout( timerID );
22 - timerID = setTimeout( function() {
23 - callback.apply( that, args );
 27+ // Bind the top half
 28+ // Do this only once for every (event, timeout) pair
 29+ if ( !( $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout ) ) ) {
 30+ $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout, true );
 31+ $(this).bind( event, function() {
 32+ var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
 33+ // Cancel the running timer
 34+ if ( typeof timerID != 'undefined' )
 35+ clearTimeout( timerID );
 36+ timerID = setTimeout( function() {
 37+ $(that).trigger( '_delayedBind-' + encEvent + '-' + timeout );
2438 }, timeout );
25 - $(this).data( '_delayedBindTimerID-' + event, timerID );
26 - } );
 39+ $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout, timerID );
 40+ } );
 41+ }
 42+
 43+ // Bottom half
 44+ $(this).bind( '_delayedBind-' + encEvent + '-' + timeout, data, callback );
2745 } );
2846 },
2947
3048 /**
3149 * Cancel the timers for delayed events on the selected elements.
3250 */
33 - delayedBindCancel: function( event ) {
 51+ delayedBindCancel: function( timeout, event ) {
 52+ var encEvent = encodeEvent( event );
3453 return this.each( function() {
35 - var timerID = $(this).data( '_delayedBindTimerID-' + event );
 54+ var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
3655 if ( typeof timerID != 'undefined' )
3756 clearTimeout( timerID );
3857 } );
 58+ },
 59+
 60+ /**
 61+ * Unbind an event bound with delayedBind()
 62+ */
 63+ delayedBindUnbind: function( timeout, event, callback ) {
 64+ var encEvent = encodeEvent( event );
 65+ return this.each( function() {
 66+ $(this).unbind( '_delayedBind-' + encEvent + '-' + timeout, callback );
 67+ } );
3968 }
4069 } );
4170 } )( jQuery );
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.js
@@ -276,42 +276,71 @@
277277 };
278278
279279 (function( $ ) {
 280+/**
 281+ * Function that escapes spaces in event names. This is needed because
 282+ * "_delayedBind-foo bar-1000" refers to two events
 283+ */
 284+function encodeEvent( event ) {
 285+ return event.replace( /-/g, '--' ).replace( / /g, '-' );
 286+}
 287+
280288 $.fn.extend( {
281289 /**
282290 * Bind a callback to an event in a delayed fashion.
283291 * In detail, this means that the callback will be called a certain
284292 * time after the event fires, but the timer is reset every time
285293 * the event fires.
 294+ * @param timeout Number of milliseconds to wait
286295 * @param event Name of the event (string)
 296+ * @param data Data to pass to the event handler (optional)
287297 * @param callback Function to call
288 - * @param timeout Number of milliseconds to wait
289298 */
290 - delayedBind: function( event, callback, timeout ) {
 299+ // TODO: Handle data param properly
 300+ delayedBind: function( timeout, event, data, callback ) {
 301+ var encEvent = encodeEvent( event );
291302 return this.each( function() {
292303 var that = this;
293 - $(this).bind( event, function() {
294 - var timerID = $(this).data( '_delayedBindTimerID-' + event );
295 - var args = arguments;
296 - // Cancel the running timer
297 - if ( typeof timerID != 'undefined' )
298 - clearTimeout( timerID );
299 - timerID = setTimeout( function() {
300 - callback.apply( that, args );
 304+ // Bind the top half
 305+ // Do this only once for every (event, timeout) pair
 306+ if ( !( $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout ) ) ) {
 307+ $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout, true );
 308+ $(this).bind( event, function() {
 309+ var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
 310+ // Cancel the running timer
 311+ if ( typeof timerID != 'undefined' )
 312+ clearTimeout( timerID );
 313+ timerID = setTimeout( function() {
 314+ $(that).trigger( '_delayedBind-' + encEvent + '-' + timeout );
301315 }, timeout );
302 - $(this).data( '_delayedBindTimerID-' + event, timerID );
303 - } );
 316+ $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout, timerID );
 317+ } );
 318+ }
 319+
 320+ // Bottom half
 321+ $(this).bind( '_delayedBind-' + encEvent + '-' + timeout, data, callback );
304322 } );
305323 },
306324
307325 /**
308326 * Cancel the timers for delayed events on the selected elements.
309327 */
310 - delayedBindCancel: function( event ) {
 328+ delayedBindCancel: function( timeout, event ) {
 329+ var encEvent = encodeEvent( event );
311330 return this.each( function() {
312 - var timerID = $(this).data( '_delayedBindTimerID-' + event );
 331+ var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
313332 if ( typeof timerID != 'undefined' )
314333 clearTimeout( timerID );
315334 } );
 335+ },
 336+
 337+ /**
 338+ * Unbind an event bound with delayedBind()
 339+ */
 340+ delayedBindUnbind: function( timeout, event, callback ) {
 341+ var encEvent = encodeEvent( event );
 342+ return this.each( function() {
 343+ $(this).unbind( '_delayedBind-' + encEvent + '-' + timeout, callback );
 344+ } );
316345 }
317346 } );
318347 } )( jQuery );
@@ -1947,7 +1976,7 @@
19481977 $.wikiEditor.modules.toc.fn.build( context );
19491978 $.wikiEditor.modules.toc.fn.update( context );
19501979 context.$textarea
1951 - .bind( 'keyup encapsulateSelection',
 1980+ .delayedBind( 1000, 'keyup encapsulateSelection',
19521981 function( event ) {
19531982 var context = $(this).data( 'wikiEditor-context' );
19541983 $(this).eachAsync( {
@@ -1959,7 +1988,7 @@
19601989 } );
19611990 }
19621991 )
1963 - .bind( 'mouseup scrollToPosition focus',
 1992+ .bind( 'mouseup scrollToPosition focus keyup encapsulateSelection',
19641993 function( event ) {
19651994 var context = $(this).data( 'wikiEditor-context' );
19661995 $(this).eachAsync( {
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js
@@ -18,9 +18,11 @@
1919 var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toUTCString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;}
2020 expires='; expires='+date.toUTCString();}
2121 var path=options.path?'; path='+(options.path):'';var domain=options.domain?'; domain='+(options.domain):'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i<cookies.length;i++){var cookie=jQuery.trim(cookies[i]);if(cookie.substring(0,name.length+1)==(name+'=')){cookieValue=decodeURIComponent(cookie.substring(name.length+1));break;}}}
22 -return cookieValue;}};(function($){$.fn.extend({delayedBind:function(event,callback,timeout){return this.each(function(){var that=this;$(this).bind(event,function(){var timerID=$(this).data('_delayedBindTimerID-'+event);var args=arguments;if(typeof timerID!='undefined')
23 -clearTimeout(timerID);timerID=setTimeout(function(){callback.apply(that,args);},timeout);$(this).data('_delayedBindTimerID-'+event,timerID);});});},delayedBindCancel:function(event){return this.each(function(){var timerID=$(this).data('_delayedBindTimerID-'+event);if(typeof timerID!='undefined')
24 -clearTimeout(timerID);});}});})(jQuery);(function($){$.fn.namespaceSelector=function(defaultNS){if(typeof defaultNS=='undefined')
 22+return cookieValue;}};(function($){function encodeEvent(event){return event.replace(/-/g,'--').replace(/ /g,'-');}
 23+$.fn.extend({delayedBind:function(timeout,event,data,callback){var encEvent=encodeEvent(event);return this.each(function(){var that=this;if(!($(this).data('_delayedBindBound-'+encEvent+'-'+timeout))){$(this).data('_delayedBindBound-'+encEvent+'-'+timeout,true);$(this).bind(event,function(){var timerID=$(this).data('_delayedBindTimerID-'+encEvent+'-'+timeout);if(typeof timerID!='undefined')
 24+clearTimeout(timerID);timerID=setTimeout(function(){$(that).trigger('_delayedBind-'+encEvent+'-'+timeout);},timeout);$(this).data('_delayedBindTimerID-'+encEvent+'-'+timeout,timerID);});}
 25+$(this).bind('_delayedBind-'+encEvent+'-'+timeout,data,callback);});},delayedBindCancel:function(timeout,event){var encEvent=encodeEvent(event);return this.each(function(){var timerID=$(this).data('_delayedBindTimerID-'+encEvent+'-'+timeout);if(typeof timerID!='undefined')
 26+clearTimeout(timerID);});},delayedBindUnbind:function(timeout,event,callback){var encEvent=encodeEvent(event);return this.each(function(){$(this).unbind('_delayedBind-'+encEvent+'-'+timeout,callback);});}});})(jQuery);(function($){$.fn.namespaceSelector=function(defaultNS){if(typeof defaultNS=='undefined')
2527 defaultNS=0;return this.each(function(){for(var id in wgFormattedNamespaces){var opt=$('<option />').attr('value',id).text(wgFormattedNamespaces[id]);if(id==defaultNS)
2628 opt.attr('selected','selected');opt.appendTo($(this));}});};})(jQuery);(function($){$.suggestions={cancel:function(context){if(context.data.timerID!=null){clearTimeout(context.data.timerID);}
2729 if(typeof context.config.cancel=='function'){context.config.cancel.call(context.data.$textbox);}},restore:function(context){context.data.$textbox.val(context.data.prevText);},update:function(context,delayed){function maybeFetch(){if(context.data.$textbox.val()!==context.data.prevText){context.data.prevText=context.data.$textbox.val();if(typeof context.config.fetch=='function'){context.config.fetch.call(context.data.$textbox,context.data.$textbox.val());}}}
@@ -125,7 +127,7 @@
126128 return $section;},updateBookletSelection:function(context,id,$pages,$index){var cookie='wikiEditor-'+context.instance+'-booklet-'+id+'-page';var selected=$.cookie(cookie);var $selectedIndex=$index.find('*[rel='+selected+']');if($selectedIndex.size()==0){selected=$index.children().eq(0).attr('rel');$.cookie(cookie,selected);}
127129 $pages.children().hide();$pages.find('*[rel='+selected+']').show();$index.children().removeClass('current');$selectedIndex.addClass('current');},build:function(context,config){var $tabs=$('<div />').addClass('tabs').appendTo(context.modules.$toolbar);var $sections=$('<div />').addClass('sections').appendTo(context.modules.$toolbar);context.modules.$toolbar.append($('<div />').css('clear','both'));var sectionQueue=[];for(section in config){if(section=='main'){context.modules.$toolbar.prepend($.wikiEditor.modules.toolbar.fn.buildSection(context,section,config[section]));}else{sectionQueue.push({'$sections':$sections,'context':context,'id':section,'config':config[section]});$tabs.append($.wikiEditor.modules.toolbar.fn.buildTab(context,section,config[section]));}}
128130 $.eachAsync(sectionQueue,{'bulk':0,'end':function(){$('body').css('position','static');$('body').css('position','relative');},'loop':function(i,s){s.$sections.append($.wikiEditor.modules.toolbar.fn.buildSection(s.context,s.id,s.config));}});}}};})(jQuery);(function($){$.wikiEditor.modules.toc={api:{},fn:{create:function(context,config){if('$toc'in context.modules){return;}
129 -context.modules.$toc=$('<div></div>').addClass('wikiEditor-ui-toc').attr('id','wikiEditor-ui-toc');$.wikiEditor.modules.toc.fn.build(context,config);context.$ui.find('.wikiEditor-ui-bottom').append(context.modules.$toc);context.modules.$toc.height(context.$ui.find('.wikiEditor-ui-bottom').height());context.modules.$toc.css('width','12em').css('marginTop',-(context.$ui.find('.wikiEditor-ui-bottom').height()));context.$ui.find('.wikiEditor-ui-text').css(($('body.rtl').size()?'marginLeft':'marginRight'),'12em');$.wikiEditor.modules.toc.fn.build(context);$.wikiEditor.modules.toc.fn.update(context);context.$textarea.bind('keyup encapsulateSelection',function(event){var context=$(this).data('wikiEditor-context');$(this).eachAsync({bulk:0,loop:function(){$.wikiEditor.modules.toc.fn.build(context);$.wikiEditor.modules.toc.fn.update(context);}});}).bind('mouseup scrollToPosition focus',function(event){var context=$(this).data('wikiEditor-context');$(this).eachAsync({bulk:0,loop:function(){$.wikiEditor.modules.toc.fn.update(context);}});}).blur(function(){var context=$(this).data('wikiEditor-context');$.wikiEditor.modules.toc.fn.unhighlight(context);});},unhighlight:function(context){context.modules.$toc.find('a').removeClass('currentSelection');},update:function(context){$.wikiEditor.modules.toc.fn.unhighlight(context);var position=context.$textarea.getCaretPosition();var section=0;if(context.data.outline.length>0){if(!(position<context.data.outline[0].position-1)){while(section<context.data.outline.length&&context.data.outline[section].position-1<position){section++;}
 131+context.modules.$toc=$('<div></div>').addClass('wikiEditor-ui-toc').attr('id','wikiEditor-ui-toc');$.wikiEditor.modules.toc.fn.build(context,config);context.$ui.find('.wikiEditor-ui-bottom').append(context.modules.$toc);context.modules.$toc.height(context.$ui.find('.wikiEditor-ui-bottom').height());context.modules.$toc.css('width','12em').css('marginTop',-(context.$ui.find('.wikiEditor-ui-bottom').height()));context.$ui.find('.wikiEditor-ui-text').css(($('body.rtl').size()?'marginLeft':'marginRight'),'12em');$.wikiEditor.modules.toc.fn.build(context);$.wikiEditor.modules.toc.fn.update(context);context.$textarea.delayedBind(1000,'keyup encapsulateSelection',function(event){var context=$(this).data('wikiEditor-context');$(this).eachAsync({bulk:0,loop:function(){$.wikiEditor.modules.toc.fn.build(context);$.wikiEditor.modules.toc.fn.update(context);}});}).bind('mouseup scrollToPosition focus keyup encapsulateSelection',function(event){var context=$(this).data('wikiEditor-context');$(this).eachAsync({bulk:0,loop:function(){$.wikiEditor.modules.toc.fn.update(context);}});}).blur(function(){var context=$(this).data('wikiEditor-context');$.wikiEditor.modules.toc.fn.unhighlight(context);});},unhighlight:function(context){context.modules.$toc.find('a').removeClass('currentSelection');},update:function(context){$.wikiEditor.modules.toc.fn.unhighlight(context);var position=context.$textarea.getCaretPosition();var section=0;if(context.data.outline.length>0){if(!(position<context.data.outline[0].position-1)){while(section<context.data.outline.length&&context.data.outline[section].position-1<position){section++;}
130132 section=Math.max(0,section);}
131133 context.modules.$toc.find('a.section-'+section).addClass('currentSelection');}},build:function(context){function buildStructure(outline,offset,level){if(offset==undefined)offset=0;if(level==undefined)level=1;var sections=[];for(var i=offset;i<outline.length;i++){if(outline[i].nLevel==level){var sub=buildStructure(outline,i+1,level+1);if(sub.length){outline[i].sections=sub;}
132134 sections[sections.length]=outline[i];}else if(outline[i].nLevel<level){break;}}

Status & tagging log