r67755 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r67754‎ | r67755 | r67756 >
Date:20:18, 9 June 2010
Author:adam
Status:deferred (Comments)
Tags:
Comment:
Adding emboldening to simple search via autoellipsis. Also reenabling textbox updating while navigation search suggestions.
Modified paths:
  • /trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/Vector/Modules/SimpleSearch/SimpleSearch.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/Vector/Vector.combined.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/Vector/Vector.combined.min.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/Vector/Vector.hooks.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/css/suggestions.css (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.autoEllipsis.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins/jquery.suggestions.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UsabilityInitiative/css/suggestions.css
@@ -38,10 +38,8 @@
3939 color: WindowText;
4040 margin: 0;
4141 width: 100%;
42 -}
43 -.suggestions-result span {
4442 line-height: 1.5em;
45 - padding: 0.25em 0.25em;
 43+ padding: 0.01em 0.25em;
4644 }
4745 .suggestions-result-current {
4846 background-color: #4C59A6;
@@ -64,4 +62,7 @@
6563 .suggestions-result-current .special-query {
6664 color: white;
6765 color: HighlightText;
 66+}
 67+.autoellipsis-matched {
 68+ font-weight: bold;
6869 }
\ No newline at end of file
Index: trunk/extensions/UsabilityInitiative/Vector/Modules/SimpleSearch/SimpleSearch.js
@@ -110,7 +110,8 @@
111111 }
112112 },
113113 delay: 120,
114 - positionFromLeft: $j( 'body' ).is( '.rtl' )
 114+ positionFromLeft: $j( 'body' ).is( '.rtl' ),
 115+ highlightInput: true
115116 } )
116117 .bind( 'paste cut click', function() {
117118 $j( this ).trigger( 'keypress' );
Index: trunk/extensions/UsabilityInitiative/Vector/Vector.hooks.php
@@ -17,13 +17,13 @@
1818 array( 'src' => 'Modules/ExpandableSearch/ExpandableSearch.js', 'version' => 3 ),
1919 array( 'src' => 'Modules/EditWarning/EditWarning.js', 'version' => 8 ),
2020 array( 'src' => 'Modules/FooterCleanup/FooterCleanup.js', 'version' => 5 ),
21 - array( 'src' => 'Modules/SimpleSearch/SimpleSearch.js', 'version' => 15 ),
 21+ array( 'src' => 'Modules/SimpleSearch/SimpleSearch.js', 'version' => 16 ),
2222 ),
2323 'combined' => array(
24 - array( 'src' => 'Vector.combined.js', 'version' => 48 ),
 24+ array( 'src' => 'Vector.combined.js', 'version' => 49 ),
2525 ),
2626 'minified' => array(
27 - array( 'src' => 'Vector.combined.min.js', 'version' => 48 ),
 27+ array( 'src' => 'Vector.combined.min.js', 'version' => 49 ),
2828 ),
2929 );
3030 static $modules = array(
Index: trunk/extensions/UsabilityInitiative/Vector/Vector.combined.js
@@ -568,7 +568,8 @@
569569 }
570570 },
571571 delay: 120,
572 - positionFromLeft: $j( 'body' ).is( '.rtl' )
 572+ positionFromLeft: $j( 'body' ).is( '.rtl' ),
 573+ highlightInput: true
