r61548 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r61547‎ | r61548 | r61549 >
Date:01:12, 27 January 2010
Author:catrope
Status:deferred
Tags:
Comment:
UsabilityInitiative: Performance fixes for TOC building
* Don't parse {{ }} when the templateEditor module is not enabled
* Refactored offsets generation system to not generate offset objects for every single character position (not very time- or memory-efficient if there's 220K of them) but instead only store offset objects for the beginning of every textnode and generate others on the fly in getOffset()
* Use a plain DOM solution instead of $(p).hasClass('wikiEditor-noinclude'), the latter is slow when run thousands of times
* Cache ellipsed strings in autoEllipsis plugins. Also cache $(this) and $(this).width()
* Recombine for r61539
Modified paths:
  • /trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/WikiEditor/WikiEditor.combined.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/WikiEditor/WikiEditor.combined.min.js (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.wikiEditor.highlight.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php
@@ -105,7 +105,7 @@
106106 array(
107107 'src' => 'js/plugins/jquery.autoEllipsis.js',
108108 'class' => 'j.fn.autoEllipsis',
109 - 'version' => 6
 109+ 'version' => 7
110110 ),
111111 array(
112112 'src' => 'js/plugins/jquery.browser.js',
@@ -135,12 +135,12 @@
136136 array(
137137 'src' => 'js/plugins/jquery.wikiEditor.js',
138138 'class' => 'j.wikiEditor',
139 - 'version' => 69
 139+ 'version' => 70
140140 ),
141141 array(
142142 'src' => 'js/plugins/jquery.wikiEditor.highlight.js',
143143 'class' => 'j.wikiEditor.modules.highlight',
144 - 'version' => 22
 144+ 'version' => 23
145145 ),
146146 array(
147147 'src' => 'js/plugins/jquery.wikiEditor.toolbar.js',
@@ -173,10 +173,10 @@
174174 'version' => 1 ),
175175 ),
176176 'combined' => array(
177 - array( 'src' => 'js/plugins.combined.js', 'version' => 180 ),
 177+ array( 'src' => 'js/plugins.combined.js', 'version' => 181 ),
178178 ),
179179 'minified' => array(
180 - array( 'src' => 'js/plugins.combined.min.js', 'version' => 180 ),
 180+ array( 'src' => 'js/plugins.combined.min.js', 'version' => 181 ),
181181 ),
182182 ),
183183 );
Index: trunk/extensions/UsabilityInitiative/WikiEditor/WikiEditor.combined.js
@@ -1773,19 +1773,24 @@
17741774 <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\
17751775 <div id="wikieditor-toolbar-replace-invalidregex"></div>\
17761776 </div>\
1777 - <fieldset><table width="100%"><tr>\
1778 - <td width="25%"><label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label></td>\
1779 - <td width="75%"><input type="text" id="wikieditor-toolbar-replace-search" style="width: 90%;" /></td>\
1780 - </tr><tr>\
1781 - <td><label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label></td>\
1782 - <td><input type="text" id="wikieditor-toolbar-replace-replace" style="width: 90%;" /></td>\
1783 - </tr></table><table><tr>\
1784 - <td><input type="checkbox" id="wikieditor-toolbar-replace-case" /></td>\
1785 - <td><label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label></td>\
1786 - </tr><tr>\
1787 - <td><input type="checkbox" id="wikieditor-toolbar-replace-regex" /></td>\
1788 - <td><label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label></td>\
1789 - </tr></table></fieldset>',
 1777+ <fieldset>\
 1778+ <div class="wikieditor-toolbar-field-wrapper">\
 1779+ <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\
 1780+ <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;" />\
 1781+ </div>\
 1782+ <div class="wikieditor-toolbar-field-wrapper">\
 1783+ <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\
 1784+ <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;" />\
 1785+ </div>\
 1786+ <div class="wikieditor-toolbar-field-wrapper">\
 1787+ <input type="checkbox" id="wikieditor-toolbar-replace-case" />\
 1788+ <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\
 1789+ </div>\
 1790+ <div class="wikieditor-toolbar-field-wrapper">\
 1791+ <input type="checkbox" id="wikieditor-toolbar-replace-regex" />\
 1792+ <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\
 1793+ </div>\
 1794+ </fieldset>',
