r60100 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r60099‎ | r60100 | r60101 >
Date:23:19, 15 December 2009
Author:catrope
Status:deferred
Tags:
Comment:
UsabilityInitiative: Totally rewrote the wrap() function in the highlight module, so it now cleverly wraps stuff using DOM manipulation functions rather than tearing down the entire iframe contents and injecting it anew with slight modifications. Also changed the way markers are specified, added CSS so we have actual highlighting and turned it on by default. On the side, fix an issue with the tokenStack not being sorted by occurrance.
Modified paths:
  • /trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php (modified) (history)
  • /trunk/extensions/UsabilityInitiative/css/wikiEditor.highlight.css (added) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins.combined.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.highlight.js (modified) (history)
  • /trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.templateEditor.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php
@@ -72,12 +72,12 @@
7373 array( 'src' => 'js/plugins/jquery.suggestions.js', 'version' => 6 ),
7474 array( 'src' => 'js/plugins/jquery.textSelection.js', 'version' => 21 ),
7575 array( 'src' => 'js/plugins/jquery.wikiEditor.js', 'version' => 36 ),
76 - array( 'src' => 'js/plugins/jquery.wikiEditor.highlight.js', 'version' => 2 ),
 76+ array( 'src' => 'js/plugins/jquery.wikiEditor.highlight.js', 'version' => 3 ),
7777 array( 'src' => 'js/plugins/jquery.wikiEditor.toolbar.js', 'version' => 40 ),
7878 array( 'src' => 'js/plugins/jquery.wikiEditor.dialogs.js', 'version' => 10 ),
7979 array( 'src' => 'js/plugins/jquery.wikiEditor.toc.js', 'version' => 52 ),
8080 array( 'src' => 'js/plugins/jquery.wikiEditor.preview.js', 'version' => 8 ),
81 - array( 'src' => 'js/plugins/jquery.wikiEditor.templateEditor.js', 'version' => 2 ),
 81+ array( 'src' => 'js/plugins/jquery.wikiEditor.templateEditor.js', 'version' => 3 ),
8282 array( 'src' => 'js/plugins/jquery.wikiEditor.publish.js', 'version' => 1 ),
8383 ),
8484 'combined' => array(
Index: trunk/extensions/UsabilityInitiative/css/wikiEditor.highlight.css
@@ -0,0 +1,13 @@
 2+/* CSS for highlight module. Note that this CSS is loaded in the iframe,
 3+ * not in the main document. For that reason, it's also not in the combined and
 4+ * minified files.
 5+ */
 6+.wikiEditor-highlight-template {
 7+ display: inline;
 8+ color: red;
 9+}
 10+
 11+.wikiEditor-toc-header { /* TODO: move parsing to highlight module and rename this */
 12+ display: inline;
 13+ font-weight: bold;
 14+}
Property changes on: trunk/extensions/UsabilityInitiative/css/wikiEditor.highlight.css
___________________________________________________________________
Name: svn:eol-style
115 + native
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.templateEditor.js
@@ -46,22 +46,19 @@
4747 }
4848 };
4949 // Get the markers and tokens from the current context
50 - var markers = context.modules.highlight.data.markers;
51 - var tokenStack = context.modules.highlight.data.tokenArray;
 50+ var markers = context.modules.highlight.markers;
 51+ var tokenStack = context.modules.highlight.tokenArray;