573574 } )
574575 .bind( 'paste cut click', function() {
575576 $j( this ).trigger( 'keypress' );
Index: trunk/extensions/UsabilityInitiative/Vector/Vector.combined.min.js
@@ -27,5 +27,5 @@
2828 var mod={'browsers':{'ltr':{'opera':[['>=',9.6]],'blackberry':false,'ipod':false,'iphone':false},'rtl':{'opera':[['>=',9.6]],'blackberry':false,'ipod':false,'iphone':false}}};if(!$j.wikiEditor.isSupported(mod)){return true;}
2929 $j('div#simpleSearch > input#searchInput').each(function(){$j('<label />').text(mw.usability.getMsg('vector-simplesearch-search')).css({'display':'none','position':'absolute','bottom':0,'padding':'0.25em','color':'#999999','cursor':'text'}).css(($j('body').is('.rtl')?'right':'left'),0).click(function(){$j(this).parent().find('input#searchInput').focus();}).appendTo($j(this).parent());if($j(this).val()==''){$j(this).parent().find('label').fadeIn(100);}}).bind('keypress',function(){if($j(this).parent().find('label:visible').size()>0)
3030 $j(this).parent().find('label').fadeOut(100);}).focus(function(){$j(this).parent().find('label').fadeOut(100);}).blur(function(){if($j(this).val()==''){$j(this).parent().find('label').fadeIn(100);}});$j(document).bind('dragend',function(event){if($j('div#simpleSearch > label:visible').size()>0&&$j('div#simpleSearch > input#searchInput').val().length>0)
31 -$j('div#simpleSearch > label').fadeOut(100);});$j('#searchInput, #searchInput2, #powerSearchText, #searchText').suggestions({fetch:function(query){var $this=$j(this);var request=$j.ajax({url:wgScriptPath+'/api.php',data:{'action':'opensearch','search':query,'namespace':0,'suggest':''},dataType:'json',success:function(data){$this.suggestions('suggestions',data[1]);}});$j(this).data('request',request);},cancel:function(){var request=$j(this).data('request');if(request&&typeof request.abort=='function'){request.abort();$j(this).removeData('request');}},result:{select:function($textbox){$textbox.closest('form').submit();}},delay:120,positionFromLeft:$j('body').is('.rtl')}).bind('paste cut click',function(){$j(this).trigger('keypress');});$j('#searchInput').suggestions({result:{select:function($textbox){$textbox.closest('form').submit();}},special:{render:function(query){if($j(this).children().size()==0){$j(this).show()
 31+$j('div#simpleSearch > label').fadeOut(100);});$j('#searchInput, #searchInput2, #powerSearchText, #searchText').suggestions({fetch:function(query){var $this=$j(this);var request=$j.ajax({url:wgScriptPath+'/api.php',data:{'action':'opensearch','search':query,'namespace':0,'suggest':''},dataType:'json',success:function(data){$this.suggestions('suggestions',data[1]);}});$j(this).data('request',request);},cancel:function(){var request=$j(this).data('request');if(request&&typeof request.abort=='function'){request.abort();$j(this).removeData('request');}},result:{select:function($textbox){$textbox.closest('form').submit();}},delay:120,positionFromLeft:$j('body').is('.rtl'),highlightInput:true}).bind('paste cut click',function(){$j(this).trigger('keypress');});$j('#searchInput').suggestions({result:{select:function($textbox){$textbox.closest('form').submit();}},special:{render:function(query){if($j(this).children().size()==0){$j(this).show()
3232 $label=$j('<div />').addClass('special-label').text(mw.usability.getMsg('vector-simplesearch-containing')).appendTo($j(this));$query=$j('<div />').addClass('special-query').text(query).appendTo($j(this));$query.autoEllipsis();}else{$j(this).find('.special-query').empty().text(query).autoEllipsis();}},select:function($textbox){$textbox.closest('form').append($j('<input />').attr({'type':'hidden','name':'fulltext','value':1}));$textbox.closest('form').submit();}},$region:$j('#simpleSearch')}).bind('paste cut click',function(){$j(this).trigger('keypress');});});
\ No newline at end of file
Index: trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php
@@ -18,7 +18,7 @@
1919 private static $styleFiles = array(
2020 'base_sets' => array(
2121 'raw' => array(
22 - array( 'src' => 'css/suggestions.css', 'version' => 14 ),
 22+ array( 'src' => 'css/suggestions.css', 'version' => 15 ),
2323 array( 'src' => 'css/vector.collapsibleNav.css', 'version' => 12 ),
2424 array( 'src' => 'css/vector.expandableSearch.css', 'version' => 3 ),
2525 array( 'src' => 'css/vector.footerCleanup.css', 'version' => 2 ),
@@ -30,11 +30,11 @@
3131 array( 'src' => 'css/vector/jquery-ui-1.7.2.css', 'version' => '1.7.2y' ),
3232 ),
3333 'combined' => array(
34 - array( 'src' => 'css/combined.css', 'version' => 101 ),
 34+ array( 'src' => 'css/combined.css', 'version' => 102 ),
3535 array( 'src' => 'css/vector/jquery-ui-1.7.2.css', 'version' => '1.7.2y' ),
3636 ),
3737 'minified' => array(
38 - array( 'src' => 'css/combined.min.css', 'version' => 101 ),
 38+ array( 'src' => 'css/combined.min.css', 'version' => 102 ),
3939 array( 'src' => 'css/vector/jquery-ui-1.7.2.css', 'version' => '1.7.2y' ),
4040 ),
4141 )
@@ -65,7 +65,7 @@
6666
6767 // Core functionality of extension scripts
6868 array( 'src' => 'js/plugins/jquery.async.js', 'version' => 3 ),
69 - array( 'src' => 'js/plugins/jquery.autoEllipsis.js', 'version' => 11 ),
 69+ array( 'src' => 'js/plugins/jquery.autoEllipsis.js', 'version' => 12 ),
7070 array( 'src' => 'js/plugins/jquery.browser.js', 'version' => 8 ),
7171 array( 'src' => 'js/plugins/jquery.collapsibleTabs.js', 'version' => 6 ),
7272 array( 'src' => 'js/plugins/jquery.color.js', 'version' => 1 ),
@@ -73,7 +73,7 @@
7474 array( 'src' => 'js/plugins/jquery.delayedBind.js', 'version' => 1 ),
7575 array( 'src' => 'js/plugins/jquery.suggestions.js', 'version' => 18 ),
7676 array( 'src' => 'js/plugins/jquery.expandableField.js', 'version' => 16 ),
77 - array( 'src' => 'js/plugins/jquery.suggestions.js', 'version' => 21 ),
 77+ array( 'src' => 'js/plugins/jquery.suggestions.js', 'version' => 22 ),
7878 array( 'src' => 'js/plugins/jquery.textSelection.js', 'version' => 35 ),
7979 array( 'src' => 'js/plugins/jquery.wikiEditor.js', 'version' => 193 ),
8080 array( 'src' => 'js/plugins/jquery.wikiEditor.highlight.js', 'version' => 53 ),
@@ -89,10 +89,10 @@
9090 array( 'src' => 'js/thirdparty/contentCollector.js', 'version' => 2 ),
9191 ),
9292 'combined' => array(
93 - array( 'src' => 'js/plugins.combined.js', 'version' => 422 ),
 93+ array( 'src' => 'js/plugins.combined.js', 'version' => 423 ),
9494 ),
9595 'minified' => array(
96 - array( 'src' => 'js/plugins.combined.min.js', 'version' => 422 ),
 96+ array( 'src' => 'js/plugins.combined.min.js', 'version' => 423 ),
9797 ),
9898 ),
9999 );
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.suggestions.js
@@ -38,6 +38,8 @@
3939 * Type: Number, Range: 1 - infinity, Default: 3
4040 * positionFromLeft: Whether to position the suggestion box with the left attribute or the right
4141 * Type: Boolean, Default: true
 42+ * highlightInput: Whether to hightlight matched portions of the input or not
 43+ * Type: Boolean, Default: false
4244 */
4345 ( function( $ ) {
4446
@@ -145,7 +147,9 @@
146148 $results.empty();
147149 var expWidth = -1;
148150 var $autoEllipseMe = $( [] );
 151+ var matchedText = null;
149152 for ( var i = 0; i < context.config.suggestions.length; i++ ) {
 153+ var text = context.config.suggestions[i];
150154 var $result = $( '<div />' )
151155 .addClass( 'suggestions-result' )
152156 .attr( 'rel', i )
@@ -156,16 +160,18 @@
157161 );
158162 } )
159163 .appendTo( $results );
160 -
161164 // Allow custom rendering
162165 if ( typeof context.config.result.render == 'function' ) {
163166 context.config.result.render.call( $result, context.config.suggestions[i] );
164167 } else {
165168 // Add <span> with text
 169+ if( context.config.highlightInput ) {
 170+ matchedText = text.substr( 0, context.data.prevText.length );
 171+ }
166172 $result.append( $( '<span />' )
167173 .css( 'whiteSpace', 'nowrap' )
168 - .text( context.config.suggestions[i] )
169 - );
 174+ .text( text )
 175+ );
170176
171177 // Widen results box if needed
172178 // New width is only calculated here, applied later
@@ -182,7 +188,7 @@
183189 context.data.$container.width( Math.min( expWidth, maxWidth ) );
184190 }
185191 // autoEllipse the results. Has to be done after changing the width
186 - $autoEllipseMe.autoEllipsis( { hasSpan: true, tooltip: true } );
 192+ $autoEllipseMe.autoEllipsis( { hasSpan: true, tooltip: true, matchText: matchedText } );