17901795 init: function() {
17911796 $j(this).find( '[rel]' ).each( function() {
17921797 $j(this).text( gM( $j(this).attr( 'rel' ) ) );
Index: trunk/extensions/UsabilityInitiative/WikiEditor/WikiEditor.combined.min.js
@@ -147,19 +147,24 @@
148148 <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\
149149 <div id="wikieditor-toolbar-replace-invalidregex"></div>\
150150 </div>\
151 - <fieldset><table width="100%"><tr>\
152 - <td width="25%"><label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label></td>\
153 - <td width="75%"><input type="text" id="wikieditor-toolbar-replace-search" style="width: 90%;" /></td>\
154 - </tr><tr>\
155 - <td><label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label></td>\
156 - <td><input type="text" id="wikieditor-toolbar-replace-replace" style="width: 90%;" /></td>\
157 - </tr></table><table><tr>\
158 - <td><input type="checkbox" id="wikieditor-toolbar-replace-case" /></td>\
159 - <td><label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label></td>\
160 - </tr><tr>\
161 - <td><input type="checkbox" id="wikieditor-toolbar-replace-regex" /></td>\
162 - <td><label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label></td>\
163 - </tr></table></fieldset>',init:function(){$j(this).find('[rel]').each(function(){$j(this).text(gM($j(this).attr('rel')));});$j(this).data('replaceCallback',function(mode){$j('#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex').hide();var searchStr=$j('#wikieditor-toolbar-replace-search').val();if(searchStr==''){$j('#wikieditor-toolbar-replace-emptysearch').show();return;}
 151+ <fieldset>\
 152+ <div class="wikieditor-toolbar-field-wrapper">\
 153+ <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\
 154+ <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;" />\
 155+ </div>\
 156+ <div class="wikieditor-toolbar-field-wrapper">\
 157+ <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\
 158+ <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;" />\
 159+ </div>\
 160+ <div class="wikieditor-toolbar-field-wrapper">\
 161+ <input type="checkbox" id="wikieditor-toolbar-replace-case" />\
 162+ <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\
 163+ </div>\
 164+ <div class="wikieditor-toolbar-field-wrapper">\
 165+ <input type="checkbox" id="wikieditor-toolbar-replace-regex" />\
 166+ <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\
 167+ </div>\
 168+ </fieldset>',init:function(){$j(this).find('[rel]').each(function(){$j(this).text(gM($j(this).attr('rel')));});$j(this).data('replaceCallback',function(mode){$j('#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex').hide();var searchStr=$j('#wikieditor-toolbar-replace-search').val();if(searchStr==''){$j('#wikieditor-toolbar-replace-emptysearch').show();return;}
164169 var replaceStr=$j('#wikieditor-toolbar-replace-replace').val();var flags='m';var matchCase=$j('#wikieditor-toolbar-replace-case').is(':checked');var isRegex=$j('#wikieditor-toolbar-replace-regex').is(':checked');if(!matchCase){flags+='i';}
165170 if(mode=='replaceAll'){flags+='g';}
166171 if(!isRegex){searchStr=RegExp.escape(searchStr);}
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.js
@@ -554,13 +554,12 @@
555555 // Firefox and Opera
556556 var start = options.start, end = options.end;
557557 if ( !sc || !ec ) {
558 - var offsets = context.fn.getOffsets();
559 - var startContainer = offsets[start].node, startOffset = offsets[start].offset;
560 - var endContainer = offsets[end].node, endOffset = offsets[end].offset;
561 - sc = offsets[start].node;
562 - ec = offsets[end].node;
563 - start = offsets[start].offset;
564 - end = offsets[end].offset;
 558+ var s = context.fn.getOffset( start );
 559+ var e = context.fn.getOffset( end );
 560+ sc = s.node;
 561+ ec = e.node;
 562+ start = s.offset;
 563+ end = e.offset;
565564 }
566565 if ( !sc || !ec ) {
567566 // The requested offset isn't in the offsets array
@@ -673,11 +672,12 @@
674673 return $( [] );
675674 }
676675 var seekPos = context.fn.htmlToText( range2.htmlText ).length;
677 - var offsets = context.fn.getOffsets();
678 - e = offsets[seekPos] ? offsets[seekPos].node : null;
679 - offset = offsets[seekPos] ? offsets[seekPos].offset : null;
680 - if ( !e )
 676+ var offset = context.fn.getOffset( seekPos );
 677+ e = offset ? offset.node : null;
 678+ offset = offset ? offset.offset : null;
 679+ if ( !e ) {
681680 return $( [] );
 681+ }
682682 }
683683 if ( e.nodeName != '#text' ) {
684684 // The selection is not in a textnode, but between two non-text nodes
@@ -738,7 +738,9 @@
739739 p = p ? p.nextSibling : null;
740740 do {
741741 // Filter nodes with the wikiEditor-noinclude class
742 - while ( p && $( p ).hasClass( 'wikiEditor-noinclude' ) ) {
 742+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 743+ // $() is slow in a tight loop
 744+ while ( p && ( ' ' + p.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
743745 p = p.nextSibling;
744746 }
745747 if ( p && p.firstChild ) {
@@ -758,7 +760,9 @@
759761 var inP = node.nodeName == "P";
760762 do {
761763 // Filter nodes with the wikiEditor-noinclude class
762 - while ( node && $( node ).hasClass( 'wikiEditor-noinclude' ) ) {
 764+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 765+ // $() is slow in a tight loop
 766+ while ( node && ( ' ' + node.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
763767 node = node.nextSibling;
764768 }
765769 if ( node && node.firstChild ) {
@@ -771,11 +775,30 @@
772776 } while ( node && node.firstChild );
773777 return new Traverser( node, depth, inP );
774778 },
775 - 'getOffsets': function() {
 779+ 'getOffset': function( offset ) {
776780 if ( !context.offsets ) {
777781 context.fn.refreshOffsets();
778782 }
779 - return context.offsets;
 783+ if ( offset in context.offsets ) {
 784+ return context.offsets[offset];
 785+ }
 786+ // Our offset is not pre-cached. Find the highest offset below it and interpolate
 787+ var lowerBound = 0;
 788+ for ( var o in context.offsets ) {
 789+ if ( o > offset ) {
 790+ break;
 791+ }
 792+ lowerBound = o;
 793+ }
 794+ var base = context.offsets[lowerBound];
 795+ return context.offsets[offset] = {
 796+ 'node': base.node,
 797+ 'offset': base.offset + offset - o,
 798+ 'length': base.length,
 799+ 'depth': base.depth,
 800+ 'lastTextNode': lastTextNode,
 801+ 'lastTextNodeDepth': lastTextNodeDepth
 802+ };
780803 },
781804 'purgeOffsets': function() {
782805 context.offsets = null;
@@ -792,16 +815,14 @@
793816 var nextPos = t.node.nodeName == '#text' ? pos + t.node.nodeValue.length : pos + 1;
794817 var nextT = t.next();
795818 var leavingP = t.inP && nextT && !nextT.inP;
796 - for ( var p = pos; p < nextPos; p++ ) {
797 - context.offsets[p] = {
798 - 'node': t.node,
799 - 'offset': p - pos,
800 - 'length': nextPos - pos + ( leavingP ? 1 : 0 ),
801 - 'depth': t.depth,
802 - 'lastTextNode': lastTextNode,
803 - 'lastTextNodeDepth': lastTextNodeDepth
804 - };
805 - }
 819+ context.offsets[pos] = {
 820+ 'node': t.node,
 821+ 'offset': 0,
 822+ 'length': nextPos - pos + ( leavingP ? 1 : 0 ),
 823+ 'depth': t.depth,
 824+ 'lastTextNode': lastTextNode,
 825+ 'lastTextNodeDepth': lastTextNodeDepth
 826+ };
806827 if ( leavingP ) {
807828 // <p>Foo</p> looks like "Foo\n", make it quack like it too
808829 // Basically we're faking the \n character much like we're treating <br>s
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.autoEllipsis.js
@@ -3,24 +3,40 @@
44 */
55 ( function( $ ) {
66
 7+// Cache ellipsed substrings for every string-width combination
 8+var cache = { };
 9+
710 $.fn.autoEllipsis = function( options ) {
 11+ options = $.extend( {
 12+ 'position': 'center',
 13+ 'tooltip': false,
 14+ 'restoreText': false
 15+ }, options );
816 $(this).each( function() {
9 - options = $.extend( {
10 - 'position': 'center',
11 - 'tooltip': false,
12 - 'restoreText': false
13 - }, options );
 17+ var $this = $(this);
1418 if ( options.restoreText ) {
15 - if ( ! $( this ).data( 'autoEllipsis.originalText' ) ) {
16 - $( this ).data( 'autoEllipsis.originalText', $( this ).text() );
 19+ if ( ! $this.data( 'autoEllipsis.originalText' ) ) {
 20+ $this.data( 'autoEllipsis.originalText', $this.text() );
1721 } else {
18 - $( this ).text( $( this ).data( 'autoEllipsis.originalText' ) );
 22+ $this.text( $this.data( 'autoEllipsis.originalText' ) );
1923 }
2024 }
21 - var text = $(this).text();
22 - var $text = $( '<span />' ).text( text ).css( 'whiteSpace', 'nowrap' );
23 - $(this).empty().append( $text );
24 - if ( $text.width() > $(this).width() ) {
 25+ var text = $this.text();
 26+ var w = $this.width();
 27+ var $text = $( '<span />' ).css( 'whiteSpace', 'nowrap' );
 28+ $this.empty().append( $text );
 29+
 30+ // Try cache
 31+ if ( !( text in cache ) ) {
 32+ cache[text] = {};
 33+ }
 34+ if ( w in cache[text] ) {
 35+ $text.text( cache[text][w] );
 36+ return;
 37+ }
 38+
 39+ $text.text( text );
 40+ if ( $text.width() > w ) {
2541 switch ( options.position ) {
2642 case 'right':
2743 // Use binary search-like technique for efficiency
@@ -28,7 +44,7 @@
2945 do {
3046 var m = Math.ceil( ( l + r ) / 2 );
3147 $text.text( text.substr( 0, m ) + '...' );
32 - if ( $text.width() > $(this).width() ) {
 48+ if ( $text.width() > w ) {
3349 // Text is too long
3450 r = m - 1;
3551 } else {
@@ -41,7 +57,7 @@
4258 // TODO: Use binary search like for 'right'
4359 var i = [Math.round( text.length / 2 ), Math.round( text.length / 2 )];
4460 var side = 1; // Begin with making the end shorter
45 - while ( $text.outerWidth() > ( $(this).width() ) && i[0] > 0 ) {
 61+ while ( $text.outerWidth() > w && i[0] > 0 ) {
4662 $text.text( text.substr( 0, i[0] ) + '...' + text.substr( i[1] ) );
4763 // Alternate between trimming the end and begining
4864 if ( side == 0 ) {
@@ -58,7 +74,7 @@
5975 case 'left':
6076 // TODO: Use binary search like for 'right'
6177 var r = 0;
62 - while ( $text.outerWidth() > $(this).width() && r < text.length ) {
 78+ while ( $text.outerWidth() > w && r < text.length ) {
6379 $text.text( '...' + text.substr( r ) );
6480 r++;
6581 }
@@ -67,6 +83,7 @@
6884 if ( options.tooltip )
6985 $text.attr( 'title', text );
7086 }
 87+ cache[text][w] = $text.text();
7188 } );
7289 };
7390
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.highlight.js
@@ -76,6 +76,7 @@
7777 },
7878 /**
7979 * Strips division of HTML
 80+ * FIXME: Isn't this done by context.fn.htmlToText() already?
8081 *
8182 * @param division
8283 */
@@ -104,8 +105,8 @@
105106 var tokenArray = context.modules.highlight.tokenArray = [];
106107 // We need to look over some text and find interesting areas, then return the positions of those areas as tokens
107108 var text = context.fn.getContents();
108 - for ( module in $.wikiEditor.modules ) {
109 - if ( 'exp' in $.wikiEditor.modules[module] ) {
 109+ for ( module in context.modules ) {
 110+ if ( module in $.wikiEditor.modules && 'exp' in $.wikiEditor.modules[module] ) {
110111 for ( var i = 0; i < $.wikiEditor.modules[module].exp.length; i++ ) {
111112 var regex = $.wikiEditor.modules[module].exp[i].regex;
112113 var label = $.wikiEditor.modules[module].exp[i].label;
@@ -151,55 +152,48 @@
152153 markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
153154
154155 // Traverse the iframe DOM, inserting markers where they're needed.
155 - var offsets = context.fn.getOffsets();
156156 for ( var i = 0; i < markers.length; i++ ) {
157157 // We want to isolate each marker, so we may need to split textNodes
158158 // if a marker starts or ends halfway one.
159159 var start = markers[i].start;
160 - if ( !( start in offsets ) ) {
161 - // This shouldn't happen
162 - continue;
163 - }
164 - var startNode = offsets[start].node;
165 - var startDepth = offsets[start].depth;
 160+ var s = context.fn.getOffset( start );
 161+ var startNode = s.node;
 162+ var startDepth = s.depth;
166163 // The next marker starts somewhere in this textNode or at this BR
167 - if ( offsets[start].offset > 0 ) {
 164+ if ( s.offset > 0 ) {
168165 // t.node must be a textnode at this point because
169166 // only textnodes can have offset > 0
170167
171168 // Split off the prefix
172169 // This leaves the prefix in the current node and puts
173170 // the rest in a new node which is our start node
174 - startNode = startNode.splitText( offsets[start].offset );
 171+ startNode = startNode.splitText( s.offset );
175172 }
176173 // Don't wrap leading BRs, produces undesirable results
177174 while ( startNode.nodeName == 'BR' && start + 1 in offsets ) {
178175 start++;
179 - startNode = offsets[start].node;
180 - startDepth = offsets[start].depth;
 176+ startNode = s.node;
 177+ startDepth = s.depth;
181178 }
182179
183180 var end = markers[i].end;
184 - if ( !( end in offsets ) ) {
185 - // This shouldn't happen
186 - continue;
187 - }
188 - var endNode = offsets[end].node;
189 - var endDepth = offsets[end].depth;
190 - if ( offsets[end].offset < offsets[end].length - 1 ) {
 181+ var e = context.fn.getOffset( end );
 182+ var endNode = e.node;
 183+ var endDepth = e.depth;
 184+ if ( e.offset < e.length - 1 ) {
191185 // t.node must be a textnode at this point because
192186 // .length is 1 for BRs and offset can't be < 0
193187
194188 // Split off the suffix - This puts the suffix in a new node and leaves the rest in the current
195189 // node.
196190 // endNode.nodeValue.length - ( newPos - markers[i].end )
197 - endNode.splitText( offsets[end].offset + 1 );
 191+ endNode.splitText( e.offset + 1 );
198192 }
199193
200194 // Don't wrap trailing BRs, doing that causes weird issues
201195 if ( endNode.nodeName == 'BR' ) {
202 - endNode = offsets[end].lastTextNode;
203 - endDepth = offsets[end].lastTextNodeDepth;
 196+ endNode = e.lastTextNode;
 197+ endDepth = e.lastTextNodeDepth;
204198 }
205199
206200 // Now wrap everything between startNode and endNode (may be equal). First find the common ancestor of
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.js
@@ -5130,24 +5130,40 @@
51315131 */
51325132 ( function( $ ) {
51335133
 5134+// Cache ellipsed substrings for every string-width combination
 5135+var cache = { };
 5136+
51345137 $.fn.autoEllipsis = function( options ) {
 5138+ options = $.extend( {
 5139+ 'position': 'center',
 5140+ 'tooltip': false,
 5141+ 'restoreText': false
 5142+ }, options );
51355143 $(this).each( function() {
5136 - options = $.extend( {
5137 - 'position': 'center',
5138 - 'tooltip': false,
5139 - 'restoreText': false
5140 - }, options );
 5144+ var $this = $(this);
51415145 if ( options.restoreText ) {
5142 - if ( ! $( this ).data( 'autoEllipsis.originalText' ) ) {
5143 - $( this ).data( 'autoEllipsis.originalText', $( this ).text() );
 5146+ if ( ! $this.data( 'autoEllipsis.originalText' ) ) {
 5147+ $this.data( 'autoEllipsis.originalText', $this.text() );
51445148 } else {
5145 - $( this ).text( $( this ).data( 'autoEllipsis.originalText' ) );
 5149+ $this.text( $this.data( 'autoEllipsis.originalText' ) );
51465150 }
51475151 }
5148 - var text = $(this).text();
5149 - var $text = $( '<span />' ).text( text ).css( 'whiteSpace', 'nowrap' );
5150 - $(this).empty().append( $text );
5151 - if ( $text.width() > $(this).width() ) {
 5152+ var text = $this.text();
 5153+ var w = $this.width();
 5154+ var $text = $( '<span />' ).css( 'whiteSpace', 'nowrap' );
 5155+ $this.empty().append( $text );
 5156+
 5157+ // Try cache
 5158+ if ( !( text in cache ) ) {
 5159+ cache[text] = {};
 5160+ }
 5161+ if ( w in cache[text] ) {
 5162+ $text.text( cache[text][w] );
 5163+ return;
 5164+ }
 5165+
 5166+ $text.text( text );
 5167+ if ( $text.width() > w ) {
51525168 switch ( options.position ) {
51535169 case 'right':
51545170 // Use binary search-like technique for efficiency
@@ -5155,7 +5171,7 @@
51565172 do {
51575173 var m = Math.ceil( ( l + r ) / 2 );
51585174 $text.text( text.substr( 0, m ) + '...' );
5159 - if ( $text.width() > $(this).width() ) {
 5175+ if ( $text.width() > w ) {
51605176 // Text is too long
51615177 r = m - 1;
51625178 } else {
@@ -5168,7 +5184,7 @@
51695185 // TODO: Use binary search like for 'right'
51705186 var i = [Math.round( text.length / 2 ), Math.round( text.length / 2 )];
51715187 var side = 1; // Begin with making the end shorter
5172 - while ( $text.outerWidth() > ( $(this).width() ) && i[0] > 0 ) {
 5188+ while ( $text.outerWidth() > w && i[0] > 0 ) {
51735189 $text.text( text.substr( 0, i[0] ) + '...' + text.substr( i[1] ) );
51745190 // Alternate between trimming the end and begining
51755191 if ( side == 0 ) {
@@ -5185,7 +5201,7 @@
51865202 case 'left':
51875203 // TODO: Use binary search like for 'right'
51885204 var r = 0;
5189 - while ( $text.outerWidth() > $(this).width() && r < text.length ) {
 5205+ while ( $text.outerWidth() > w && r < text.length ) {
51905206 $text.text( '...' + text.substr( r ) );
51915207 r++;
51925208 }
@@ -5194,6 +5210,7 @@
51955211 if ( options.tooltip )
51965212 $text.attr( 'title', text );
51975213 }
 5214+ cache[text][w] = $text.text();
51985215 } );
51995216 };
52005217
@@ -6916,13 +6933,12 @@
69176934 // Firefox and Opera
69186935 var start = options.start, end = options.end;
69196936 if ( !sc || !ec ) {
6920 - var offsets = context.fn.getOffsets();
6921 - var startContainer = offsets[start].node, startOffset = offsets[start].offset;
6922 - var endContainer = offsets[end].node, endOffset = offsets[end].offset;
6923 - sc = offsets[start].node;
6924 - ec = offsets[end].node;
6925 - start = offsets[start].offset;
6926 - end = offsets[end].offset;
 6937+ var s = context.fn.getOffset( start );
 6938+ var e = context.fn.getOffset( end );
 6939+ sc = s.node;
 6940+ ec = e.node;
 6941+ start = s.offset;
 6942+ end = e.offset;
69276943 }
69286944 if ( !sc || !ec ) {
69296945 // The requested offset isn't in the offsets array
@@ -7035,11 +7051,12 @@
70367052 return $( [] );
70377053 }
70387054 var seekPos = context.fn.htmlToText( range2.htmlText ).length;
7039 - var offsets = context.fn.getOffsets();
7040 - e = offsets[seekPos] ? offsets[seekPos].node : null;
7041 - offset = offsets[seekPos] ? offsets[seekPos].offset : null;
7042 - if ( !e )
 7055+ var offset = context.fn.getOffset( seekPos );
 7056+ e = offset ? offset.node : null;
 7057+ offset = offset ? offset.offset : null;
 7058+ if ( !e ) {
70437059 return $( [] );
 7060+ }
70447061 }
70457062 if ( e.nodeName != '#text' ) {
70467063 // The selection is not in a textnode, but between two non-text nodes
@@ -7100,7 +7117,9 @@
71017118 p = p ? p.nextSibling : null;
71027119 do {
71037120 // Filter nodes with the wikiEditor-noinclude class
7104 - while ( p && $( p ).hasClass( 'wikiEditor-noinclude' ) ) {
 7121+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 7122+ // $() is slow in a tight loop
 7123+ while ( p && ( ' ' + p.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
71057124 p = p.nextSibling;
71067125 }
71077126 if ( p && p.firstChild ) {
@@ -7120,7 +7139,9 @@
71217140 var inP = node.nodeName == "P";
71227141 do {
71237142 // Filter nodes with the wikiEditor-noinclude class
7124 - while ( node && $( node ).hasClass( 'wikiEditor-noinclude' ) ) {
 7143+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
 7144+ // $() is slow in a tight loop
 7145+ while ( node && ( ' ' + node.className + ' ' ).indexOf( ' wikiEditor-noinclude ' ) != -1 ) {
71257146 node = node.nextSibling;
71267147 }
71277148 if ( node && node.firstChild ) {
@@ -7133,11 +7154,30 @@
71347155 } while ( node && node.firstChild );
71357156 return new Traverser( node, depth, inP );
71367157 },
7137 - 'getOffsets': function() {
 7158+ 'getOffset': function( offset ) {
71387159 if ( !context.offsets ) {
71397160 context.fn.refreshOffsets();
71407161 }
7141 - return context.offsets;
 7162+ if ( offset in context.offsets ) {
 7163+ return context.offsets[offset];
 7164+ }
 7165+ // Our offset is not pre-cached. Find the highest offset below it and interpolate
 7166+ var lowerBound = 0;
 7167+ for ( var o in context.offsets ) {
 7168+ if ( o > offset ) {
 7169+ break;
 7170+ }
 7171+ lowerBound = o;
 7172+ }
 7173+ var base = context.offsets[lowerBound];
 7174+ return context.offsets[offset] = {
 7175+ 'node': base.node,
 7176+ 'offset': base.offset + offset - o,
 7177+ 'length': base.length,
 7178+ 'depth': base.depth,
 7179+ 'lastTextNode': lastTextNode,
 7180+ 'lastTextNodeDepth': lastTextNodeDepth
 7181+ };
71427182 },
71437183 'purgeOffsets': function() {
71447184 context.offsets = null;
@@ -7154,16 +7194,14 @@
71557195 var nextPos = t.node.nodeName == '#text' ? pos + t.node.nodeValue.length : pos + 1;
71567196 var nextT = t.next();
71577197 var leavingP = t.inP && nextT && !nextT.inP;
7158 - for ( var p = pos; p < nextPos; p++ ) {
7159 - context.offsets[p] = {
7160 - 'node': t.node,
7161 - 'offset': p - pos,
7162 - 'length': nextPos - pos + ( leavingP ? 1 : 0 ),
7163 - 'depth': t.depth,
7164 - 'lastTextNode': lastTextNode,
7165 - 'lastTextNodeDepth': lastTextNodeDepth
7166 - };
7167 - }
 7198+ context.offsets[pos] = {
 7199+ 'node': t.node,
 7200+ 'offset': 0,
 7201+ 'length': nextPos - pos + ( leavingP ? 1 : 0 ),
 7202+ 'depth': t.depth,
 7203+ 'lastTextNode': lastTextNode,
 7204+ 'lastTextNodeDepth': lastTextNodeDepth
 7205+ };
71687206 if ( leavingP ) {
71697207 // <p>Foo</p> looks like "Foo\n", make it quack like it too
71707208 // Basically we're faking the \n character much like we're treating <br>s
@@ -7554,6 +7592,7 @@
75557593 },
75567594 /**
75577595 * Strips division of HTML
 7596+ * FIXME: Isn't this done by context.fn.htmlToText() already?
75587597 *
75597598 * @param division
75607599 */
@@ -7582,8 +7621,8 @@
75837622 var tokenArray = context.modules.highlight.tokenArray = [];
75847623 // We need to look over some text and find interesting areas, then return the positions of those areas as tokens
75857624 var text = context.fn.getContents();
7586 - for ( module in $.wikiEditor.modules ) {
7587 - if ( 'exp' in $.wikiEditor.modules[module] ) {
 7625+ for ( module in context.modules ) {
 7626+ if ( module in $.wikiEditor.modules && 'exp' in $.wikiEditor.modules[module] ) {
75887627 for ( var i = 0; i < $.wikiEditor.modules[module].exp.length; i++ ) {
75897628 var regex = $.wikiEditor.modules[module].exp[i].regex;
75907629 var label = $.wikiEditor.modules[module].exp[i].label;
@@ -7629,55 +7668,48 @@
76307669 markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
76317670
76327671 // Traverse the iframe DOM, inserting markers where they're needed.
7633 - var offsets = context.fn.getOffsets();
76347672 for ( var i = 0; i < markers.length; i++ ) {
76357673 // We want to isolate each marker, so we may need to split textNodes
76367674 // if a marker starts or ends halfway one.
76377675 var start = markers[i].start;
7638 - if ( !( start in offsets ) ) {
7639 - // This shouldn't happen
7640 - continue;
7641 - }
7642 - var startNode = offsets[start].node;
7643 - var startDepth = offsets[start].depth;
 7676+ var s = context.fn.getOffset( start );
 7677+ var startNode = s.node;
 7678+ var startDepth = s.depth;
76447679 // The next marker starts somewhere in this textNode or at this BR
7645 - if ( offsets[start].offset > 0 ) {
 7680+ if ( s.offset > 0 ) {
76467681 // t.node must be a textnode at this point because
76477682 // only textnodes can have offset > 0
76487683
76497684 // Split off the prefix
76507685 // This leaves the prefix in the current node and puts
76517686 // the rest in a new node which is our start node
7652 - startNode = startNode.splitText( offsets[start].offset );
 7687+ startNode = startNode.splitText( s.offset );
76537688 }
76547689 // Don't wrap leading BRs, produces undesirable results
76557690 while ( startNode.nodeName == 'BR' && start + 1 in offsets ) {
76567691 start++;
7657 - startNode = offsets[start].node;
7658 - startDepth = offsets[start].depth;
 7692+ startNode = s.node;
 7693+ startDepth = s.depth;
76597694 }
76607695
76617696 var end = markers[i].end;
7662 - if ( !( end in offsets ) ) {
7663 - // This shouldn't happen
7664 - continue;
7665 - }
7666 - var endNode = offsets[end].node;
7667 - var endDepth = offsets[end].depth;
7668 - if ( offsets[end].offset < offsets[end].length - 1 ) {
 7697+ var e = context.fn.getOffset( end );
 7698+ var endNode = e.node;
 7699+ var endDepth = e.depth;
 7700+ if ( e.offset < e.length - 1 ) {
76697701 // t.node must be a textnode at this point because
76707702 // .length is 1 for BRs and offset can't be < 0
76717703
76727704 // Split off the suffix - This puts the suffix in a new node and leaves the rest in the current
76737705 // node.
76747706 // endNode.nodeValue.length - ( newPos - markers[i].end )
7675 - endNode.splitText( offsets[end].offset + 1 );
 7707+ endNode.splitText( e.offset + 1 );
76767708 }
76777709
76787710 // Don't wrap trailing BRs, doing that causes weird issues
76797711 if ( endNode.nodeName == 'BR' ) {
7680 - endNode = offsets[end].lastTextNode;
7681 - endDepth = offsets[end].lastTextNodeDepth;
 7712+ endNode = e.lastTextNode;
 7713+ endDepth = e.lastTextNodeDepth;
76827714 }
76837715
76847716 // Now wrap everything between startNode and endNode (may be equal). First find the common ancestor of
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js
@@ -354,12 +354,15 @@
355355 {var i=0,l=array.length,loop=opts.loop||function(){};$.whileAsync($.extend(opts,{test:function(){return i<l;},loop:function()
356356 {var val=array[i];return loop.call(val,i++,val);}}));}
357357 $.fn.eachAsync=function(opts)
358 -{$.eachAsync(this,opts);return this;}})(jQuery);(function($){$.fn.autoEllipsis=function(options){$(this).each(function(){options=$.extend({'position':'center','tooltip':false,'restoreText':false},options);if(options.restoreText){if(!$(this).data('autoEllipsis.originalText')){$(this).data('autoEllipsis.originalText',$(this).text());}else{$(this).text($(this).data('autoEllipsis.originalText'));}}
359 -var text=$(this).text();var $text=$('<span />').text(text).css('whiteSpace','nowrap');$(this).empty().append($text);if($text.width()>$(this).width()){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()>$(this).width()){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()>($(this).width())&&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;}}
360 -break;case'left':var r=0;while($text.outerWidth()>$(this).width()&&r<text.length){$text.text('...'+text.substr(r));r++;}
 358+{$.eachAsync(this,opts);return this;}})(jQuery);(function($){var cache={};$.fn.autoEllipsis=function(options){options=$.extend({'position':'center','tooltip':false,'restoreText':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'));}}
 359+var text=$this.text();var w=$this.width();var $text=$('<span />').css('whiteSpace','nowrap');$this.empty().append($text);if(!(text in cache)){cache[text]={};}
 360+if(w in cache[text]){$text.text(cache[text][w]);return;}
 361+$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;}}
 362+break;case'left':var r=0;while($text.outerWidth()>w&&r<text.length){$text.text('...'+text.substr(r));r++;}
361363 break;}
362364 if(options.tooltip)
363 -$text.attr('title',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]);}
 365+$text.attr('title',text);}
 366+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]);}
364367 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';}
365368 if(r.name==='presto'){r.version=($.browser.version>9.27)?'futhark':'linear_b';}
366369 r.versionNumber=parseFloat(r.version,10)||0;r.versionX=(r.version!==x)?(r.version+'').substr(0,1):x;r.className=r.name+r.versionX;return r;};a=(a.match(/Opera|Navigator|Minefield|KHTML|Chrome/)?m(a,[[/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/,''],['Chrome Safari','Chrome'],['KHTML','Konqueror'],['Minefield','Firefox'],['Navigator','Netscape']]):a).toLowerCase();$.browser=$.extend((!z)?$.browser:{},c(a,/(camino|chrome|firefox|netscape|konqueror|lynx|msie|opera|safari)/,[],/(camino|chrome|firefox|netscape|netscape6|opera|version|konqueror|lynx|msie|safari)(\/|\s)([a-z0-9\.\+]*?)(\;|dev|rel|\s|$)/));$.layout=c(a,/(gecko|konqueror|msie|opera|webkit)/,[['konqueror','khtml'],['msie','trident'],['opera','presto']],/(applewebkit|rv|konqueror|msie)(\:|\/|\s)([a-z0-9\.]*?)(\;|\)|\s)/);$.os={name:(/(win|mac|linux|sunos|solaris|iphone)/.exec(navigator.platform.toLowerCase())||[u])[0].replace('sunos','solaris')};if(!z){$('html').addClass([$.os.name,$.browser.name,$.browser.className,$.layout.name,$.layout.className].join(' '));}};$.browserTest(navigator.userAgent);})(jQuery);(function($){$.fn.collapsibleTabs=function($$options){if(!this.length)return this;var $settings=$.extend({},$.collapsibleTabs.defaults,$$options);this.each(function(){var $this=$(this);$.collapsibleTabs.instances=($.collapsibleTabs.instances.length==0?$this:$.collapsibleTabs.instances.add($this));$this.data('collapsibleTabsSettings',$settings);$this.children($settings.collapsible).each(function(){var $collapsible=$(this);$collapsible.data('collapsibleTabsSettings',{'expandedContainer':$settings.expandedContainer,'collapsedContainer':$settings.collapsedContainer,'expandedWidth':$collapsible.width(),'prevElement':$collapsible.prev()});});});if(!$.collapsibleTabs.boundEvent){$(window).delayedBind('500','resize',function(){$.collapsibleTabs.handleResize();});}
@@ -453,7 +456,7 @@
454457 var range3=context.$iframe[0].contentWindow.document.selection.createRange();range3.collapse(false);range3.moveEnd('character',1);if(range3.text!="\r"&&range3.text!="\n"&&range3.text!=""){post+="\n";}}
455458 var insertText="";if(options.splitlines){for(var i=0;i<selTextArr.length;i++){insertText=insertText+pre+selTextArr[i]+post;if(i!=selTextArr.length-1)insertText+="\n";}}else{insertText=pre+selText+post;}
456459 range.pasteHTML(insertText.replace(/\</g,'&lt;').replace(/>/g,'&gt;').replace(/\r?\n/g,'<br />'));}
457 -$(context.$iframe[0].contentWindow.document).trigger('encapsulateSelection',[pre,options.peri,post,options.ownline,options.replace]);return context.$textarea;},'getCaretPosition':function(options){},'setSelection':function(options){var sc=options.startContainer,ec=options.endContainer;sc=sc&&sc.jquery?sc[0]:sc;ec=ec&&ec.jquery?ec[0]:ec;if(context.$iframe[0].contentWindow.getSelection){var start=options.start,end=options.end;if(!sc||!ec){var offsets=context.fn.getOffsets();var startContainer=offsets[start].node,startOffset=offsets[start].offset;var endContainer=offsets[end].node,endOffset=offsets[end].offset;sc=offsets[start].node;ec=offsets[end].node;start=offsets[start].offset;end=offsets[end].offset;}
 460+$(context.$iframe[0].contentWindow.document).trigger('encapsulateSelection',[pre,options.peri,post,options.ownline,options.replace]);return context.$textarea;},'getCaretPosition':function(options){},'setSelection':function(options){var sc=options.startContainer,ec=options.endContainer;sc=sc&&sc.jquery?sc[0]:sc;ec=ec&&ec.jquery?ec[0]:ec;if(context.$iframe[0].contentWindow.getSelection){var start=options.start,end=options.end;if(!sc||!ec){var s=context.fn.getOffset(start);var e=context.fn.getOffset(end);sc=s.node;ec=e.node;start=s.offset;end=e.offset;}
458461 if(!sc||!ec){return context.$textarea;}
459462 var sel=context.$iframe[0].contentWindow.getSelection();while(sc.firstChild&&sc.nodeName!='#text'){sc=sc.firstChild;}
460463 while(ec.firstChild&&ec.nodeName!='#text'){ec=ec.firstChild;}
@@ -464,8 +467,7 @@
465468 var topBound=html.scrollTop()>body.scrollTop()?html.scrollTop():body.scrollTop(),bottomBound=topBound+context.$iframe.height();if(force||y<topBound||y>bottomBound){html.scrollTop(y);body.scrollTop(y);}
466469 $element.trigger('scrollToTop');},'beforeSelection':function(selector,strict){if(typeof selector=='undefined'){selector='*';}
467470 var e,offset;if(context.$iframe[0].contentWindow.getSelection){var selection=context.$iframe[0].contentWindow.getSelection();if(selection.baseNode!==null){e=selection.getRangeAt(0).startContainer;offset=selection.startOffset;}else{return $([]);}}else if(context.$iframe[0].contentWindow.document.selection){var range=context.$iframe[0].contentWindow.document.selection.createRange();var range2=context.$iframe[0].contentWindow.document.body.createTextRange();try{range2.setEndPoint('EndToStart',range);}catch(e){return $([]);}
468 -var seekPos=context.fn.htmlToText(range2.htmlText).length;var offsets=context.fn.getOffsets();e=offsets[seekPos]?offsets[seekPos].node:null;offset=offsets[seekPos]?offsets[seekPos].offset:null;if(!e)
469 -return $([]);}
 471+var seekPos=context.fn.htmlToText(range2.htmlText).length;var offset=context.fn.getOffset(seekPos);e=offset?offset.node:null;offset=offset?offset.offset:null;if(!e){return $([]);}}
470472 if(e.nodeName!='#text'){var newE=e.firstChild;for(var i=0;i<offset-1&&newE;i++){newE=newE.nextSibling;}
471473 while(newE&&newE.lastChild){newE=newE.lastChild;}
472474 e=newE||e;}
@@ -474,13 +476,15 @@
475477 e=next||e.parentNode;strict=false;}
476478 return $([]);},'traverser':function(start){function Traverser(node,depth,inP){this.node=node;this.depth=depth;this.inP=inP;this.next=function(){var p=this.node;var nextDepth=this.depth;var nextInP=this.inP;while(p&&!p.nextSibling){if(p.nodeName=="P"){nextInP=false;}
477479 p=p.parentNode;nextDepth--;if(nextDepth==0){p=null;}}
478 -p=p?p.nextSibling:null;do{while(p&&$(p).hasClass('wikiEditor-noinclude')){p=p.nextSibling;}
 480+p=p?p.nextSibling:null;do{while(p&&(' '+p.className+' ').indexOf(' wikiEditor-noinclude ')!=-1){p=p.nextSibling;}
479481 if(p&&p.firstChild){p=p.firstChild;nextDepth++;if(p.nodeName=="P"){nextInP=true;}}}while(p&&p.firstChild);return p?new Traverser(p,nextDepth,nextInP):null;};}
480 -var node=start.jquery?start.get(0):start;var depth=0;var inP=node.nodeName=="P";do{while(node&&$(node).hasClass('wikiEditor-noinclude')){node=node.nextSibling;}
481 -if(node&&node.firstChild){node=node.firstChild;depth++;if(node.nodeName=="P"){inP=true;}}}while(node&&node.firstChild);return new Traverser(node,depth,inP);},'getOffsets':function(){if(!context.offsets){context.fn.refreshOffsets();}
482 -return context.offsets;},'purgeOffsets':function(){context.offsets=null;},'refreshOffsets':function(){context.offsets=[];var t=context.fn.traverser(context.$content);var pos=0,lastTextNode=null,lastTextNodeDepth=null;while(t){if(t.node.nodeName!='#text'&&t.node.nodeName!='BR'){t=t.next();continue;}
483 -var nextPos=t.node.nodeName=='#text'?pos+t.node.nodeValue.length:pos+1;var nextT=t.next();var leavingP=t.inP&&nextT&&!nextT.inP;for(var p=pos;p<nextPos;p++){context.offsets[p]={'node':t.node,'offset':p-pos,'length':nextPos-pos+(leavingP?1:0),'depth':t.depth,'lastTextNode':lastTextNode,'lastTextNodeDepth':lastTextNodeDepth};}
484 -if(leavingP){context.offsets[nextPos]={'node':t.node,'offset':nextPos-pos,'length':nextPos-pos+1,'depth':t.depth,'lastTextNode':lastTextNode,'lastTextNodeDepth':lastTextNodeDepth};}
 482+var node=start.jquery?start.get(0):start;var depth=0;var inP=node.nodeName=="P";do{while(node&&(' '+node.className+' ').indexOf(' wikiEditor-noinclude ')!=-1){node=node.nextSibling;}
 483+if(node&&node.firstChild){node=node.firstChild;depth++;if(node.nodeName=="P"){inP=true;}}}while(node&&node.firstChild);return new Traverser(node,depth,inP);},'getOffset':function(offset){if(!context.offsets){context.fn.refreshOffsets();}
 484+if(offset in context.offsets){return context.offsets[offset];}
 485+var lowerBound=0;for(var o in context.offsets){if(o>offset){break;}
 486+lowerBound=o;}
 487+var base=context.offsets[lowerBound];return context.offsets[offset]={'node':base.node,'offset':base.offset+offset-o,'length':base.length,'depth':base.depth,'lastTextNode':lastTextNode,'lastTextNodeDepth':lastTextNodeDepth};},'purgeOffsets':function(){context.offsets=null;},'refreshOffsets':function(){context.offsets=[];var t=context.fn.traverser(context.$content);var pos=0,lastTextNode=null,lastTextNodeDepth=null;while(t){if(t.node.nodeName!='#text'&&t.node.nodeName!='BR'){t=t.next();continue;}
 488+var nextPos=t.node.nodeName=='#text'?pos+t.node.nodeValue.length:pos+1;var nextT=t.next();var leavingP=t.inP&&nextT&&!nextT.inP;context.offsets[pos]={'node':t.node,'offset':0,'length':nextPos-pos+(leavingP?1:0),'depth':t.depth,'lastTextNode':lastTextNode,'lastTextNodeDepth':lastTextNodeDepth};if(leavingP){context.offsets[nextPos]={'node':t.node,'offset':nextPos-pos,'length':nextPos-pos+1,'depth':t.depth,'lastTextNode':lastTextNode,'lastTextNodeDepth':lastTextNodeDepth};}
485489 pos=nextPos+(leavingP?1:0);if(t.node.nodeName=='#text'){lastTextNode=t.node;lastTextNodeDepth=t.depth;}
486490 t=nextT;}}};context.$textarea.wrap($('<div></div>').addClass('wikiEditor-ui')).wrap($('<div></div>').addClass('wikiEditor-ui-view wikiEditor-ui-view-wikitext')).wrap($('<div></div>').addClass('wikiEditor-ui-left')).wrap($('<div></div>').addClass('wikiEditor-ui-bottom')).wrap($('<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.$iframe=$('<iframe></iframe>').attr({'frameBorder':0,'border':0,'src':wgScriptPath+'/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.html?'+'instance='+context.instance+'&ts='+(new Date()).getTime(),'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;}}
487491 context.$content=$(context.$iframe[0].contentWindow.document.body);var html=context.$textarea.val().replace(/\</g,'&lt;').replace(/\>/g,'&gt;');if($.browser.msie){if($.browser.versionNumber<=7){html=html.replace(/ /g,"&nbsp;");}else{html=html.replace(/(^|\n) /g,"$1&nbsp;");}
@@ -494,15 +498,13 @@
495499 var maxTI=0;$j('[tabindex]').each(function(){var ti=parseInt($j(this).attr('tabindex'));if(ti>maxTI)
496500 maxTI=ti;});var tabIndex=maxTI+1;$j('.ui-dialog input, .ui-dialog button').not('[tabindex]').each(function(){$j(this).attr('tabindex',tabIndex++);});}}});},resize:function(){var wrapper=$(this).closest('.ui-dialog');var oldWidth=wrapper.width();var oldHidden=$(this).find('*').not(':visible');oldHidden.each(function(){$(this).data('oldstyle',$(this).attr('style'));});oldHidden.show();var oldWS=$(this).css('white-space');$(this).css('white-space','nowrap');if(wrapper.width()<=$(this).get(0).scrollWidth){var thisWidth=$(this).data('thisWidth')?$(this).data('thisWidth'):0;thisWidth=Math.max($(this).get(0).scrollWidth,thisWidth);$(this).width(thisWidth);$(this).data('thisWidth',thisWidth);var wrapperWidth=$(this).data('wrapperWidth')?$(this).data('wrapperWidth'):0;wrapperWidth=Math.max(wrapper.get(0).scrollWidth,wrapperWidth);wrapper.width(wrapperWidth);$(this).data('wrapperWidth',wrapperWidth);$(this).dialog({'width':wrapper.width()});wrapper.css('left',parseInt(wrapper.css('left'))-(wrapper.width()-oldWidth)/2);}
497501 $(this).css('white-space',oldWS);oldHidden.each(function(){$(this).attr('style',$(this).data('oldstyle'));});}},modules:{},quickDialog:function(body,settings){$('<div />').text(body).appendTo($('body')).dialog($.extend({bgiframe:true,modal:true},settings)).dialog('open');}};})(jQuery);(function($){$.wikiEditor.modules.highlight={cfg:{'styleVersion':3},evt:{delayedChange:function(context,event){if(event.data.scope=='division'){$.wikiEditor.modules.highlight.fn.scan(context,"");$.wikiEditor.modules.highlight.fn.mark(context,"","");}},ready:function(context,event){$.wikiEditor.modules.highlight.fn.scan(context,"");$.wikiEditor.modules.highlight.fn.mark(context,"","");}},fn:{create:function(context,config){},divide:function(context){},isolate:function(context){return[];},strip:function(context,division){return $('<div />').html(division.html().replace(/\<br[^\>]*\>/g,"\n")).text();},scan:function(context,division){function Token(offset,label,tokenStart,match){this.offset=offset;this.label=label;this.tokenStart=tokenStart;this.match=match;}
498 -var tokenArray=context.modules.highlight.tokenArray=[];var text=context.fn.getContents();for(module in $.wikiEditor.modules){if('exp'in $.wikiEditor.modules[module]){for(var i=0;i<$.wikiEditor.modules[module].exp.length;i++){var regex=$.wikiEditor.modules[module].exp[i].regex;var label=$.wikiEditor.modules[module].exp[i].label;var markAfter=false;if(typeof $.wikiEditor.modules[module].exp[i].markAfter!='undefined'){markAfter=true;}
 502+var tokenArray=context.modules.highlight.tokenArray=[];var text=context.fn.getContents();for(module in context.modules){if(module in $.wikiEditor.modules&&'exp'in $.wikiEditor.modules[module]){for(var i=0;i<$.wikiEditor.modules[module].exp.length;i++){var regex=$.wikiEditor.modules[module].exp[i].regex;var label=$.wikiEditor.modules[module].exp[i].label;var markAfter=false;if(typeof $.wikiEditor.modules[module].exp[i].markAfter!='undefined'){markAfter=true;}
499503 match=text.match(regex);var oldOffset=0;while(match!=null){var markOffset=0;var tokenStart=match.index+oldOffset+markOffset;if(markAfter){markOffset+=match[0].length;}
500504 tokenArray.push(new Token(match.index+oldOffset+markOffset,label,tokenStart,match));oldOffset+=match.index+match[0].length;newSubstring=text.substring(oldOffset);match=newSubstring.match(regex);}}}}
501 -tokenArray.sort(function(a,b){return a.offset-b.offset||a.tokenStart-b.tokenStart;});context.fn.trigger('scan');},mark:function(context,division,tokens){var markers=context.modules.highlight.markers=[];context.fn.trigger('mark');markers.sort(function(a,b){return a.start-b.start||a.end-b.end;});var offsets=context.fn.getOffsets();for(var i=0;i<markers.length;i++){var start=markers[i].start;if(!(start in offsets)){continue;}
502 -var startNode=offsets[start].node;var startDepth=offsets[start].depth;if(offsets[start].offset>0){startNode=startNode.splitText(offsets[start].offset);}
503 -while(startNode.nodeName=='BR'&&start+1 in offsets){start++;startNode=offsets[start].node;startDepth=offsets[start].depth;}
504 -var end=markers[i].end;if(!(end in offsets)){continue;}
505 -var endNode=offsets[end].node;var endDepth=offsets[end].depth;if(offsets[end].offset<offsets[end].length-1){endNode.splitText(offsets[end].offset+1);}
506 -if(endNode.nodeName=='BR'){endNode=offsets[end].lastTextNode;endDepth=offsets[end].lastTextNodeDepth;}
 505+tokenArray.sort(function(a,b){return a.offset-b.offset||a.tokenStart-b.tokenStart;});context.fn.trigger('scan');},mark:function(context,division,tokens){var markers=context.modules.highlight.markers=[];context.fn.trigger('mark');markers.sort(function(a,b){return a.start-b.start||a.end-b.end;});for(var i=0;i<markers.length;i++){var start=markers[i].start;var s=context.fn.getOffset(start);var startNode=s.node;var startDepth=s.depth;if(s.offset>0){startNode=startNode.splitText(s.offset);}
 506+while(startNode.nodeName=='BR'&&start+1 in offsets){start++;startNode=s.node;startDepth=s.depth;}
 507+var end=markers[i].end;var e=context.fn.getOffset(end);var endNode=e.node;var endDepth=e.depth;if(e.offset<e.length-1){endNode.splitText(e.offset+1);}
 508+if(endNode.nodeName=='BR'){endNode=e.lastTextNode;endDepth=e.lastTextNodeDepth;}
507509 var ca1=startNode,ca2=endNode;if(startDepth>endDepth){for(var j=0;j<startDepth-endDepth&&ca1;j++){ca1=ca1.parentNode.firstChild==ca1?ca1.parentNode:null;}}
508510 else if(startDepth<endDepth){for(var j=0;j<endDepth-startDepth&&ca2;j++){ca2=ca2.parentNode.lastChild==ca2?ca2.parentNode:null;}}
509511 while(ca1&&ca2&&ca1.parentNode&&ca2.parentNode&&ca1.parentNode!=ca2.parentNode&&ca1.parentNode.firstChild&&ca2.parentNode.lastChild){ca1=ca1.parentNode.firstChild==ca1?ca1.parentNode:null;ca2=ca2.parentNode.lastChild==ca2?ca2.parentNode:null;}

Follow-up revisions

RevisionCommit summaryAuthorDate
r61576UsabilityInitiative: (bug 22260) Fix some JS errors occurring when the docume...catrope14:49, 27 January 2010

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r61539Removing the table layout on the search and replace dialog, and a small white...adam22:56, 26 January 2010

Status & tagging log