5252 // Scan through and detect the boundries of template calls
5353 var templateBeginFound = false;
5454 var templateBoundaries;
5555 while ( templateBoundaries = findOutermostTemplates( tokenStack ) ) {
56 - // Ensure indexes exist for left and right boundry markers
57 - if ( typeof markers[tokenStack[templateBoundaries[0]].offset] == 'undefined' ) {
58 - markers[tokenStack[templateBoundaries[0]].offset] = [];
59 - }
60 - if ( typeof markers[tokenStack[templateBoundaries[1]].offset] == 'undefined' ) {
61 - markers[tokenStack[templateBoundaries[1]].offset] = [];
62 - }
63 - // Append boundry markers
64 - markers[tokenStack[templateBoundaries[0]].offset].push( "<div class='wiki-template'>" );
65 - markers[tokenStack[templateBoundaries[1]].offset].push( "</div>" );
 56+ context.modules.highlight.markers.push( {
 57+ start: tokenStack[templateBoundaries[0]].offset,
 58+ end: tokenStack[templateBoundaries[1]].offset,
 59+ wrapElement: function() {
 60+ return $( '<div />' ).addClass( 'wikiEditor-highlight-template' );
 61+ }
 62+ } );
6663 }
6764 }
6865 },
Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.highlight.js
@@ -28,10 +28,22 @@
2929 * ; Definition
3030 * : Definition
3131 */
32 - if ( event.data.scope == 'do_not_trigger' ) {
 32+ if ( event.data.scope == 'keydown' ) {
3333 $.wikiEditor.modules.highlight.fn.scan( context, "" );
3434 $.wikiEditor.modules.highlight.fn.mark( context, "", "" );
3535 }
 36+ },
 37+ ready: function( context, event ) {
 38+ // Add our CSS to the iframe
 39+ context.$content.parent().find( 'head' ).append( $j( '<link />' ).attr( {
 40+ 'rel': 'stylesheet',
 41+ 'type': 'text/css',
 42+ 'href': wgScriptPath + '/extensions/UsabilityInitiative/css/wikiEditor.highlight.css',
 43+ } ) );
 44+
 45+ // Highlight stuff for the first time
 46+ $.wikiEditor.modules.highlight.fn.scan( context, "" );
 47+ $.wikiEditor.modules.highlight.fn.mark( context, "", "" );
3648 }
3749 },
3850 /**
@@ -117,6 +129,7 @@
118130 }
119131 }
120132
 133+ context.modules.highlight.tokenArray.sort( function( a, b ) { return a.offset - b.offset; } );
121134 return context.modules.highlight.tokenArray; // array of tokens
122135 },
123136 /**
@@ -125,30 +138,155 @@
126139 * @param division
127140 * @param tokens
128141 */
 142+ // FIXME: What do division and tokens do?