187193 }
188194 }
189195 break;
@@ -197,6 +203,7 @@
198204 break;
199205 case 'submitOnClick':
200206 case 'positionFromLeft':
 207+ case 'highlightInput':
201208 context.config[property] = value ? true : false;
202209 break;
203210 }
@@ -211,7 +218,7 @@
212219 if ( !result.get || selected.get( 0 ) != result.get( 0 ) ) {
213220 if ( result == 'prev' ) {
214221 if( selected.is( '.suggestions-special' ) ) {
215 - result = context.data.$container.find( '.suggestions-results div:last' )
 222+ result = context.data.$container.find( '.suggestions-result:last' )
216223 } else {
217224 result = selected.prev();
218225 if ( selected.length == 0 ) {
@@ -271,7 +278,7 @@
272279 // Arrow down
273280 case 40:
274281 if ( wasVisible ) {
275 - $.suggestions.highlight( context, 'next', false );
 282+ $.suggestions.highlight( context, 'next', true );
276283 } else {
277284 $.suggestions.update( context, false );
278285 }
@@ -280,7 +287,7 @@
281288 // Arrow up
282289 case 38:
283290 if ( wasVisible ) {
284 - $.suggestions.highlight( context, 'prev', false );
 291+ $.suggestions.highlight( context, 'prev', true );
285292 }
286293 preventDefault = wasVisible;
287294 break;
@@ -348,7 +355,8 @@
349356 'delay': 120,
350357 'submitOnClick': false,
351358 'maxExpandFactor': 3,
352 - 'positionFromLeft': true
 359+ 'positionFromLeft': true,
 360+ 'highlightInput': false
353361 }
354362 };
355363 }
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.autoEllipsis.js
@@ -11,7 +11,8 @@
1212 'position': 'center',
1313 'tooltip': false,
1414 'restoreText': false,
15 - 'hasSpan': false
 15+ 'hasSpan': false,
 16+ 'matchText': null
1617 }, options );
1718 $(this).each( function() {
1819 var $this = $(this);
@@ -22,51 +23,88 @@
2324 $this.text( $this.data( 'autoEllipsis.originalText' ) );
2425 }
2526 }
26 - var text = $this.text();
27 - var w = $this.width();
28 - var $text;
29 - if ( options.hasSpan ) {
30 - $text = $this.children( 'span' );
 27+
 28+ // container element - used for measuring against
 29+ var $container = $this;
 30+ // trimmable text element - only the text within this element will be trimmed
 31+ var $trimmableText = null;
 32+ // protected text element - the width of this element is counted, but next is never trimmed from it
 33+ var $protectedText = null;
 34+
 35+ if ( options.matchText ) {
 36+ var text = $this.text();
 37+ var matchedText = options.matchText;
 38+ $trimmableText = $( '<span />' )
 39+ .css( 'whiteSpace', 'nowrap' )
 40+ .addClass( 'autoellipsis-trimmed' )
 41+ .text( $this.text().substr( matchedText.length, $this.text().length ) );
 42+ $protectedText = $( '<span />' )
 43+ .addClass( 'autoellipsis-matched' )
 44+ .css( 'whiteSpace', 'nowrap' )
 45+ .text( options.matchText );
 46+ $container
 47+ .empty()
 48+ .append( $protectedText )
 49+ .append( $trimmableText );
3150 } else {
32 - $text = $( '<span />' ).css( 'whiteSpace', 'nowrap' );
33 - $this.empty().append( $text );
 51+ if ( options.hasSpan ) {
 52+ $trimmableText = $this.children( options.selector );
 53+ } else {
 54+ $trimmableText = $( '<span />' )
 55+ .css( 'whiteSpace', 'nowrap' )
 56+ .text( $this.text() );
 57+ $this
 58+ .empty()
 59+ .append( $trimmableText );
 60+ }
3461 }
3562
 63+ var text = $container.text();
 64+ var trimmableText = $trimmableText.text();
 65+ var w = $container.width();
 66+ var pw = $protectedText ? $protectedText.width() : 0;
3667 // Try cache
3768 if ( !( text in cache ) ) {
3869 cache[text] = {};
3970 }
40 - if ( w in cache[text] ) {
41 - $text.text( cache[text][w] );
 71+ if ( options.matchText && !( options.matchText in cache[text] ) ) {
 72+ cache[text][options.matchText] = {};
 73+ }
 74+ if ( !options.matchText && w in cache[text] ) {
 75+ $container.html( cache[text][w] );
4276 if ( options.tooltip )
43 - $text.attr( 'title', text );
 77+ $container.attr( 'title', text );
4478 return;
4579 }
46 -
47 - $text.text( text );
48 - if ( $text.width() > w ) {
 80+ if( options.matchText && options.matchText in cache[text] && w in cache[text][options.matchText] ) {
 81+ $container.html( cache[text][options.matchText][w] );
 82+ if ( options.tooltip )
 83+ $container.attr( 'title', text );
 84+ return;
 85+ }
 86+ if ( $trimmableText.width() + pw > w ) {
4987 switch ( options.position ) {
5088 case 'right':
5189 // Use binary search-like technique for efficiency
52 - var l = 0, r = text.length;
 90+ var l = 0, r = trimmableText.length;
5391 do {
5492 var m = Math.ceil( ( l + r ) / 2 );
55 - $text.text( text.substr( 0, m ) + '...' );
56 - if ( $text.width() > w ) {
 93+ $trimmableText.text( trimmableText.substr( 0, m ) + '...' );
 94+ if ( $trimmableText.width() + pw > w ) {
5795 // Text is too long
5896 r = m - 1;
5997 } else {
6098 l = m;
6199 }
62100 } while ( l < r );
63 - $text.text( text.substr( 0, l ) + '...' );
 101+ $trimmableText.text( trimmableText.substr( 0, l ) + '...' );
64102 break;
65103 case 'center':
66104 // TODO: Use binary search like for 'right'
67 - var i = [Math.round( text.length / 2 ), Math.round( text.length / 2 )];
 105+ var i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
68106 var side = 1; // Begin with making the end shorter
69 - while ( $text.outerWidth() > w && i[0] > 0 ) {
70 - $text.text( text.substr( 0, i[0] ) + '...' + text.substr( i[1] ) );
 107+ while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
 108+ $trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
71109 // Alternate between trimming the end and begining
72110 if ( side == 0 ) {
73111 // Make the begining shorter
@@ -82,16 +120,22 @@
83121 case 'left':
84122 // TODO: Use binary search like for 'right'
85123 var r = 0;
86 - while ( $text.outerWidth() > w && r < text.length ) {
87 - $text.text( '...' + text.substr( r ) );
 124+ while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
 125+ $trimmableText.text( '...' + trimmableText.substr( r ) );
88126 r++;
89127 }
90128 break;
91129 }
92130 }
93 - if ( options.tooltip )
94 - $text.attr( 'title', text );
95 - cache[text][w] = $text.text();
 131+ if ( options.tooltip ) {
 132+ $container.attr( 'title', text );
 133+ }
 134+ if ( options.matchText ) {
 135+ cache[text][options.matchText][w] = $container.html();
 136+ } else {
 137+ cache[text][w] = $container.html();
 138+ }
 139+
96140 } );
97141 };
98142
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.js
@@ -5255,7 +5255,8 @@
52565256 'position': 'center',
52575257 'tooltip': false,
52585258 'restoreText': false,
5259 - 'hasSpan': false
 5259+ 'hasSpan': false,
 5260+ 'matchText': null
52605261 }, options );
52615262 $(this).each( function() {
52625263 var $this = $(this);
@@ -5266,51 +5267,88 @@
52675268 $this.text( $this.data( 'autoEllipsis.originalText' ) );
52685269 }
52695270 }
5270 - var text = $this.text();
5271 - var w = $this.width();
5272 - var $text;
5273 - if ( options.hasSpan ) {
5274 - $text = $this.children( 'span' );
 5271+
 5272+ // container element - used for measuring against
 5273+ var $container = $this;
 5274+ // trimmable text element - only the text within this element will be trimmed
 5275+ var $trimmableText = null;
 5276+ // protected text element - the width of this element is counted, but next is never trimmed from it
 5277+ var $protectedText = null;
 5278+
 5279+ if ( options.matchText ) {
 5280+ var text = $this.text();
 5281+ var matchedText = options.matchText;
 5282+ $trimmableText = $( '<span />' )
 5283+ .css( 'whiteSpace', 'nowrap' )
 5284+ .addClass( 'autoellipsis-trimmed' )
 5285+ .text( $this.text().substr( matchedText.length, $this.text().length ) );
 5286+ $protectedText = $( '<span />' )
 5287+ .addClass( 'autoellipsis-matched' )
 5288+ .css( 'whiteSpace', 'nowrap' )
 5289+ .text( options.matchText );
 5290+ $container
 5291+ .empty()
 5292+ .append( $protectedText )
 5293+ .append( $trimmableText );
52755294 } else {
5276 - $text = $( '<span />' ).css( 'whiteSpace', 'nowrap' );
5277 - $this.empty().append( $text );
 5295+ if ( options.hasSpan ) {
 5296+ $trimmableText = $this.children( options.selector );
 5297+ } else {
 5298+ $trimmableText = $( '<span />' )
 5299+ .css( 'whiteSpace', 'nowrap' )
 5300+ .text( $this.text() );
 5301+ $this
 5302+ .empty()
 5303+ .append( $trimmableText );
 5304+ }
52785305 }
52795306
 5307+ var text = $container.text();
 5308+ var trimmableText = $trimmableText.text();
 5309+ var w = $container.width();
 5310+ var pw = $protectedText ? $protectedText.width() : 0;
52805311 // Try cache
52815312 if ( !( text in cache ) ) {
52825313 cache[text] = {};
52835314 }
5284 - if ( w in cache[text] ) {
5285 - $text.text( cache[text][w] );
 5315+ if ( options.matchText && !( options.matchText in cache[text] ) ) {
 5316+ cache[text][options.matchText] = {};
 5317+ }
 5318+ if ( !options.matchText && w in cache[text] ) {
 5319+ $container.html( cache[text][w] );
52865320 if ( options.tooltip )
5287 - $text.attr( 'title', text );
 5321+ $container.attr( 'title', text );
52885322 return;
52895323 }
5290 -
5291 - $text.text( text );
5292 - if ( $text.width() > w ) {
 5324+ if( options.matchText && options.matchText in cache[text] && w in cache[text][options.matchText] ) {
 5325+ $container.html( cache[text][options.matchText][w] );
 5326+ if ( options.tooltip )
 5327+ $container.attr( 'title', text );
 5328+ return;
 5329+ }
 5330+ if ( $trimmableText.width() + pw > w ) {
52935331 switch ( options.position ) {
52945332 case 'right':
52955333 // Use binary search-like technique for efficiency
5296 - var l = 0, r = text.length;
 5334+ var l = 0, r = trimmableText.length;
52975335 do {
52985336 var m = Math.ceil( ( l + r ) / 2 );
5299 - $text.text( text.substr( 0, m ) + '...' );
5300 - if ( $text.width() > w ) {
 5337+ $trimmableText.text( trimmableText.substr( 0, m ) + '...' );
 5338+ if ( $trimmableText.width() + pw > w ) {
53015339 // Text is too long
53025340 r = m - 1;
53035341 } else {
53045342 l = m;
53055343 }
53065344 } while ( l < r );
5307 - $text.text( text.substr( 0, l ) + '...' );
 5345+ $trimmableText.text( trimmableText.substr( 0, l ) + '...' );
53085346 break;
53095347 case 'center':
53105348 // TODO: Use binary search like for 'right'
5311 - var i = [Math.round( text.length / 2 ), Math.round( text.length / 2 )];
 5349+ var i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
53125350 var side = 1; // Begin with making the end shorter
5313 - while ( $text.outerWidth() > w && i[0] > 0 ) {
5314 - $text.text( text.substr( 0, i[0] ) + '...' + text.substr( i[1] ) );
 5351+ while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
 5352+ $trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
53155353 // Alternate between trimming the end and begining
53165354 if ( side == 0 ) {
53175355 // Make the begining shorter
@@ -5326,16 +5364,22 @@
53275365 case 'left':
53285366 // TODO: Use binary search like for 'right'
53295367 var r = 0;
5330 - while ( $text.outerWidth() > w && r < text.length ) {
5331 - $text.text( '...' + text.substr( r ) );
 5368+ while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
 5369+ $trimmableText.text( '...' + trimmableText.substr( r ) );
53325370 r++;
53335371 }
53345372 break;
53355373 }
53365374 }
5337 - if ( options.tooltip )
5338 - $text.attr( 'title', text );
5339 - cache[text][w] = $text.text();
 5375+ if ( options.tooltip ) {
 5376+ $container.attr( 'title', text );
 5377+ }
 5378+ if ( options.matchText ) {
 5379+ cache[text][options.matchText][w] = $container.html();
 5380+ } else {
 5381+ cache[text][w] = $container.html();
 5382+ }
 5383+
53405384 } );
53415385 };
53425386
@@ -5997,6 +6041,8 @@
59986042 * Type: Number, Range: 1 - infinity, Default: 3
59996043 * positionFromLeft: Whether to position the suggestion box with the left attribute or the right
60006044 * Type: Boolean, Default: true
 6045+ * highlightInput: Whether to hightlight matched portions of the input or not
 6046+ * Type: Boolean, Default: false
60016047 */
60026048 ( function( $ ) {
60036049
@@ -6104,7 +6150,9 @@
61056151 $results.empty();
61066152 var expWidth = -1;
61076153 var $autoEllipseMe = $( [] );
 6154+ var matchedText = null;
61086155 for ( var i = 0; i < context.config.suggestions.length; i++ ) {
 6156+ var text = context.config.suggestions[i];
61096157 var $result = $( '<div />' )
61106158 .addClass( 'suggestions-result' )
61116159 .attr( 'rel', i )
@@ -6115,16 +6163,18 @@
61166164 );
61176165 } )
61186166 .appendTo( $results );
6119 -
61206167 // Allow custom rendering
61216168 if ( typeof context.config.result.render == 'function' ) {
61226169 context.config.result.render.call( $result, context.config.suggestions[i] );
61236170 } else {
61246171 // Add <span> with text
 6172+ if( context.config.highlightInput ) {
 6173+ matchedText = text.substr( 0, context.data.prevText.length );
 6174+ }
61256175 $result.append( $( '<span />' )
61266176 .css( 'whiteSpace', 'nowrap' )
6127 - .text( context.config.suggestions[i] )
6128 - );
 6177+ .text( text )
 6178+ );
61296179
61306180 // Widen results box if needed
61316181 // New width is only calculated here, applied later
@@ -6141,7 +6191,7 @@
61426192 context.data.$container.width( Math.min( expWidth, maxWidth ) );
61436193 }
61446194 // autoEllipse the results. Has to be done after changing the width
6145 - $autoEllipseMe.autoEllipsis( { hasSpan: true, tooltip: true } );
 6195+ $autoEllipseMe.autoEllipsis( { hasSpan: true, tooltip: true, matchText: matchedText } );
61466196 }
61476197 }
61486198 break;
@@ -6156,6 +6206,7 @@
61576207 break;
61586208 case 'submitOnClick':
61596209 case 'positionFromLeft':
 6210+ case 'highlightInput':
61606211 context.config[property] = value ? true : false;
61616212 break;
61626213 }
@@ -6170,7 +6221,7 @@
61716222 if ( !result.get || selected.get( 0 ) != result.get( 0 ) ) {
61726223 if ( result == 'prev' ) {
61736224 if( selected.is( '.suggestions-special' ) ) {
6174 - result = context.data.$container.find( '.suggestions-results div:last' )
 6225+ result = context.data.$container.find( '.suggestions-result:last' )
61756226 } else {
61766227 result = selected.prev();
61776228 if ( selected.length == 0 ) {
@@ -6230,7 +6281,7 @@
62316282 // Arrow down
62326283 case 40:
62336284 if ( wasVisible ) {
6234 - $.suggestions.highlight( context, 'next', false );
 6285+ $.suggestions.highlight( context, 'next', true );
62356286 } else {
62366287 $.suggestions.update( context, false );
62376288 }
@@ -6239,7 +6290,7 @@
62406291 // Arrow up
62416292 case 38:
62426293 if ( wasVisible ) {
6243 - $.suggestions.highlight( context, 'prev', false );
 6294+ $.suggestions.highlight( context, 'prev', true );
62446295 }
62456296 preventDefault = wasVisible;
62466297 break;
@@ -6307,7 +6358,8 @@
63086359 'delay': 120,
63096360 'submitOnClick': false,
63106361 'maxExpandFactor': 3,
6311 - 'positionFromLeft': true
 6362+ 'positionFromLeft': true,
 6363+ 'highlightInput': false
63126364 }
63136365 };
63146366 }
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js
@@ -361,16 +361,19 @@
362362 {var i=0,l=array.length,loop=opts.loop||function(){};$.whileAsync($.extend(opts,{test:function(){return i<l;},loop:function()
363363 {var val=array[i];return loop.call(val,i++,val);}}));}
364364 $.fn.eachAsync=function(opts)
365 -{$.eachAsync(this,opts);return this;}})(jQuery);(function($){var cache={};$.fn.autoEllipsis=function(options){options=$.extend({'position':'center','tooltip':false,'restoreText':false,'hasSpan':false},options);$(this).each(function(){var $this=$(this);if(options.restoreText){if(!$this.data('autoEllipsis.originalText')){$this.data('autoEllipsis.originalText',$this.text());}else{$this.text($this.data('autoEllipsis.originalText'));}}
366 -var text=$this.text();var w=$this.width();var $text;if(options.hasSpan){$text=$this.children('span');}else{$text=$('<span />').css('whiteSpace','nowrap');$this.empty().append($text);}
367 -if(!(text in cache)){cache[text]={};}
368 -if(w in cache[text]){$text.text(cache[text][w]);if(options.tooltip)
369 -$text.attr('title',text);return;}
370 -$text.text(text);if($text.width()>w){switch(options.position){case'right':var l=0,r=text.length;do{var m=Math.ceil((l+r)/2);$text.text(text.substr(0,m)+'...');if($text.width()>w){r=m-1;}else{l=m;}}while(l<r);$text.text(text.substr(0,l)+'...');break;case'center':var i=[Math.round(text.length/2),Math.round(text.length/2)];var side=1;while($text.outerWidth()>w&&i[0]>0){$text.text(text.substr(0,i[0])+'...'+text.substr(i[1]));if(side==0){i[0]--;side=1;}else{i[1]++;side=0;}}
371 -break;case'left':var r=0;while($text.outerWidth()>w&&r<text.length){$text.text('...'+text.substr(r));r++;}
 365+{$.eachAsync(this,opts);return this;}})(jQuery);(function($){var cache={};$.fn.autoEllipsis=function(options){options=$.extend({'position':'center','tooltip':false,'restoreText':false,'hasSpan':false,'matchText':null},options);$(this).each(function(){var $this=$(this);if(options.restoreText){if(!$this.data('autoEllipsis.originalText')){$this.data('autoEllipsis.originalText',$this.text());}else{$this.text($this.data('autoEllipsis.originalText'));}}
 366+var $container=$this;var $trimmableText=null;var $protectedText=null;if(options.matchText){var text=$this.text();var matchedText=options.matchText;$trimmableText=$('<span />').css('whiteSpace','nowrap').addClass('autoellipsis-trimmed').text($this.text().substr(matchedText.length,$this.text().length));$protectedText=$('<span />').addClass('autoellipsis-matched').css('whiteSpace','nowrap').text(options.matchText);$container.empty().append($protectedText).append($trimmableText);}else{if(options.hasSpan){$trimmableText=$this.children(options.selector);}else{$trimmableText=$('<span />').css('whiteSpace','nowrap').text($this.text());$this.empty().append($trimmableText);}}
 367+var text=$container.text();var trimmableText=$trimmableText.text();var w=$container.width();var pw=$protectedText?$protectedText.width():0;if(!(text in cache)){cache[text]={};}
 368+if(options.matchText&&!(options.matchText in cache[text])){cache[text][options.matchText]={};}
 369+if(!options.matchText&&w in cache[text]){$container.html(cache[text][w]);if(options.tooltip)
 370+$container.attr('title',text);return;}
 371+if(options.matchText&&options.matchText in cache[text]&&w in cache[text][options.matchText]){$container.html(cache[text][options.matchText][w]);if(options.tooltip)
 372+$container.attr('title',text);return;}
 373+if($trimmableText.width()+pw>w){switch(options.position){case'right':var l=0,r=trimmableText.length;do{var m=Math.ceil((l+r)/2);$trimmableText.text(trimmableText.substr(0,m)+'...');if($trimmableText.width()+pw>w){r=m-1;}else{l=m;}}while(l<r);$trimmableText.text(trimmableText.substr(0,l)+'...');break;case'center':var i=[Math.round(trimmableText.length/2),Math.round(trimmableText.length/2)];var side=1;while($trimmableText.outerWidth()+pw>w&&i[0]>0){$trimmableText.text(trimmableText.substr(0,i[0])+'...'+trimmableText.substr(i[1]));if(side==0){i[0]--;side=1;}else{i[1]++;side=0;}}
 374+break;case'left':var r=0;while($trimmableText.outerWidth()+pw>w&&r<trimmableText.length){$trimmableText.text('...'+trimmableText.substr(r));r++;}
372375 break;}}
373 -if(options.tooltip)
374 -$text.attr('title',text);cache[text][w]=$text.text();});};})(jQuery);(function($){$.browserTest=function(a,z){var u='unknown',x='X',m=function(r,h){for(var i=0;i<h.length;i=i+1){r=r.replace(h[i][0],h[i][1]);}
 376+if(options.tooltip){$container.attr('title',text);}
 377+if(options.matchText){cache[text][options.matchText][w]=$container.html();}else{cache[text][w]=$container.html();}});};})(jQuery);(function($){$.browserTest=function(a,z){var u='unknown',x='X',m=function(r,h){for(var i=0;i<h.length;i=i+1){r=r.replace(h[i][0],h[i][1]);}
375378 return r;},c=function(i,a,b,c){var r={name:m((a.exec(i)||[u,u])[1],b)};r[r.name]=true;r.version=(c.exec(i)||[x,x,x,x])[3];if(r.name.match(/safari/)&&r.version>400){r.version='2.0';}
376379 if(r.name==='presto'){r.version=($.browser.version>9.27)?'futhark':'linear_b';}
377380 if(r.name==='opera'&&$.browser.version>=9.8){r.version=i.match(/version\/([0-9\.]*)/i)[1]||10;}
@@ -402,19 +405,20 @@
403406 if(delayed){context.data.timerID=setTimeout(maybeFetch,context.config.delay);}else{maybeFetch();}
404407 $.suggestions.special(context);},special:function(context){if(typeof context.config.special.render=='function'){setTimeout(function(){$special=context.data.$container.find('.suggestions-special');context.config.special.render.call($special,context.data.$textbox.val());},1);}},configure:function(context,property,value){switch(property){case'fetch':case'cancel':case'special':case'result':case'$region':context.config[property]=value;break;case'suggestions':context.config[property]=value;if(typeof context.data!=='undefined'){if(context.data.$textbox.val().length==0){context.data.$container.hide();}else{context.data.$container.show();var newCSS={'top':context.config.$region.offset().top+context.config.$region.outerHeight(),'bottom':'auto','width':context.config.$region.outerWidth(),'height':'auto'}
405408 if(context.config.positionFromLeft){newCSS['left']=context.config.$region.offset().left;newCSS['right']='auto';}else{newCSS['left']='auto';newCSS['right']=$('body').width()-(context.config.$region.offset().left+context.config.$region.outerWidth());}
406 -context.data.$container.css(newCSS);var $results=context.data.$container.children('.suggestions-results');$results.empty();var expWidth=-1;var $autoEllipseMe=$([]);for(var i=0;i<context.config.suggestions.length;i++){var $result=$('<div />').addClass('suggestions-result').attr('rel',i).data('text',context.config.suggestions[i]).mouseover(function(e){$.suggestions.highlight(context,$(this).closest('.suggestions-results div'),false);}).appendTo($results);if(typeof context.config.result.render=='function'){context.config.result.render.call($result,context.config.suggestions[i]);}else{$result.append($('<span />').css('whiteSpace','nowrap').text(context.config.suggestions[i]));var $span=$result.children('span');if($span.outerWidth()>$result.width()&&$span.outerWidth()>expWidth){expWidth=$span.outerWidth();}
 409+context.data.$container.css(newCSS);var $results=context.data.$container.children('.suggestions-results');$results.empty();var expWidth=-1;var $autoEllipseMe=$([]);var matchedText=null;for(var i=0;i<context.config.suggestions.length;i++){var text=context.config.suggestions[i];var $result=$('<div />').addClass('suggestions-result').attr('rel',i).data('text',context.config.suggestions[i]).mouseover(function(e){$.suggestions.highlight(context,$(this).closest('.suggestions-results div'),false);}).appendTo($results);if(typeof context.config.result.render=='function'){context.config.result.render.call($result,context.config.suggestions[i]);}else{if(context.config.highlightInput){matchedText=text.substr(0,context.data.prevText.length);}
 410+$result.append($('<span />').css('whiteSpace','nowrap').text(text));var $span=$result.children('span');if($span.outerWidth()>$result.width()&&$span.outerWidth()>expWidth){expWidth=$span.outerWidth();}
407411 $autoEllipseMe=$autoEllipseMe.add($result);}}
408412 if(expWidth>context.data.$container.width()){var maxWidth=context.config.maxExpandFactor*context.data.$textbox.width();context.data.$container.width(Math.min(expWidth,maxWidth));}
409 -$autoEllipseMe.autoEllipsis({hasSpan:true,tooltip:true});}}
410 -break;case'maxRows':context.config[property]=Math.max(1,Math.min(100,value));break;case'delay':context.config[property]=Math.max(0,Math.min(1200,value));break;case'maxExpandFactor':context.config[property]=Math.max(1,value);break;case'submitOnClick':case'positionFromLeft':context.config[property]=value?true:false;break;}},highlight:function(context,result,updateTextbox){var selected=context.data.$container.find('.suggestions-result-current');if(!result.get||selected.get(0)!=result.get(0)){if(result=='prev'){if(selected.is('.suggestions-special')){result=context.data.$container.find('.suggestions-results div:last')}else{result=selected.prev();if(selected.length==0){if(context.data.$container.find('.suggestions-special').html()!=""){result=context.data.$container.find('.suggestions-special');}else{result=context.data.$container.find('.suggestions-results div:last');}}}}else if(result=='next'){if(selected.length==0){result=context.data.$container.find('.suggestions-results div:first');if(result.length==0&&context.data.$container.find('.suggestions-special').html()!=""){result=context.data.$container.find('.suggestions-special');}}else{result=selected.next();if(selected.is('.suggestions-special')){result=$([]);}else if(result.length==0&&context.data.$container.find('.suggestions-special').html()!=""){result=context.data.$container.find('.suggestions-special');}}}
 413+$autoEllipseMe.autoEllipsis({hasSpan:true,tooltip:true,matchText:matchedText});}}
 414+break;case'maxRows':context.config[property]=Math.max(1,Math.min(100,value));break;case'delay':context.config[property]=Math.max(0,Math.min(1200,value));break;case'maxExpandFactor':context.config[property]=Math.max(1,value);break;case'submitOnClick':case'positionFromLeft':case'highlightInput':context.config[property]=value?true:false;break;}},highlight:function(context,result,updateTextbox){var selected=context.data.$container.find('.suggestions-result-current');if(!result.get||selected.get(0)!=result.get(0)){if(result=='prev'){if(selected.is('.suggestions-special')){result=context.data.$container.find('.suggestions-result:last')}else{result=selected.prev();if(selected.length==0){if(context.data.$container.find('.suggestions-special').html()!=""){result=context.data.$container.find('.suggestions-special');}else{result=context.data.$container.find('.suggestions-results div:last');}}}}else if(result=='next'){if(selected.length==0){result=context.data.$container.find('.suggestions-results div:first');if(result.length==0&&context.data.$container.find('.suggestions-special').html()!=""){result=context.data.$container.find('.suggestions-special');}}else{result=selected.next();if(selected.is('.suggestions-special')){result=$([]);}else if(result.length==0&&context.data.$container.find('.suggestions-special').html()!=""){result=context.data.$container.find('.suggestions-special');}}}
411415 selected.removeClass('suggestions-result-current');result.addClass('suggestions-result-current');}
412416 if(updateTextbox){if(result.length==0){$.suggestions.restore(context);}else{context.data.$textbox.val(result.data('text'));context.data.$textbox.change();}
413417 context.data.$textbox.trigger('change');}
414 -$.suggestions.special(context);},keypress:function(e,context,key){var wasVisible=context.data.$container.is(':visible');var preventDefault=false;switch(key){case 40:if(wasVisible){$.suggestions.highlight(context,'next',false);}else{$.suggestions.update(context,false);}
415 -preventDefault=true;break;case 38:if(wasVisible){$.suggestions.highlight(context,'prev',false);}
 418+$.suggestions.special(context);},keypress:function(e,context,key){var wasVisible=context.data.$container.is(':visible');var preventDefault=false;switch(key){case 40:if(wasVisible){$.suggestions.highlight(context,'next',true);}else{$.suggestions.update(context,false);}
 419+preventDefault=true;break;case 38:if(wasVisible){$.suggestions.highlight(context,'prev',true);}
416420 preventDefault=wasVisible;break;case 27:context.data.$container.hide();$.suggestions.restore(context);$.suggestions.cancel(context);context.data.$textbox.trigger('change');preventDefault=wasVisible;break;case 13:context.data.$container.hide();preventDefault=wasVisible;selected=context.data.$container.find('.suggestions-result-current');if(selected.size()==0){$.suggestions.cancel(context);context.config.$region.closest('form').submit();}else if(selected.is('.suggestions-special')){if(typeof context.config.special.select=='function'){context.config.special.select.call(selected,context.data.$textbox);}}else{if(typeof context.config.result.select=='function'){$.suggestions.highlight(context,selected,true);context.config.result.select.call(selected,context.data.$textbox);}else{$.suggestions.highlight(context,selected,true);}}
417421 break;default:$.suggestions.update(context,true);break;}
418 -if(preventDefault){e.preventDefault();e.stopImmediatePropagation();}}};$.fn.suggestions=function(){var returnValue=null;var args=arguments;$(this).each(function(){var context=$(this).data('suggestions-context');if(typeof context=='undefined'||context==null){context={config:{'fetch':function(){},'cancel':function(){},'special':{},'result':{},'$region':$(this),'suggestions':[],'maxRows':7,'delay':120,'submitOnClick':false,'maxExpandFactor':3,'positionFromLeft':true}};}
 422+if(preventDefault){e.preventDefault();e.stopImmediatePropagation();}}};$.fn.suggestions=function(){var returnValue=null;var args=arguments;$(this).each(function(){var context=$(this).data('suggestions-context');if(typeof context=='undefined'||context==null){context={config:{'fetch':function(){},'cancel':function(){},'special':{},'result':{},'$region':$(this),'suggestions':[],'maxRows':7,'delay':120,'submitOnClick':false,'maxExpandFactor':3,'positionFromLeft':true,'highlightInput':false}};}
419423 if(args.length>0){if(typeof args[0]=='object'){for(var key in args[0]){$.suggestions.configure(context,key,args[0][key]);}}else if(typeof args[0]=='string'){if(args.length>1){$.suggestions.configure(context,args[0],args[1]);}else if(returnValue==null){returnValue=(args[0]in context.config?undefined:context.config[args[0]]);}}}
420424 if(typeof context.data=='undefined'){context.data={'timerID':null,'prevText':null,'visibleResults':0,'mouseDownOn':$([]),'$textbox':$(this)};var newCSS={'top':Math.round(context.data.$textbox.offset().top+context.data.$textbox.outerHeight()),'width':context.data.$textbox.outerWidth(),'display':'none'}
421425 if(context.config.positionFromLeft){newCSS['left']=context.config.$region.offset().left;newCSS['right']='auto';}else{newCSS['left']='auto';newCSS['right']=$('body').width()-(context.config.$region.offset().left+context.config.$region.outerWidth());}

Comments

#Comment by Catrope (talk | contribs)   12:19, 21 June 2010

I may be a bit paranoid here, but I think there's a potential issue with this code when it's called with matchText: '123' first, then with matchText: null but with the same text in $container and with $container.width() being 123 pixels. This code:

+		if ( !options.matchText && w in cache[text] ) {
+			$container.html( cache[text][w] );

will then feed an array to $container.html() because arr['123'] and arr[123] are the same thing in JavaScript; I have no idea how html() responds to being passed an array, but it's probably not pretty (I guess it'll be something along the lines of [Object object]).

The solution would be not to share the caching namespace the way you're doing but to use separate caches for the cases with and without matchText.

#Comment by Adammiller~mediawikiwiki (talk | contribs)   14:46, 21 June 2010

Implemented this fix in r67755.

#Comment by Catrope (talk | contribs)   14:48, 21 June 2010

Should read r68359.

Status & tagging log