129143 mark: function( context, division, tokens ) {
130 - // We need to markup some text based on some tokens
131 - var rawText = context.fn.getContents();
132 -
133 - //get all markers
 144+ // Get all markers
 145+ context.modules.highlight.markers = [];
134146 for ( module in $.wikiEditor.modules ) {
135147 if ( 'evt' in $.wikiEditor.modules[module] && 'mark' in $.wikiEditor.modules[module].evt ) {
136 - $.wikiEditor.modules[module].evt.mark();
 148+ $.wikiEditor.modules[module].evt.mark( context ); // FIXME: event?
137149 }
138150 }
139 - markedText = '';
140 - var previousIndex = 0;
141 - for ( var currentIndex in context.modules.highlight.markers ){
142 - markedText += rawText.substring( previousIndex, currentIndex );
143 - for( var i = 0 ; i < context.modules.highlight.markers[currentIndex].length; i++ ){
144 - markedText += context.modules.highlight.markers[currentIndex][i];
145 - }
146 - previousIndex = currentIndex;
147 - }
148 - if ( markedText != '' ){
149 - markedText.replace( /\n/g, '<br\>' );
150 - context.fn.setContents( { contents:markedText } );
151 - }
 151+ var markers = context.modules.highlight.markers;
 152+ markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
 153+
 154+ // Traverse the iframe DOM, inserting markers where they're needed
 155+ // The loop traverses all leaf nodes in the DOM, and uses DOM methods
 156+ // rather than jQuery because it has to work with text nodes and for performance
 157+ var pos = 0;
 158+ var node = context.$content.get( 0 );
 159+ var next = null;
 160+ var i = 0; // index for markers[]
 161+ var startNode = null;
 162+ var depth = 0, nextDepth = 0, startDepth = null;
 163+
 164+ // Find the leftmost leaf node in the tree
 165+ while ( node.firstChild ) {
 166+ node = node.firstChild;
 167+ depth++;
 168+ }
 169+ while ( i < markers.length && node ) {
 170+ // Find the next leaf node
 171+ var p = node;
 172+ nextDepth = depth;
 173+ while ( p && !p.nextSibling ) {
 174+ p = p.parentNode;
 175+ nextDepth--;
 176+ }
 177+ p = p ? p.nextSibling : null;
 178+ while ( p && p.firstChild ) {
 179+ p = p.firstChild;
 180+ nextDepth++;
 181+ }
 182+ next = p;
 183+
 184+ if ( node.nodeName != '#text' ) {
 185+ if ( node.nodeName == 'BR' )
 186+ pos++;
 187+ // Skip this node
 188+ node = next;
 189+ depth = nextDepth;
 190+ continue;
 191+ }
 192+ var newPos = pos + node.nodeValue.length;
 193+
 194+ // We want to isolate each marker, so we may need to split textNodes
 195+ // if a marker starts or end halfway one.
 196+ if ( !startNode && markers[i].start >= pos && markers[i].start < newPos ) {
 197+ // The next marker starts somewhere in this textNode
 198+ if ( markers[i].start > pos ) {
 199+ // Split off the prefix
 200+ // This leaves the prefix in the current node and puts
 201+ // the rest in a new node, which we immediately advance to
 202+ node = node.splitText( markers[i].start - pos );
 203+ pos = markers[i].start;
 204+ }
 205+ startNode = node;
 206+ startDepth = depth;
 207+ }
 208+
 209+ // TODO: What happens when wrapping a zero-length string?
 210+ // TODO: Detect that something's already been wrapped and leave it alone
 211+ if ( startNode && markers[i].end > pos && markers[i].end <= newPos ) {
 212+ // The marker ends somewhere in this textNode
 213+ if ( markers[i].end < newPos ) {
 214+ // Split off the suffix
 215+ // This puts the suffix in a new node and leaves the rest
 216+ // in the current node. We have to make sure the split-off
 217+ // node will be visited correctly
 218+
 219+ // node.nodeValue.length - ( newPos - markers[i].end )
 220+ next = node.splitText( node.nodeValue.length - newPos + markers[i].end );
 221+ newPos = markers[i].end;
 222+ }
 223+
 224+ // Now wrap everything between startNode and node (may be equal).
 225+ // First find the common ancestor of startNode and node.
 226+ // ca1 and ca2 will be children of this common ancestor, such that
 227+ // ca1 is an ancestor of startNode and ca2 of node.
 228+ // We also check that startNode and node are the leftmost and rightmost
 229+ // leaves in the subtrees rooted at ca1 and ca2 respectively; if this is
 230+ // not the case, we can't cleanly wrap things without misnesting and we
 231+ // silently fail.
 232+ var ca1 = startNode, ca2 = node;
 233+ // Correct for startNode and node possibly not having the same depth
 234+ if ( startDepth > depth ) {
 235+ for ( var j = 0; j < startDepth - depth && ca1; j++ ) {
 236+ ca1 = ca1.parentNode;
 237+ }
 238+ }
 239+ else if ( startDepth < depth ) {
 240+ for ( var j = 0; j < depth - startDepth && ca2; j++ ) {
 241+ ca2 = ca2.parentNode;
 242+ }
 243+ }
 244+ while ( ca1 && ca2 && ca1.parentNode != ca2.parentNode ) {
 245+ if ( ca1.parentNode.firstChild != ca1 || ca2.parentNode.lastChild != ca2 ) {
 246+ // startNode and node are not the leftmost and rightmost leaves
 247+ ca1 = ca2 = null;
 248+ } else {
 249+ ca1 = ca1.parentNode;
 250+ ca2 = ca2.parentNode;
 251+ }
 252+ }
 253+
 254+ if ( ca1 && ca2 ) {
 255+ // We have to store things like .parentNode and .nextSibling
 256+ // because appendChild() changes these properties
 257+ var newNode = markers[i].wrapElement;
 258+ if ( typeof newNode == 'function' )
 259+ newNode = newNode();
 260+ if ( newNode.jquery )
 261+ newNode = newNode.get( 0 );
 262+ var commonAncestor = ca1.parentNode;
 263+ var nextNode = ca2.nextSibling;
 264+
 265+ // Append all nodes between ca1 and ca2 (inclusive)
 266+ // to newNode
 267+ var n = ca1;
 268+ while ( n != nextNode ) {
 269+ var ns = n.nextSibling;
 270+ newNode.appendChild( n );
 271+ n = ns;
 272+ }
 273+
 274+ // Insert newNode in the right place
 275+ if ( nextNode )
 276+ commonAncestor.insertBefore( newNode, nextNode );
 277+ else
 278+ commonAncestor.appendChild( newNode );
 279+ }
 280+ startNode = null; // Clear for next iteration
 281+ startDepth = null;
 282+ i++;
 283+ }
 284+
 285+ pos = newPos;
 286+ node = next;
 287+ depth = nextDepth;
 288+ }
152289 }
153290 }
154291
155 -}; })( jQuery );
\ No newline at end of file
 292+}; })( jQuery );
 293+
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.js
@@ -2156,10 +2156,22 @@
21572157 * ; Definition
21582158 * : Definition
21592159 */
2160 - if ( event.data.scope == 'do_not_trigger' ) {
 2160+ if ( event.data.scope == 'keydown' ) {
21612161 $.wikiEditor.modules.highlight.fn.scan( context, "" );
21622162 $.wikiEditor.modules.highlight.fn.mark( context, "", "" );
21632163 }
 2164+ },
 2165+ ready: function( context, event ) {
 2166+ // Add our CSS to the iframe
 2167+ context.$content.parent().find( 'head' ).append( $j( '<link />' ).attr( {
 2168+ 'rel': 'stylesheet',
 2169+ 'type': 'text/css',
 2170+ 'href': wgScriptPath + '/extensions/UsabilityInitiative/css/wikiEditor.highlight.css',
 2171+ } ) );
 2172+
 2173+ // Highlight stuff for the first time
 2174+ $.wikiEditor.modules.highlight.fn.scan( context, "" );
 2175+ $.wikiEditor.modules.highlight.fn.mark( context, "", "" );
21642176 }
21652177 },
21662178 /**
@@ -2245,6 +2257,7 @@
22462258 }
22472259 }
22482260
 2261+ context.modules.highlight.tokenArray.sort( function( a, b ) { return a.offset - b.offset; } );
22492262 return context.modules.highlight.tokenArray; // array of tokens
22502263 },
22512264 /**
@@ -2253,33 +2266,159 @@
22542267 * @param division
22552268 * @param tokens
22562269 */
 2270+ // FIXME: What do division and tokens do?
22572271 mark: function( context, division, tokens ) {
2258 - // We need to markup some text based on some tokens
2259 - var rawText = context.fn.getContents();
2260 -
2261 - //get all markers
 2272+ // Get all markers
 2273+ context.modules.highlight.markers = [];
22622274 for ( module in $.wikiEditor.modules ) {
22632275 if ( 'evt' in $.wikiEditor.modules[module] && 'mark' in $.wikiEditor.modules[module].evt ) {
2264 - $.wikiEditor.modules[module].evt.mark();
 2276+ $.wikiEditor.modules[module].evt.mark( context ); // FIXME: event?
22652277 }
22662278 }
2267 - markedText = '';
2268 - var previousIndex = 0;
2269 - for ( var currentIndex in context.modules.highlight.markers ){
2270 - markedText += rawText.substring( previousIndex, currentIndex );
2271 - for( var i = 0 ; i < context.modules.highlight.markers[currentIndex].length; i++ ){
2272 - markedText += context.modules.highlight.markers[currentIndex][i];
2273 - }
2274 - previousIndex = currentIndex;
2275 - }
2276 - if ( markedText != '' ){
2277 - markedText.replace( /\n/g, '<br\>' );
2278 - context.fn.setContents( { contents:markedText } );
2279 - }
 2279+ var markers = context.modules.highlight.markers;
 2280+ markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
 2281+
 2282+ // Traverse the iframe DOM, inserting markers where they're needed
 2283+ // The loop traverses all leaf nodes in the DOM, and uses DOM methods
 2284+ // rather than jQuery because it has to work with text nodes and for performance
 2285+ var pos = 0;
 2286+ var node = context.$content.get( 0 );
 2287+ var next = null;
 2288+ var i = 0; // index for markers[]
 2289+ var startNode = null;
 2290+ var depth = 0, nextDepth = 0, startDepth = null;
 2291+
 2292+ // Find the leftmost leaf node in the tree
 2293+ while ( node.firstChild ) {
 2294+ node = node.firstChild;
 2295+ depth++;
 2296+ }
 2297+ while ( i < markers.length && node ) {
 2298+ // Find the next leaf node
 2299+ var p = node;
 2300+ nextDepth = depth;
 2301+ while ( p && !p.nextSibling ) {
 2302+ p = p.parentNode;
 2303+ nextDepth--;
 2304+ }
 2305+ p = p ? p.nextSibling : null;
 2306+ while ( p && p.firstChild ) {
 2307+ p = p.firstChild;
 2308+ nextDepth++;
 2309+ }
 2310+ next = p;
 2311+
 2312+ if ( node.nodeName != '#text' ) {
 2313+ if ( node.nodeName == 'BR' )
 2314+ pos++;
 2315+ // Skip this node
 2316+ node = next;
 2317+ depth = nextDepth;
 2318+ continue;
 2319+ }
 2320+ var newPos = pos + node.nodeValue.length;
 2321+
 2322+ // We want to isolate each marker, so we may need to split textNodes
 2323+ // if a marker starts or end halfway one.
 2324+ if ( !startNode && markers[i].start >= pos && markers[i].start < newPos ) {
 2325+ // The next marker starts somewhere in this textNode
 2326+ if ( markers[i].start > pos ) {
 2327+ // Split off the prefix
 2328+ // This leaves the prefix in the current node and puts
 2329+ // the rest in a new node, which we immediately advance to
 2330+ node = node.splitText( markers[i].start - pos );
 2331+ pos = markers[i].start;
 2332+ }
 2333+ startNode = node;
 2334+ startDepth = depth;
 2335+ }
 2336+
 2337+ // TODO: What happens when wrapping a zero-length string?
 2338+ // TODO: Detect that something's already been wrapped and leave it alone
 2339+ if ( startNode && markers[i].end > pos && markers[i].end <= newPos ) {
 2340+ // The marker ends somewhere in this textNode
 2341+ if ( markers[i].end < newPos ) {
 2342+ // Split off the suffix
 2343+ // This puts the suffix in a new node and leaves the rest
 2344+ // in the current node. We have to make sure the split-off
 2345+ // node will be visited correctly
 2346+
 2347+ // node.nodeValue.length - ( newPos - markers[i].end )
 2348+ next = node.splitText( node.nodeValue.length - newPos + markers[i].end );
 2349+ newPos = markers[i].end;
 2350+ }
 2351+
 2352+ // Now wrap everything between startNode and node (may be equal).
 2353+ // First find the common ancestor of startNode and node.
 2354+ // ca1 and ca2 will be children of this common ancestor, such that
 2355+ // ca1 is an ancestor of startNode and ca2 of node.
 2356+ // We also check that startNode and node are the leftmost and rightmost
 2357+ // leaves in the subtrees rooted at ca1 and ca2 respectively; if this is
 2358+ // not the case, we can't cleanly wrap things without misnesting and we
 2359+ // silently fail.
 2360+ var ca1 = startNode, ca2 = node;
 2361+ // Correct for startNode and node possibly not having the same depth
 2362+ if ( startDepth > depth ) {
 2363+ for ( var j = 0; j < startDepth - depth && ca1; j++ ) {
 2364+ ca1 = ca1.parentNode;
 2365+ }
 2366+ }
 2367+ else if ( startDepth < depth ) {
 2368+ for ( var j = 0; j < depth - startDepth && ca2; j++ ) {
 2369+ ca2 = ca2.parentNode;
 2370+ }
 2371+ }
 2372+ while ( ca1 && ca2 && ca1.parentNode != ca2.parentNode ) {
 2373+ if ( ca1.parentNode.firstChild != ca1 || ca2.parentNode.lastChild != ca2 ) {
 2374+ // startNode and node are not the leftmost and rightmost leaves
 2375+ ca1 = ca2 = null;
 2376+ } else {
 2377+ ca1 = ca1.parentNode;
 2378+ ca2 = ca2.parentNode;
 2379+ }
 2380+ }
 2381+
 2382+ if ( ca1 && ca2 ) {
 2383+ // We have to store things like .parentNode and .nextSibling
 2384+ // because appendChild() changes these properties
 2385+ var newNode = markers[i].wrapElement;
 2386+ if ( typeof newNode == 'function' )
 2387+ newNode = newNode();
 2388+ if ( newNode.jquery )
 2389+ newNode = newNode.get( 0 );
 2390+ var commonAncestor = ca1.parentNode;
 2391+ var nextNode = ca2.nextSibling;
 2392+
 2393+ // Append all nodes between ca1 and ca2 (inclusive)
 2394+ // to newNode
 2395+ var n = ca1;
 2396+ while ( n != nextNode ) {
 2397+ var ns = n.nextSibling;
 2398+ newNode.appendChild( n );
 2399+ n = ns;
 2400+ }
 2401+
 2402+ // Insert newNode in the right place
 2403+ if ( nextNode )
 2404+ commonAncestor.insertBefore( newNode, nextNode );
 2405+ else
 2406+ commonAncestor.appendChild( newNode );
 2407+ }
 2408+ startNode = null; // Clear for next iteration
 2409+ startDepth = null;
 2410+ i++;
 2411+ }
 2412+
 2413+ pos = newPos;
 2414+ node = next;
 2415+ depth = nextDepth;
 2416+ }
22802417 }
22812418 }
22822419
2283 -}; })( jQuery );/* Preview module for wikiEditor */
 2420+}; })( jQuery );
 2421+
 2422+/* Preview module for wikiEditor */
22842423 ( function( $ ) { $.wikiEditor.modules.preview = {
22852424
22862425 /**
@@ -2572,22 +2711,19 @@
25732712 }
25742713 };
25752714 // Get the markers and tokens from the current context
2576 - var markers = context.modules.highlight.data.markers;
2577 - var tokenStack = context.modules.highlight.data.tokenArray;
 2715+ var markers = context.modules.highlight.markers;
 2716+ var tokenStack = context.modules.highlight.tokenArray;
25782717 // Scan through and detect the boundries of template calls
25792718 var templateBeginFound = false;
25802719 var templateBoundaries;
25812720 while ( templateBoundaries = findOutermostTemplates( tokenStack ) ) {
2582 - // Ensure indexes exist for left and right boundry markers
2583 - if ( typeof markers[tokenStack[templateBoundaries[0]].offset] == 'undefined' ) {
2584 - markers[tokenStack[templateBoundaries[0]].offset] = [];
2585 - }
2586 - if ( typeof markers[tokenStack[templateBoundaries[1]].offset] == 'undefined' ) {
2587 - markers[tokenStack[templateBoundaries[1]].offset] = [];
2588 - }
2589 - // Append boundry markers
2590 - markers[tokenStack[templateBoundaries[0]].offset].push( "<div class='wiki-template'>" );
2591 - markers[tokenStack[templateBoundaries[1]].offset].push( "</div>" );
 2721+ context.modules.highlight.markers.push( {
 2722+ start: tokenStack[templateBoundaries[0]].offset,
 2723+ end: tokenStack[templateBoundaries[1]].offset,
 2724+ wrapElement: function() {
 2725+ return $( '<div />' ).addClass( 'wikiEditor-highlight-template' );
 2726+ }
 2727+ } );
25922728 }
25932729 }
25942730 },
Index: trunk/extensions/UsabilityInitiative/js/plugins.combined.min.js
@@ -119,14 +119,30 @@
120120 configuration.newButtons[gM(msg)]=configuration.buttons[msg];configuration.buttons=configuration.newButtons;var dialogDiv=$('<div /> ').attr('id',module.id).html(module.html).data('context',context).appendTo($('body')).each(module.init).dialog(configuration);if(!('resizeme'in module)||module.resizeme){dialogDiv.bind('dialogopen',$.wikiEditor.modules.dialogs.fn.resize).find('.ui-tabs').bind('tabsshow',function(){$(this).closest('.ui-dialog-content').each($.wikiEditor.modules.dialogs.fn.resize);});}
121121 var maxTI=0;$j('[tabindex]').each(function(){var ti=parseInt($j(this).attr('tabindex'));if(ti>maxTI)
122122 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);}
123 -$(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={evt:{change:function(context,event){if(event.data.scope=='do_not_trigger'){$.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){this.offset=offset;this.label=label;}
 123+$(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={evt:{change:function(context,event){if(event.data.scope=='keydown'){$.wikiEditor.modules.highlight.fn.scan(context,"");$.wikiEditor.modules.highlight.fn.mark(context,"","");}},ready:function(context,event){context.$content.parent().find('head').append($j('<link />').attr({'rel':'stylesheet','type':'text/css','href':wgScriptPath+'/extensions/UsabilityInitiative/css/wikiEditor.highlight.css',}));$.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){this.offset=offset;this.label=label;}
124124 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;}
125125 match=text.match(regex);var oldOffset=0;while(match!=null){var markOffset=0;if(markAfter){markOffset+=match[0].length;}
126126 context.modules.highlight.tokenArray.push(new Token(match.index+oldOffset+markOffset,label));oldOffset+=match.index+match[0].length;newSubstring=text.substring(oldOffset);match=newSubstring.match(regex);}}}}
127 -return context.modules.highlight.tokenArray;},mark:function(context,division,tokens){var rawText=context.fn.getContents();for(module in $.wikiEditor.modules){if('evt'in $.wikiEditor.modules[module]&&'mark'in $.wikiEditor.modules[module].evt){$.wikiEditor.modules[module].evt.mark();}}
128 -markedText='';var previousIndex=0;for(var currentIndex in context.modules.highlight.markers){markedText+=rawText.substring(previousIndex,currentIndex);for(var i=0;i<context.modules.highlight.markers[currentIndex].length;i++){markedText+=context.modules.highlight.markers[currentIndex][i];}
129 -previousIndex=currentIndex;}
130 -if(markedText!=''){markedText.replace(/\n/g,'<br\>');context.fn.setContents({contents:markedText});}}}};})(jQuery);(function($){$.wikiEditor.modules.preview={fn:{create:function(context,config){if('initialized'in context.modules.preview){return;}
 127+context.modules.highlight.tokenArray.sort(function(a,b){return a.offset-b.offset;});return context.modules.highlight.tokenArray;},mark:function(context,division,tokens){context.modules.highlight.markers=[];for(module in $.wikiEditor.modules){if('evt'in $.wikiEditor.modules[module]&&'mark'in $.wikiEditor.modules[module].evt){$.wikiEditor.modules[module].evt.mark(context);}}
 128+var markers=context.modules.highlight.markers;markers.sort(function(a,b){return a.start-b.start||a.end-b.end;});var pos=0;var node=context.$content.get(0);var next=null;var i=0;var startNode=null;var depth=0,nextDepth=0,startDepth=null;while(node.firstChild){node=node.firstChild;depth++;}
 129+while(i<markers.length&&node){var p=node;nextDepth=depth;while(p&&!p.nextSibling){p=p.parentNode;nextDepth--;}
 130+p=p?p.nextSibling:null;while(p&&p.firstChild){p=p.firstChild;nextDepth++;}
 131+next=p;if(node.nodeName!='#text'){if(node.nodeName=='BR')
 132+pos++;node=next;depth=nextDepth;continue;}
 133+var newPos=pos+node.nodeValue.length;if(!startNode&&markers[i].start>=pos&&markers[i].start<newPos){if(markers[i].start>pos){node=node.splitText(markers[i].start-pos);pos=markers[i].start;}
 134+startNode=node;startDepth=depth;}
 135+if(startNode&&markers[i].end>pos&&markers[i].end<=newPos){if(markers[i].end<newPos){next=node.splitText(node.nodeValue.length-newPos+markers[i].end);newPos=markers[i].end;}
 136+var ca1=startNode,ca2=node;if(startDepth>depth){for(var j=0;j<startDepth-depth&&ca1;j++){ca1=ca1.parentNode;}}
 137+else if(startDepth<depth){for(var j=0;j<depth-startDepth&&ca2;j++){ca2=ca2.parentNode;}}
 138+while(ca1&&ca2&&ca1.parentNode!=ca2.parentNode){if(ca1.parentNode.firstChild!=ca1||ca2.parentNode.lastChild!=ca2){ca1=ca2=null;}else{ca1=ca1.parentNode;ca2=ca2.parentNode;}}
 139+if(ca1&&ca2){var newNode=markers[i].wrapElement;if(typeof newNode=='function')
 140+newNode=newNode();if(newNode.jquery)
 141+newNode=newNode.get(0);var commonAncestor=ca1.parentNode;var nextNode=ca2.nextSibling;var n=ca1;while(n!=nextNode){var ns=n.nextSibling;newNode.appendChild(n);n=ns;}
 142+if(nextNode)
 143+commonAncestor.insertBefore(newNode,nextNode);else
 144+commonAncestor.appendChild(newNode);}
 145+startNode=null;startDepth=null;i++;}
 146+pos=newPos;node=next;depth=nextDepth;}}}};})(jQuery);(function($){$.wikiEditor.modules.preview={fn:{create:function(context,config){if('initialized'in context.modules.preview){return;}
131147 context.modules.preview={'initialized':true,'previewText':null,'changesText':null};context.modules.preview.$preview=context.fn.addView({'name':'preview','titleMsg':'wikieditor-preview-tab','init':function(context){var wikitext=context.fn.getContents();if(context.modules.preview.previewText==wikitext){return;}
132148 context.modules.preview.$preview.find('.wikiEditor-preview-contents').empty();context.modules.preview.$preview.find('.wikiEditor-preview-loading').show();$.post(wgScriptPath+'/api.php',{'action':'parse','title':wgPageName,'text':wikitext,'prop':'text','pst':'','format':'json'},function(data){if(typeof data.parse=='undefined'||typeof data.parse.text=='undefined'||typeof data.parse.text['*']=='undefined'){return;}
133149 context.modules.preview.previewText=wikitext;context.modules.preview.$preview.find('.wikiEditor-preview-loading').hide();context.modules.preview.$preview.find('.wikiEditor-preview-contents').html(data.parse.text['*']).find('a:not([href^=#])').click(function(){return false;});},'json');}});context.$changesTab=context.fn.addView({'name':'changes','titleMsg':'wikieditor-preview-changes-tab','init':function(context){var wikitext=context.fn.getContents();if(context.modules.preview.changesText==wikitext){return;}
@@ -158,9 +174,7 @@
159175 $('#wikiEditor-'+context.instance+'-dialog-watch').hide();else if($('#wpWatchthis').is(':checked'))
160176 $('#wikiEditor-'+context.instance+'-dialog-watch').attr('checked','checked');$(this).find('form').submit(function(e){$(this).closest('.ui-dialog').find('button:first').click();e.preventDefault();});},dialog:{buttons:{'wikieditor-publish-dialog-publish':function(){var minorChecked=$('#wikiEditor-'+context.instance+'-dialog-minor').is(':checked')?'checked':'';var watchChecked=$('#wikiEditor-'+context.instance+'-dialog-watch').is(':checked')?'checked':'';$('#wpMinoredit').attr('checked',minorChecked);$('#wpWatchthis').attr('checked',watchChecked);$('#wpSummary').val($j('#wikiEditor-'+context.instance+'-dialog-summary').val());$('#editform').submit();},'wikieditor-publish-dialog-goback':function(){$(this).dialog('close');}},open:function(){$('#wikiEditor-'+context.instance+'-dialog-summary').focus();},width:500},resizeme:false}});context.fn.addButton({'captionMsg':'wikieditor-publish-button-publish','action':function(){$('#'+dialogID).dialog('open');return false;}});context.fn.addButton({'captionMsg':'wikieditor-publish-button-cancel','action':function(){}});}}};})(jQuery);(function($){$.wikiEditor.modules.templateEditor={evt:{mark:function(context,event){var i=0;function findOutermostTemplates(tokenStack){var templateBeginFound=false;for(;i<tokenStack.length;i++){if(tokenStack[i].label=='TEMPLATE_BEGIN'){templateBeginFound=true;break;}}
161177 var j=i++;if(!templateBeginFound){return false;}else{var nestedBegins=1;while(nestedBegins>0&&j<tokenStack.length){var label=tokenStack[++j].label;nestedBegins+=label=='TEMPLATE_END'?-1:label=='TEMPLATE_BEGIN'?1:0;}
162 -if(nestedBegins==0){var leftMarker=i-1;var rightMarker=j;i=j;return[leftMarker,rightMarker];}else{return false;}}};var markers=context.modules.highlight.data.markers;var tokenStack=context.modules.highlight.data.tokenArray;var templateBeginFound=false;var templateBoundaries;while(templateBoundaries=findOutermostTemplates(tokenStack)){if(typeof markers[tokenStack[templateBoundaries[0]].offset]=='undefined'){markers[tokenStack[templateBoundaries[0]].offset]=[];}
163 -if(typeof markers[tokenStack[templateBoundaries[1]].offset]=='undefined'){markers[tokenStack[templateBoundaries[1]].offset]=[];}
164 -markers[tokenStack[templateBoundaries[0]].offset].push("<div class='wiki-template'>");markers[tokenStack[templateBoundaries[1]].offset].push("</div>");}}},exp:[{'regex':/{{/,'label':"TEMPLATE_BEGIN"},{'regex':/}}/,'label':"TEMPLATE_END",'markAfter':true}],fn:{create:function(context,config){},model:function(wikitext){function Param(name,value,number,nameIndex,equalsIndex,valueIndex){this.name=name;this.value=value;this.number=number;this.nameIndex=nameIndex;this.equalsIndex=equalsIndex;this.valueIndex=valueIndex;}
 178+if(nestedBegins==0){var leftMarker=i-1;var rightMarker=j;i=j;return[leftMarker,rightMarker];}else{return false;}}};var markers=context.modules.highlight.markers;var tokenStack=context.modules.highlight.tokenArray;var templateBeginFound=false;var templateBoundaries;while(templateBoundaries=findOutermostTemplates(tokenStack)){context.modules.highlight.markers.push({start:tokenStack[templateBoundaries[0]].offset,end:tokenStack[templateBoundaries[1]].offset,wrapElement:function(){return $('<div />').addClass('wikiEditor-highlight-template');}});}}},exp:[{'regex':/{{/,'label':"TEMPLATE_BEGIN"},{'regex':/}}/,'label':"TEMPLATE_END",'markAfter':true}],fn:{create:function(context,config){},model:function(wikitext){function Param(name,value,number,nameIndex,equalsIndex,valueIndex){this.name=name;this.value=value;this.number=number;this.nameIndex=nameIndex;this.equalsIndex=equalsIndex;this.valueIndex=valueIndex;}
165179 function Range(begin,end){this.begin=begin;this.end=end;}
166180 function getSetValue(name,value,original){var valueRange;var rangeIndex;var retVal;if(isNaN(name)){if(typeof paramsByName[name]=='undefined'){return"";}
167181 rangeIndex=paramsByName[name];}else{rangeIndex=parseInt(name);}

Status & tagging log