r74276 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r74275‎ | r74276 | r74277 >
Date:21:08, 4 October 2010
Author:tparscal
Status:ok
Tags:
Comment:
Got more dialogs working again
Modified paths:
  • /trunk/extensions/WikiEditor/WikiEditor.hooks.php (modified) (history)
  • /trunk/extensions/WikiEditor/WikiEditor.i18n.php (modified) (history)
  • /trunk/extensions/WikiEditor/WikiEditor.php (modified) (history)
  • /trunk/extensions/WikiEditor/modules/ext.wikiEditor.dialogs.js (modified) (history)
  • /trunk/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.js (modified) (history)
  • /trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js (modified) (history)

Diff [purge]

Index: trunk/extensions/WikiEditor/WikiEditor.php
@@ -26,20 +26,18 @@
2727 // Adds a tab for previewing in-line
2828 'preview' => array( 'global' => false, 'user' => true ),
2929 // Adds a button for previewing in a dialog
30 - 'previewDialog' => array( 'global' => false, 'user' => true ),
 30+ 'previewDialog' => array( 'global' => false, 'user' => false ),
3131 // Adds a button and dialog for step-by-step publishing
3232 'publish' => array( 'global' => false, 'user' => true ),
3333
3434 /* I-frame dependent (do not deploy!) */
3535
36 - // Experimental wikitext parsing/syntax highlight
37 - 'highlight' => array( 'global' => false, 'user' => true ),
3836 // Failry stable table of contents
3937 'toc' => array( 'global' => false, 'user' => true ),
4038 // Pretty broken template collapsing/editing
41 - 'templateEditor' => array( 'global' => false, 'user' => true ),
 39+ 'templateEditor' => array( 'global' => false, 'user' => false ),
4240 // Bare-bones (probably broken) template collapsing
43 - 'templates' => array( 'global' => false, 'user' => true ),
 41+ 'templates' => array( 'global' => false, 'user' => false ),
4442
4543 /* Unknown status */
4644
Index: trunk/extensions/WikiEditor/WikiEditor.hooks.php
@@ -28,7 +28,6 @@
2929 'jquery.client',
3030 'jquery.textSelection',
3131 'jquery.delayedBind',
32 - 'contentCollector',
3332 ),
3433 'messages' => array(
3534 'wikieditor-wikitext-tab',
@@ -38,7 +37,10 @@
3938 ),
4039 'jquery.wikiEditor.iframe' => array(
4140 'scripts' => 'extensions/WikiEditor/modules/jquery.wikiEditor.iframe.js',
42 - 'dependencies' => 'jquery.wikiEditor',
 41+ 'dependencies' => array(
 42+ 'jquery.wikiEditor',
 43+ 'contentCollector',
 44+ ),
4345 'group' => 'ext.wikiEditor',
4446 ),
4547 'jquery.wikiEditor.dialogs' => array(
@@ -137,7 +139,8 @@
138140 'dependencies' => array(
139141 'ext.wikiEditor',
140142 'ext.wikiEditor.toolbar',
141 - // jquery.wikiEditor.dialogs and jquery.suggestions will be loaded on the fly
 143+ 'jquery.wikiEditor.dialogs',
 144+ 'jquery.suggestions',
142145 ),
143146 'group' => 'ext.wikiEditor',
144147 ),
@@ -197,6 +200,7 @@
198201 'scripts' => 'extensions/WikiEditor/modules/ext.wikiEditor.templateEditor.js',
199202 'dependencies' => array(
200203 'ext.wikiEditor',
 204+ 'ext.wikiEditor.highlight',
201205 'jquery.wikiEditor.templateEditor',
202206 ),
203207 'messages' => array(
@@ -210,6 +214,7 @@
211215 'scripts' => 'extensions/WikiEditor/modules/ext.wikiEditor.templates.js',
212216 'dependencies' => array(
213217 'ext.wikiEditor',
 218+ 'ext.wikiEditor.highlight',
214219 'jquery.wikiEditor.templates',
215220 ),
216221 'group' => 'ext.wikiEditor',
@@ -218,6 +223,7 @@
219224 'scripts' => 'extensions/WikiEditor/modules/ext.wikiEditor.toc.js',
220225 'dependencies' => array(
221226 'ext.wikiEditor',
 227+ 'ext.wikiEditor.highlight',
222228 'jquery.wikiEditor.toc',
223229 ),
224230 'messages' => array(
@@ -478,21 +484,6 @@
479485
480486 /* Labs Features */
481487
482 - 'highlight' => array(
483 - 'preferences' => array(
484 - 'wikieditor-highlight' => array(
485 - 'type' => 'toggle',
486 - 'label-message' => 'wikieditor-highlight-preference',
487 - 'section' => 'editing/labs',
488 - ),
489 - ),
490 - 'requirements' => array(
491 - 'wikieditor-highlight' => true,
492 - ),
493 - 'modules' => array(
494 - 'ext.wikiEditor.highlight',
495 - ),
496 - ),
497488 'templateEditor' => array(
498489 'preferences' => array(
499490 'wikieditor-template-editor' => array(
Index: trunk/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js
@@ -59,7 +59,7 @@
6060 );
6161 for ( tool in data[type] ) {
6262 // Tool
63 - $group.append( $.wikiEditor.modules.toolbar.fn.buildTool( context, tool,data[type][tool] ) );
 63+ $group.append( $.wikiEditor.modules.toolbar.fn.buildTool( context, tool, data[type][tool] ) );
6464 }
6565 if ( $group.children().length ) {
6666 $group.show();
Index: trunk/extensions/WikiEditor/modules/ext.wikiEditor.dialogs.js
@@ -3,1115 +3,1146 @@
44 */
55
66 $( document ).ready( function() {
 7+ /*
78 if ( !$.wikiEditor.isSupported( $.wikiEditor.modules.dialogs ) ) {
89 return;
910 }
10 - mediaWiki.loader.using( [ 'jquery.wikiEditor.dialogs', 'jquery.suggestions' ], function() {
11 - // Replace icons
12 - $( '#wpTextbox1' )
13 - .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'xlink' } )
14 - .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'ilink' } )
15 - .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'reference' } )
16 - .wikiEditor( 'addToToolbar', {
17 - 'section': 'main',
18 - 'group': 'insert',
19 - 'tools': {
20 - 'link': {
21 - 'labelMsg': 'wikieditor-toolbar-tool-link',
22 - 'type': 'button',
23 - 'icon': 'insert-link.png',
24 - 'offset': [2, -1654],
25 - 'action': {
26 - 'type': 'dialog',
27 - 'module': 'insert-link'
 11+ */
 12+ // Replace icons
 13+ $( '#wpTextbox1' )
 14+ .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'xlink' } )
 15+ .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'ilink' } )
 16+ .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'reference' } )
 17+ .wikiEditor( 'removeFromToolbar', { 'section': 'advanced', 'group': 'insert', 'tool': 'table' } )
 18+ .wikiEditor( 'addToToolbar', {
 19+ 'section': 'main',
 20+ 'group': 'insert',
 21+ 'tools': {
 22+ 'link': {
 23+ 'labelMsg': 'wikieditor-toolbar-tool-link',
 24+ 'type': 'button',
 25+ 'icon': 'insert-link.png',
 26+ 'offset': [2, -1654],
 27+ 'action': {
 28+ 'type': 'dialog',
 29+ 'module': 'insert-link'
 30+ }
 31+ },
 32+ 'reference': {
 33+ 'labelMsg': 'wikieditor-toolbar-tool-reference',
 34+ 'type': 'button',
 35+ 'icon': 'insert-reference.png',
 36+ 'offset': [2, -1798],
 37+ 'action': {
 38+ 'type': 'dialog',
 39+ 'module': 'insert-reference'
 40+ }
 41+ }
 42+ }
 43+ } )
 44+ .wikiEditor( 'addToToolbar', {
 45+ 'section': 'advanced',
 46+ 'group': 'insert',
 47+ 'tools': {
 48+ 'table': {
 49+ 'labelMsg': 'wikieditor-toolbar-tool-table',
 50+ 'type': 'button',
 51+ 'icon': 'insert-table.png',
 52+ 'offset': [2, -1942],
 53+ 'action': {
 54+ 'type': 'dialog',
 55+ 'module': 'insert-table'
 56+ }
 57+ }
 58+ }
 59+ } )
 60+ .wikiEditor( 'addToToolbar', {
 61+ 'section': 'advanced',
 62+ 'groups': {
 63+ 'search': {
 64+ 'tools': {
 65+ 'replace': {
 66+ 'labelMsg': 'wikieditor-toolbar-tool-replace',
 67+ 'type': 'button',
 68+ 'icon': 'search-replace.png',
 69+ 'offset': [-70, -214],
 70+ 'action': {
 71+ 'type': 'dialog',
 72+ 'module': 'search-and-replace'
 73+ }
2874 }
29 - },
30 - 'reference': {
31 - 'labelMsg': 'wikieditor-toolbar-tool-reference',
32 - 'type': 'button',
33 - 'icon': 'insert-reference.png',
34 - 'offset': [2, -1798],
35 - 'action': {
36 - 'type': 'dialog',
37 - 'module': 'insert-reference'
38 - }
3975 }
4076 }
41 - } );
42 -
43 - // Add dialogs module
44 - $( '#wpTextbox1' ).wikiEditor( 'addModule', { 'dialogs': {
45 - 'insert-link': {
46 - filters: [ '#wpTextbox1.toolbar-dialogs' ],
47 - titleMsg: 'wikieditor-toolbar-tool-link-title',
48 - id: 'wikieditor-toolbar-link-dialog',
49 - html: '\
50 - <fieldset>\
51 - <div class="wikieditor-toolbar-field-wrapper">\
52 - <label for="wikieditor-toolbar-link-int-target" rel="wikieditor-toolbar-tool-link-int-target" id="wikieditor-toolbar-tool-link-int-target-label"></label>\
53 - <div id="wikieditor-toolbar-link-int-target-status"></div>\
54 - <input type="text" id="wikieditor-toolbar-link-int-target" />\
 77+ }
 78+ } );
 79+
 80+ // Add dialogs module
 81+ $( '#wpTextbox1' ).wikiEditor( 'addModule', { 'dialogs': {
 82+ 'insert-link': {
 83+ titleMsg: 'wikieditor-toolbar-tool-link-title',
 84+ id: 'wikieditor-toolbar-link-dialog',
 85+ html: '\
 86+ <fieldset>\
 87+ <div class="wikieditor-toolbar-field-wrapper">\
 88+ <label for="wikieditor-toolbar-link-int-target" rel="wikieditor-toolbar-tool-link-int-target" id="wikieditor-toolbar-tool-link-int-target-label"></label>\
 89+ <div id="wikieditor-toolbar-link-int-target-status"></div>\
 90+ <input type="text" id="wikieditor-toolbar-link-int-target" />\
 91+ </div>\
 92+ <div class="wikieditor-toolbar-field-wrapper">\
 93+ <label for="wikieditor-toolbar-link-int-text" rel="wikieditor-toolbar-tool-link-int-text"></label>\
 94+ <input type="text" id="wikieditor-toolbar-link-int-text" />\
 95+ </div>\
 96+ <div class="wikieditor-toolbar-field-wrapper">\
 97+ <div class="wikieditor-toolbar-floated-field-wrapper">\
 98+ <input type="radio" id="wikieditor-toolbar-link-type-int" name="wikieditor-toolbar-link-type" selected />\
 99+ <label for="wikieditor-toolbar-link-type-int" rel="wikieditor-toolbar-tool-link-int"></label>\
55100 </div>\
56 - <div class="wikieditor-toolbar-field-wrapper">\
57 - <label for="wikieditor-toolbar-link-int-text" rel="wikieditor-toolbar-tool-link-int-text"></label>\
58 - <input type="text" id="wikieditor-toolbar-link-int-text" />\
 101+ <div class="wikieditor-toolbar-floated-field-wrapper">\
 102+ <input type="radio" id="wikieditor-toolbar-link-type-ext" name="wikieditor-toolbar-link-type" />\
 103+ <label for="wikieditor-toolbar-link-type-ext" rel="wikieditor-toolbar-tool-link-ext"></label>\
59104 </div>\
60 - <div class="wikieditor-toolbar-field-wrapper">\
61 - <div class="wikieditor-toolbar-floated-field-wrapper">\
62 - <input type="radio" id="wikieditor-toolbar-link-type-int" name="wikieditor-toolbar-link-type" selected />\
63 - <label for="wikieditor-toolbar-link-type-int" rel="wikieditor-toolbar-tool-link-int"></label>\
64 - </div>\
65 - <div class="wikieditor-toolbar-floated-field-wrapper">\
66 - <input type="radio" id="wikieditor-toolbar-link-type-ext" name="wikieditor-toolbar-link-type" />\
67 - <label for="wikieditor-toolbar-link-type-ext" rel="wikieditor-toolbar-tool-link-ext"></label>\
68 - </div>\
69 - </div>\
70 - </fieldset>',
71 - init: function() {
72 - function isExternalLink( s ) {
73 - // The following things are considered to be external links:
74 - // * Starts a URL protocol
75 - // * Starts with www.
76 - // All of these are potentially valid titles, and the latter two categories match about 6300
77 - // titles in enwiki's ns0. Out of 6.9M titles, that's 0.09%
78 - if ( typeof arguments.callee.regex == 'undefined' ) {
79 - // Cache the regex
80 - arguments.callee.regex =
81 - new RegExp( "^(" + wgUrlProtocols + "|www\\.)", 'i');
82 - }
83 - return s.match( arguments.callee.regex );
 105+ </div>\
 106+ </fieldset>',
 107+ init: function() {
 108+ function isExternalLink( s ) {
 109+ // The following things are considered to be external links:
 110+ // * Starts a URL protocol
 111+ // * Starts with www.
 112+ // All of these are potentially valid titles, and the latter two categories match about 6300
 113+ // titles in enwiki's ns0. Out of 6.9M titles, that's 0.09%
 114+ if ( typeof arguments.callee.regex == 'undefined' ) {
 115+ // Cache the regex
 116+ arguments.callee.regex =
 117+ new RegExp( "^(" + wgUrlProtocols + "|www\\.)", 'i');
84118 }
85 - // Updates the status indicator above the target link
86 - function updateWidget( status ) {
87 - $( '#wikieditor-toolbar-link-int-target-status' ).children().hide();
88 - $( '#wikieditor-toolbar-link-int-target' ).parent()
89 - .removeClass(
90 - 'status-invalid status-external status-notexists status-exists status-loading'
91 - );
92 - if ( status ) {
93 - $( '#wikieditor-toolbar-link-int-target-status-' + status ).show();
94 - $( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status );
95 - }
96 - if ( status == 'invalid' ) {
97 - $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
98 - .attr( 'disabled', true )
99 - .addClass( 'disabled' );
100 - } else {
101 - $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
102 - .removeAttr('disabled')
103 - .removeClass('disabled');
104 - }
 119+ return s.match( arguments.callee.regex );
 120+ }
 121+ // Updates the status indicator above the target link
 122+ function updateWidget( status ) {
 123+ $( '#wikieditor-toolbar-link-int-target-status' ).children().hide();
 124+ $( '#wikieditor-toolbar-link-int-target' ).parent()
 125+ .removeClass(
 126+ 'status-invalid status-external status-notexists status-exists status-loading'
 127+ );
 128+ if ( status ) {
 129+ $( '#wikieditor-toolbar-link-int-target-status-' + status ).show();
 130+ $( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status );
105131 }
106 - // Updates the UI to show if the page title being inputed by the user exists or not
107 - // accepts parameter internal for bypassing external link detection
108 - function updateExistence( internal ) {
109 - // ensure the internal parameter is a boolean
110 - if ( internal != true ) internal = false;
 132+ if ( status == 'invalid' ) {
 133+ $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
 134+ .attr( 'disabled', true )
 135+ .addClass( 'disabled' );
 136+ } else {
 137+ $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
 138+ .removeAttr('disabled')
 139+ .removeClass('disabled');
 140+ }
 141+ }
 142+ // Updates the UI to show if the page title being inputed by the user exists or not
 143+ // accepts parameter internal for bypassing external link detection
 144+ function updateExistence( internal ) {
 145+ // ensure the internal parameter is a boolean
 146+ if ( internal != true ) internal = false;
 147+ // Abort previous request
 148+ var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
 149+ if ( request ) {
 150+ request.abort();
 151+ }
 152+ var target = $( '#wikieditor-toolbar-link-int-target' ).val();
 153+ var cache = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'existencecache' );
 154+ if ( cache[target] ) {
 155+ updateWidget( cache[target] );
 156+ return;
 157+ }
 158+ if ( target.replace( /^\s+$/,'' ) == '' ) {
 159+ // Hide the widget when the textbox is empty
 160+ updateWidget( false );
 161+ return;
 162+ }
 163+ // If the forced internal paremter was not true, check if the target is an external link
 164+ if ( !internal && isExternalLink( target ) ) {
 165+ updateWidget( 'external' );
 166+ return;
 167+ }
 168+ if ( target.indexOf( '|' ) != -1 ) {
 169+ // Title contains | , which means it's invalid
 170+ // but confuses the API. Show invalid and bypass API
 171+ updateWidget( 'invalid' );
 172+ return;
 173+ }
 174+ // Show loading spinner while waiting for the API to respond
 175+ updateWidget( 'loading' );
 176+ // Call the API to check page status, saving the request object so it can be aborted if
 177+ // necessary
 178+ $( '#wikieditor-toolbar-link-int-target-status' ).data(
 179+ 'request',
 180+ $.ajax( {
 181+ url: wgScriptPath + '/api.php',
 182+ dataType: 'json',
 183+ data: {
 184+ 'action': 'query',
 185+ 'indexpageids': '',
 186+ 'titles': target,
 187+ 'converttitles': '',
 188+ 'format': 'json'
 189+ },
 190+ success: function( data ) {
 191+ var status;
 192+ if ( !data || typeof data.query == 'undefined' ) {
 193+ // This happens in some weird cases
 194+ status = false;
 195+ } else {
 196+ var page = data.query.pages[data.query.pageids[0]];
 197+ status = 'exists';
 198+ if ( typeof page.missing != 'undefined' )
 199+ status = 'notexists';
 200+ else if ( typeof page.invalid != 'undefined' )
 201+ status = 'invalid';
 202+ }
 203+ // Cache the status of the link target if the force internal parameter was not
 204+ // passed
 205+ if ( !internal ) cache[target] = status;
 206+ updateWidget( status );
 207+ }
 208+ } )
 209+ );
 210+ }
 211+ $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).click( function() {
 212+ if( $( '#wikieditor-toolbar-link-type-ext' ).is( ':checked' ) ) {
111213 // Abort previous request
112214 var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
113215 if ( request ) {
114216 request.abort();
115217 }
116 - var target = $( '#wikieditor-toolbar-link-int-target' ).val();
117 - var cache = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'existencecache' );
118 - if ( cache[target] ) {
119 - updateWidget( cache[target] );
120 - return;
 218+ updateWidget( 'external' );
 219+ }
 220+ if( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) )
 221+ updateExistence( true );
 222+ });
 223+ // Set labels of tabs based on rel values
 224+ var msg = mediaWiki.msg;
 225+ $(this).find( '[rel]' ).each( function() {
 226+ $(this).text( msg.get( $(this).attr( 'rel' ) ) );
 227+ });
 228+ // Set tabindexes on form fields
 229+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
 230+ // Setup the tooltips in the textboxes
 231+ $( '#wikieditor-toolbar-link-int-target' )
 232+ .data( 'tooltip', msg.get( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) );
 233+ $( '#wikieditor-toolbar-link-int-text' )
 234+ .data( 'tooltip', msg.get( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) );
 235+ $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' )
 236+ .each( function() {
 237+ var tooltip = msg.get( $( this ).attr( 'id' ) + '-tooltip' );
 238+ if ( $( this ).val() == '' )
 239+ $( this )
 240+ .addClass( 'wikieditor-toolbar-dialog-hint' )
 241+ .val( $( this ).data( 'tooltip' ) )
 242+ .data( 'tooltip-mode', true );
 243+ } )
 244+ .focus( function() {
 245+ if( $( this ).val() == $( this ).data( 'tooltip' ) ) {
 246+ $( this )
 247+ .val( '' )
 248+ .removeClass( 'wikieditor-toolbar-dialog-hint' )
 249+ .data( 'tooltip-mode', false );
121250 }
122 - if ( target.replace( /^\s+$/,'' ) == '' ) {
123 - // Hide the widget when the textbox is empty
124 - updateWidget( false );
125 - return;
 251+ })
 252+ .bind( 'change', function() {
 253+ if ( $( this ).val() != $( this ).data( 'tooltip' ) ) {
 254+ $( this )
 255+ .removeClass( 'wikieditor-toolbar-dialog-hint' )
 256+ .data( 'tooltip-mode', false );
126257 }
127 - // If the forced internal paremter was not true, check if the target is an external link
128 - if ( !internal && isExternalLink( target ) ) {
 258+ })
 259+ .bind( 'blur', function() {
 260+ if ( $( this ).val() == '' ) {
 261+ $( this )
 262+ .addClass( 'wikieditor-toolbar-dialog-hint' )
 263+ .val( $( this ).data( 'tooltip' ) )
 264+ .data( 'tooltip-mode', true );
 265+ }
 266+ });
 267+
 268+ // Automatically copy the value of the internal link page title field to the link text field unless the
 269+ // user has changed the link text field - this is a convenience thing since most link texts are going to
 270+ // be the the same as the page title - Also change the internal/external radio button accordingly
 271+ $( '#wikieditor-toolbar-link-int-target' ).bind( 'change keydown paste cut', function() {
 272+ // $(this).val() is the old value, before the keypress - Defer this until $(this).val() has
 273+ // been updated
 274+ setTimeout( function() {
 275+ if ( isExternalLink( $( '#wikieditor-toolbar-link-int-target' ).val() ) ) {
 276+ $( '#wikieditor-toolbar-link-type-ext' ).attr( 'checked', 'checked' );
129277 updateWidget( 'external' );
 278+ } else {
 279+ $( '#wikieditor-toolbar-link-type-int' ).attr( 'checked', 'checked' );
 280+ updateExistence();
 281+ }
 282+ if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched' ) )
 283+ if ( $( '#wikieditor-toolbar-link-int-target' ).val() ==
 284+ $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip' ) ) {
 285+ $( '#wikieditor-toolbar-link-int-text' )
 286+ .addClass( 'wikieditor-toolbar-dialog-hint' )
 287+ .val( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip' ) )
 288+ .change();
 289+ } else {
 290+ $( '#wikieditor-toolbar-link-int-text' )
 291+ .val( $( '#wikieditor-toolbar-link-int-target' ).val() )
 292+ .change();
 293+ }
 294+ }, 0 );
 295+ });
 296+ $( '#wikieditor-toolbar-link-int-text' ).bind( 'change keydown paste cut', function() {
 297+ var oldVal = $(this).val();
 298+ var that = this;
 299+ setTimeout( function() {
 300+ if ( $(that).val() != oldVal )
 301+ $(that).data( 'untouched', false );
 302+ }, 0 );
 303+ });
 304+ // Add images to the page existence widget, which will be shown mutually exclusively to communicate if
 305+ // the page exists, does not exist or the title is invalid (like if it contains a | character)
 306+ var existsMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-exists' );
 307+ var notexistsMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-notexists' );
 308+ var invalidMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-invalid' );
 309+ var externalMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-external' );
 310+ var loadingMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-loading' );
 311+ $( '#wikieditor-toolbar-link-int-target-status' )
 312+ .append( $( '<div />' )
 313+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-exists' )
 314+ .append( existsMsg )
 315+ )
 316+ .append( $( '<div />' )
 317+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-notexists' )
 318+ .append( notexistsMsg )
 319+ )
 320+ .append( $( '<div />' )
 321+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-invalid' )
 322+ .append( invalidMsg )
 323+ )
 324+ .append( $( '<div />' )
 325+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-external' )
 326+ .append( externalMsg )
 327+ )
 328+ .append( $( '<div />' )
 329+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-loading' )
 330+ .append( $( '<img />' ).attr( {
 331+ 'src': $.wikiEditor.imgPath + 'dialogs/' + 'loading.gif',
 332+ 'alt': loadingMsg,
 333+ 'title': loadingMsg
 334+ } ) )
 335+ )
 336+ .data( 'existencecache', {} )
 337+ .children().hide();
 338+
 339+ $( '#wikieditor-toolbar-link-int-target' )
 340+ .bind( 'keyup paste cut', function() {
 341+ // Cancel the running timer if applicable
 342+ if ( typeof $(this).data( 'timerID' ) != 'undefined' ) {
 343+ clearTimeout( $(this).data( 'timerID' ) );
 344+ }
 345+ // Delay fetch for a while
 346+ // FIXME: Make 120 configurable elsewhere
 347+ var timerID = setTimeout( updateExistence, 120 );
 348+ $(this).data( 'timerID', timerID );
 349+ } )
 350+ .change( function() {
 351+ // Cancel the running timer if applicable
 352+ if ( typeof $(this).data( 'timerID' ) != 'undefined' ) {
 353+ clearTimeout( $(this).data( 'timerID' ) );
 354+ }
 355+ // Fetch right now
 356+ updateExistence();
 357+ } );
 358+
 359+ // Title suggestions
 360+ $( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( {
 361+ fetch: function( query ) {
 362+ var that = this;
 363+ var title = $(this).val();
 364+
 365+ if ( isExternalLink( title ) || title.indexOf( '|' ) != -1 || title == '') {
 366+ $(this).suggestions( 'suggestions', [] );
130367 return;
131368 }
132 - if ( target.indexOf( '|' ) != -1 ) {
133 - // Title contains | , which means it's invalid
134 - // but confuses the API. Show invalid and bypass API
135 - updateWidget( 'invalid' );
 369+
 370+ var cache = $(this).data( 'suggcache' );
 371+ if ( typeof cache[title] != 'undefined' ) {
 372+ $(this).suggestions( 'suggestions', cache[title] );
136373 return;
137374 }
138 - // Show loading spinner while waiting for the API to respond
139 - updateWidget( 'loading' );
140 - // Call the API to check page status, saving the request object so it can be aborted if
141 - // necessary
142 - $( '#wikieditor-toolbar-link-int-target-status' ).data(
143 - 'request',
144 - $.ajax( {
145 - url: wgScriptPath + '/api.php',
146 - dataType: 'json',
147 - data: {
148 - 'action': 'query',
149 - 'indexpageids': '',
150 - 'titles': target,
151 - 'converttitles': '',
152 - 'format': 'json'
153 - },
154 - success: function( data ) {
155 - var status;
156 - if ( !data || typeof data.query == 'undefined' ) {
157 - // This happens in some weird cases
158 - status = false;
159 - } else {
160 - var page = data.query.pages[data.query.pageids[0]];
161 - status = 'exists';
162 - if ( typeof page.missing != 'undefined' )
163 - status = 'notexists';
164 - else if ( typeof page.invalid != 'undefined' )
165 - status = 'invalid';
166 - }
167 - // Cache the status of the link target if the force internal parameter was not
168 - // passed
169 - if ( !internal ) cache[target] = status;
170 - updateWidget( status );
171 - }
172 - } )
173 - );
 375+
 376+ var request = $.ajax( {
 377+ url: wgScriptPath + '/api.php',
 378+ data: {
 379+ 'action': 'opensearch',
 380+ 'search': title,
 381+ 'namespace': 0,
 382+ 'suggest': '',
 383+ 'format': 'json'
 384+ },
 385+ dataType: 'json',
 386+ success: function( data ) {
 387+ cache[title] = data[1];
 388+ $(that).suggestions( 'suggestions', data[1] );
 389+ }
 390+ });
 391+ $(this).data( 'request', request );
 392+ },
 393+ cancel: function() {
 394+ var request = $(this).data( 'request' );
 395+ if ( request )
 396+ request.abort();
174397 }
175 - $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).click( function() {
176 - if( $( '#wikieditor-toolbar-link-type-ext' ).is( ':checked' ) ) {
177 - // Abort previous request
178 - var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
179 - if ( request ) {
180 - request.abort();
181 - }
182 - updateWidget( 'external' );
 398+ });
 399+ },
 400+ dialog: {
 401+ width: 500,
 402+ dialogClass: 'wikiEditor-toolbar-dialog',
 403+ buttons: {
 404+ 'wikieditor-toolbar-tool-link-insert': function() {
 405+ function escapeInternalText( s ) {
 406+ // FIXME: Should this escape [[ too? Seems to work without that
 407+ return s.replace( /(]{2,})/g, '<nowiki>$1</nowiki>' );
183408 }
184 - if( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) )
185 - updateExistence( true );
186 - });
187 - // Set labels of tabs based on rel values
188 - var msg = mediaWiki.msg;
189 - $(this).find( '[rel]' ).each( function() {
190 - $(this).text( msg.get( $(this).attr( 'rel' ) ) );
191 - });
192 - // Set tabindexes on form fields
193 - $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
194 - // Setup the tooltips in the textboxes
195 - $( '#wikieditor-toolbar-link-int-target' )
196 - .data( 'tooltip', msg.get( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) );
197 - $( '#wikieditor-toolbar-link-int-text' )
198 - .data( 'tooltip', msg.get( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) );
199 - $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' )
200 - .each( function() {
201 - var tooltip = msg.get( $( this ).attr( 'id' ) + '-tooltip' );
202 - if ( $( this ).val() == '' )
203 - $( this )
204 - .addClass( 'wikieditor-toolbar-dialog-hint' )
205 - .val( $( this ).data( 'tooltip' ) )
206 - .data( 'tooltip-mode', true );
207 - } )
208 - .focus( function() {
209 - if( $( this ).val() == $( this ).data( 'tooltip' ) ) {
210 - $( this )
211 - .val( '' )
212 - .removeClass( 'wikieditor-toolbar-dialog-hint' )
213 - .data( 'tooltip-mode', false );
214 - }
215 - })
216 - .bind( 'change', function() {
217 - if ( $( this ).val() != $( this ).data( 'tooltip' ) ) {
218 - $( this )
219 - .removeClass( 'wikieditor-toolbar-dialog-hint' )
220 - .data( 'tooltip-mode', false );
221 - }
222 - })
223 - .bind( 'blur', function() {
224 - if ( $( this ).val() == '' ) {
225 - $( this )
226 - .addClass( 'wikieditor-toolbar-dialog-hint' )
227 - .val( $( this ).data( 'tooltip' ) )
228 - .data( 'tooltip-mode', true );
229 - }
230 - });
231 -
232 - // Automatically copy the value of the internal link page title field to the link text field unless the
233 - // user has changed the link text field - this is a convenience thing since most link texts are going to
234 - // be the the same as the page title - Also change the internal/external radio button accordingly
235 - $( '#wikieditor-toolbar-link-int-target' ).bind( 'change keydown paste cut', function() {
236 - // $(this).val() is the old value, before the keypress - Defer this until $(this).val() has
237 - // been updated
238 - setTimeout( function() {
239 - if ( isExternalLink( $( '#wikieditor-toolbar-link-int-target' ).val() ) ) {
240 - $( '#wikieditor-toolbar-link-type-ext' ).attr( 'checked', 'checked' );
241 - updateWidget( 'external' );
242 - } else {
243 - $( '#wikieditor-toolbar-link-type-int' ).attr( 'checked', 'checked' );
244 - updateExistence();
245 - }
246 - if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched' ) )
247 - if ( $( '#wikieditor-toolbar-link-int-target' ).val() ==
248 - $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip' ) ) {
249 - $( '#wikieditor-toolbar-link-int-text' )
250 - .addClass( 'wikieditor-toolbar-dialog-hint' )
251 - .val( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip' ) )
252 - .change();
253 - } else {
254 - $( '#wikieditor-toolbar-link-int-text' )
255 - .val( $( '#wikieditor-toolbar-link-int-target' ).val() )
256 - .change();
257 - }
258 - }, 0 );
259 - });
260 - $( '#wikieditor-toolbar-link-int-text' ).bind( 'change keydown paste cut', function() {
261 - var oldVal = $(this).val();
262 - var that = this;
263 - setTimeout( function() {
264 - if ( $(that).val() != oldVal )
265 - $(that).data( 'untouched', false );
266 - }, 0 );
267 - });
268 - // Add images to the page existence widget, which will be shown mutually exclusively to communicate if
269 - // the page exists, does not exist or the title is invalid (like if it contains a | character)
270 - var existsMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-exists' );
271 - var notexistsMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-notexists' );
272 - var invalidMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-invalid' );
273 - var externalMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-external' );
274 - var loadingMsg = msg.get( 'wikieditor-toolbar-tool-link-int-target-status-loading' );
275 - $( '#wikieditor-toolbar-link-int-target-status' )
276 - .append( $( '<div />' )
277 - .attr( 'id', 'wikieditor-toolbar-link-int-target-status-exists' )
278 - .append( existsMsg )
279 - )
280 - .append( $( '<div />' )
281 - .attr( 'id', 'wikieditor-toolbar-link-int-target-status-notexists' )
282 - .append( notexistsMsg )
283 - )
284 - .append( $( '<div />' )
285 - .attr( 'id', 'wikieditor-toolbar-link-int-target-status-invalid' )
286 - .append( invalidMsg )
287 - )
288 - .append( $( '<div />' )
289 - .attr( 'id', 'wikieditor-toolbar-link-int-target-status-external' )
290 - .append( externalMsg )
291 - )
292 - .append( $( '<div />' )
293 - .attr( 'id', 'wikieditor-toolbar-link-int-target-status-loading' )
294 - .append( $( '<img />' ).attr( {
295 - 'src': $.wikiEditor.imgPath + 'dialogs/' + 'loading.gif',
296 - 'alt': loadingMsg,
297 - 'title': loadingMsg
298 - } ) )
299 - )
300 - .data( 'existencecache', {} )
301 - .children().hide();
302 -
303 - $( '#wikieditor-toolbar-link-int-target' )
304 - .bind( 'keyup paste cut', function() {
305 - // Cancel the running timer if applicable
306 - if ( typeof $(this).data( 'timerID' ) != 'undefined' ) {
307 - clearTimeout( $(this).data( 'timerID' ) );
308 - }
309 - // Delay fetch for a while
310 - // FIXME: Make 120 configurable elsewhere
311 - var timerID = setTimeout( updateExistence, 120 );
312 - $(this).data( 'timerID', timerID );
313 - } )
314 - .change( function() {
315 - // Cancel the running timer if applicable
316 - if ( typeof $(this).data( 'timerID' ) != 'undefined' ) {
317 - clearTimeout( $(this).data( 'timerID' ) );
318 - }
319 - // Fetch right now
320 - updateExistence();
321 - } );
322 -
323 - // Title suggestions
324 - $( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( {
325 - fetch: function( query ) {
326 - var that = this;
327 - var title = $(this).val();
328 -
329 - if ( isExternalLink( title ) || title.indexOf( '|' ) != -1 || title == '') {
330 - $(this).suggestions( 'suggestions', [] );
 409+ function escapeExternalTarget( s ) {
 410+ return s.replace( / /g, '%20' )
 411+ .replace( /\[/g, '%5B' )
 412+ .replace( /]/g, '%5D' );
 413+ }
 414+ function escapeExternalText( s ) {
 415+ // FIXME: Should this escape [ too? Seems to work without that
 416+ return s.replace( /(]+)/g, '<nowiki>$1</nowiki>' );
 417+ }
 418+ var insertText = '';
 419+ var whitespace = $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace' );
 420+ var target = $( '#wikieditor-toolbar-link-int-target' ).val();
 421+ var text = $( '#wikieditor-toolbar-link-int-text' ).val();
 422+ // check if the tooltips were passed as target or text
 423+ if ( $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip-mode' ) )
 424+ target = "";
 425+ if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip-mode' ) )
 426+ text = "";
 427+ var msg = mediaWiki.msg;
 428+ if ( target == '' ) {
 429+ alert( msg.get( 'wikieditor-toolbar-tool-link-empty' ) );
 430+ return;
 431+ }
 432+ if ( $.trim( text ) == '' ) {
 433+ // [[Foo| ]] creates an invisible link
 434+ // Instead, generate [[Foo|]]
 435+ text = '';
 436+ }
 437+ if ( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) ) {
 438+ // FIXME: Exactly how fragile is this?
 439+ if ( $( '#wikieditor-toolbar-link-int-target-status-invalid' ).is( ':visible' ) ) {
 440+ // Refuse to add links to invalid titles
 441+ alert( msg.get( 'wikieditor-toolbar-tool-link-int-invalid' ) );
331442 return;
332443 }
333444
334 - var cache = $(this).data( 'suggcache' );
335 - if ( typeof cache[title] != 'undefined' ) {
336 - $(this).suggestions( 'suggestions', cache[title] );
 445+ if ( target == text || !text.length )
 446+ insertText = '[[' + target + ']]';
 447+ else
 448+ insertText = '[[' + target + '|' + escapeInternalText( text ) + ']]';
 449+ } else {
 450+ // Prepend http:// if there is no protocol
 451+ if ( !target.match( /^[a-z]+:\/\/./ ) )
 452+ target = 'http://' + target;
 453+
 454+ // Detect if this is really an internal link in disguise
 455+ var match = target.match( $(this).data( 'articlePathRegex' ) );
 456+ if ( match && !$(this).data( 'ignoreLooksInternal' ) ) {
 457+ var buttons = { };
 458+ var that = this;
 459+ buttons[ msg.get( 'wikieditor-toolbar-tool-link-lookslikeinternal-int' ) ] =
 460+ function() {
 461+ $( '#wikieditor-toolbar-link-int-target' ).val( match[1] ).change();
 462+ $(this).dialog( 'close' );
 463+ };
 464+ buttons[ msg.get( 'wikieditor-toolbar-tool-link-lookslikeinternal-ext' ) ] =
 465+ function() {
 466+ $(that).data( 'ignoreLooksInternal', true );
 467+ $(that).closest( '.ui-dialog' ).find( 'button:first' ).click();
 468+ $(that).data( 'ignoreLooksInternal', false );
 469+ $(this).dialog( 'close' );
 470+ };
 471+ $.wikiEditor.modules.dialogs.quickDialog(
 472+ msg.get( 'wikieditor-toolbar-tool-link-lookslikeinternal', match[1] ),
 473+ { buttons: buttons }
 474+ );
337475 return;
338476 }
 477+
 478+ var escTarget = escapeExternalTarget( target );
 479+ var escText = escapeExternalText( text );
339480
340 - var request = $.ajax( {
341 - url: wgScriptPath + '/api.php',
342 - data: {
343 - 'action': 'opensearch',
344 - 'search': title,
345 - 'namespace': 0,
346 - 'suggest': '',
347 - 'format': 'json'
348 - },
349 - dataType: 'json',
350 - success: function( data ) {
351 - cache[title] = data[1];
352 - $(that).suggestions( 'suggestions', data[1] );
353 - }
354 - });
355 - $(this).data( 'request', request );
356 - },
357 - cancel: function() {
358 - var request = $(this).data( 'request' );
359 - if ( request )
360 - request.abort();
 481+ if ( escTarget == escText )
 482+ insertText = escTarget;
 483+ else if ( text == '' )
 484+ insertText = '[' + escTarget + ']';
 485+ else
 486+ insertText = '[' + escTarget + ' ' + escText + ']';
361487 }
362 - });
363 - },
364 - dialog: {
365 - width: 500,
366 - dialogClass: 'wikiEditor-toolbar-dialog',
367 - buttons: {
368 - 'wikieditor-toolbar-tool-link-insert': function() {
369 - function escapeInternalText( s ) {
370 - // FIXME: Should this escape [[ too? Seems to work without that
371 - return s.replace( /(]{2,})/g, '<nowiki>$1</nowiki>' );
 488+ // Preserve whitespace in selection when replacing
 489+ if ( whitespace ) insertText = whitespace[0] + insertText + whitespace[1];
 490+ $(this).dialog( 'close' );
 491+ $.wikiEditor.modules.toolbar.fn.doAction( $(this).data( 'context' ), {
 492+ type: 'replace',
 493+ options: {
 494+ pre: insertText
372495 }
373 - function escapeExternalTarget( s ) {
374 - return s.replace( / /g, '%20' )
375 - .replace( /\[/g, '%5B' )
376 - .replace( /]/g, '%5D' );
377 - }
378 - function escapeExternalText( s ) {
379 - // FIXME: Should this escape [ too? Seems to work without that
380 - return s.replace( /(]+)/g, '<nowiki>$1</nowiki>' );
381 - }
382 - var insertText = '';
383 - var whitespace = $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace' );
384 - var target = $( '#wikieditor-toolbar-link-int-target' ).val();
385 - var text = $( '#wikieditor-toolbar-link-int-text' ).val();
386 - // check if the tooltips were passed as target or text
387 - if ( $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip-mode' ) )
388 - target = "";
389 - if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip-mode' ) )
390 - text = "";
391 - var msg = mediaWiki.msg;
392 - if ( target == '' ) {
393 - alert( msg.get( 'wikieditor-toolbar-tool-link-empty' ) );
394 - return;
395 - }
396 - if ( $.trim( text ) == '' ) {
397 - // [[Foo| ]] creates an invisible link
398 - // Instead, generate [[Foo|]]
399 - text = '';
400 - }
401 - if ( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) ) {
402 - // FIXME: Exactly how fragile is this?
403 - if ( $( '#wikieditor-toolbar-link-int-target-status-invalid' ).is( ':visible' ) ) {
404 - // Refuse to add links to invalid titles
405 - alert( msg.get( 'wikieditor-toolbar-tool-link-int-invalid' ) );
406 - return;
407 - }
408 -
409 - if ( target == text || !text.length )
410 - insertText = '[[' + target + ']]';
411 - else
412 - insertText = '[[' + target + '|' + escapeInternalText( text ) + ']]';
413 - } else {
414 - // Prepend http:// if there is no protocol
415 - if ( !target.match( /^[a-z]+:\/\/./ ) )
416 - target = 'http://' + target;
417 -
418 - // Detect if this is really an internal link in disguise
419 - var match = target.match( $(this).data( 'articlePathRegex' ) );
420 - if ( match && !$(this).data( 'ignoreLooksInternal' ) ) {
421 - var buttons = { };
422 - var that = this;
423 - buttons[ msg.get( 'wikieditor-toolbar-tool-link-lookslikeinternal-int' ) ] =
424 - function() {
425 - $( '#wikieditor-toolbar-link-int-target' ).val( match[1] ).change();
426 - $(this).dialog( 'close' );
427 - };
428 - buttons[ msg.get( 'wikieditor-toolbar-tool-link-lookslikeinternal-ext' ) ] =
429 - function() {
430 - $(that).data( 'ignoreLooksInternal', true );
431 - $(that).closest( '.ui-dialog' ).find( 'button:first' ).click();
432 - $(that).data( 'ignoreLooksInternal', false );
433 - $(this).dialog( 'close' );
434 - };
435 - $.wikiEditor.modules.dialogs.quickDialog(
436 - msg.get( 'wikieditor-toolbar-tool-link-lookslikeinternal', match[1] ),
437 - { buttons: buttons }
438 - );
439 - return;
440 - }
441 -
442 - var escTarget = escapeExternalTarget( target );
443 - var escText = escapeExternalText( text );
444 -
445 - if ( escTarget == escText )
446 - insertText = escTarget;
447 - else if ( text == '' )
448 - insertText = '[' + escTarget + ']';
449 - else
450 - insertText = '[' + escTarget + ' ' + escText + ']';
451 - }
452 - // Preserve whitespace in selection when replacing
453 - if ( whitespace ) insertText = whitespace[0] + insertText + whitespace[1];
454 - $(this).dialog( 'close' );
455 - $.wikiEditor.modules.toolbar.fn.doAction( $(this).data( 'context' ), {
456 - type: 'replace',
457 - options: {
458 - pre: insertText
459 - }
460 - }, $(this) );
461 -
462 - // Blank form
463 - $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' );
464 - $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' )
465 - .attr( 'checked', '' );
466 - },
467 - 'wikieditor-toolbar-tool-link-cancel': function() {
468 - // Clear any saved selection state
469 - var context = $(this).data( 'context' );
470 - context.fn.restoreStuffForIE();
471 - $(this).dialog( 'close' );
472 - }
 496+ }, $(this) );
 497+
 498+ // Blank form
 499+ $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' );
 500+ $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' )
 501+ .attr( 'checked', '' );
473502 },
474 - open: function() {
475 - // Cache the articlepath regex
476 - $(this).data( 'articlePathRegex', new RegExp(
477 - '^' + RegExp.escape( wgServer + wgArticlePath )
478 - .replace( /\\\$1/g, '(.*)' ) + '$'
479 - ) );
480 - // Pre-fill the text fields based on the current selection
 503+ 'wikieditor-toolbar-tool-link-cancel': function() {
 504+ // Clear any saved selection state
481505 var context = $(this).data( 'context' );
482 - // Restore and immediately save selection state, needed for inserting stuff later
483506 context.fn.restoreStuffForIE();
484 - context.fn.saveStuffForIE();
485 - var selection = context.$textarea.textSelection( 'getSelection' );
486 - $( '#wikieditor-toolbar-link-int-target' ).focus();
487 - // Trigger the change event, so the link status indicator is up to date
488 - $( '#wikieditor-toolbar-link-int-target' ).change();
489 - $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] );
490 - if ( selection != '' ) {
491 - var target, text, type;
492 - var matches;
493 - if ( ( matches = selection.match( /^(\s*)\[\[([^\]\|]+)(\|([^\]\|]*))?\]\](\s*)$/ ) ) ) {
494 - // [[foo|bar]] or [[foo]]
495 - target = matches[2];
496 - text = ( matches[4] ? matches[4] : matches[2] );
497 - type = 'int';
498 - // Preserve whitespace when replacing
499 - $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
500 - } else if ( ( matches = selection.match( /^(\s*)\[([^\] ]+)( ([^\]]+))?\](\s*)$/ ) ) ) {
501 - // [http://www.example.com foo] or [http://www.example.com]
502 - target = matches[2];
503 - text = ( matches[4] ? matches[4] : '' );
504 - type = 'ext';
505 - // Preserve whitespace when replacing
506 - $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
507 - } else {
508 - // Trim any leading and trailing whitespace from the selection,
509 - // but preserve it when replacing
510 - target = text = $.trim( selection );
511 - if ( target.length < selection.length ) {
512 - $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [
513 - selection.substr( 0, selection.indexOf( target.charAt( 0 ) ) ),
514 - selection.substr(
515 - selection.lastIndexOf( target.charAt( target.length - 1 ) ) + 1
516 - ) ]
517 - );
518 - }
 507+ $(this).dialog( 'close' );
 508+ }
 509+ },
 510+ open: function() {
 511+ // Cache the articlepath regex
 512+ $(this).data( 'articlePathRegex', new RegExp(
 513+ '^' + RegExp.escape( wgServer + wgArticlePath )
 514+ .replace( /\\\$1/g, '(.*)' ) + '$'
 515+ ) );
 516+ // Pre-fill the text fields based on the current selection
 517+ var context = $(this).data( 'context' );
 518+ // Restore and immediately save selection state, needed for inserting stuff later
 519+ context.fn.restoreStuffForIE();
 520+ context.fn.saveStuffForIE();
 521+ var selection = context.$textarea.textSelection( 'getSelection' );
 522+ $( '#wikieditor-toolbar-link-int-target' ).focus();
 523+ // Trigger the change event, so the link status indicator is up to date
 524+ $( '#wikieditor-toolbar-link-int-target' ).change();
 525+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] );
 526+ if ( selection != '' ) {
 527+ var target, text, type;
 528+ var matches;
 529+ if ( ( matches = selection.match( /^(\s*)\[\[([^\]\|]+)(\|([^\]\|]*))?\]\](\s*)$/ ) ) ) {
 530+ // [[foo|bar]] or [[foo]]
 531+ target = matches[2];
 532+ text = ( matches[4] ? matches[4] : matches[2] );
 533+ type = 'int';
 534+ // Preserve whitespace when replacing
 535+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
 536+ } else if ( ( matches = selection.match( /^(\s*)\[([^\] ]+)( ([^\]]+))?\](\s*)$/ ) ) ) {
 537+ // [http://www.example.com foo] or [http://www.example.com]
 538+ target = matches[2];
 539+ text = ( matches[4] ? matches[4] : '' );
 540+ type = 'ext';
 541+ // Preserve whitespace when replacing
 542+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
 543+ } else {
 544+ // Trim any leading and trailing whitespace from the selection,
 545+ // but preserve it when replacing
 546+ target = text = $.trim( selection );
 547+ if ( target.length < selection.length ) {
 548+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [
 549+ selection.substr( 0, selection.indexOf( target.charAt( 0 ) ) ),
 550+ selection.substr(
 551+ selection.lastIndexOf( target.charAt( target.length - 1 ) ) + 1
 552+ ) ]
 553+ );
519554 }
520 -
521 - // Change the value by calling val() doesn't trigger the change event, so let's do that
522 - // ourselves
523 - if ( typeof text != 'undefined' )
524 - $( '#wikieditor-toolbar-link-int-text' ).val( text ).change();
525 - if ( typeof target != 'undefined' )
526 - $( '#wikieditor-toolbar-link-int-target' ).val( target ).change();
527 - if ( typeof type != 'undefined' )
528 - $( '#wikieditor-toolbar-link-' + type ).attr( 'checked', 'checked' );
529555 }
530 - $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched',
531 - $( '#wikieditor-toolbar-link-int-text' ).val() ==
532 - $( '#wikieditor-toolbar-link-int-target' ).val() ||
533 - $( '#wikieditor-toolbar-link-int-text' ).hasClass( 'wikieditor-toolbar-dialog-hint' )
534 - );
535 - $( '#wikieditor-toolbar-link-int-target' ).suggestions();
536556
537 - //don't overwrite user's text
538 - if( selection != '' ){
539 - $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', false );
540 - }
541 -
542 - $( '#wikieditor-toolbar-link-int-text, #wikiedit-toolbar-link-int-target' )
543 - .each( function() {
544 - if ( $(this).val() == '' )
545 - $(this).parent().find( 'label' ).show();
546 - });
547 -
548 - if ( !( $(this).data( 'dialogkeypressset' ) ) ) {
549 - $(this).data( 'dialogkeypressset', true );
550 - // Execute the action associated with the first button
551 - // when the user presses Enter
552 - $(this).closest( '.ui-dialog' ).keypress( function( e ) {
553 - if ( ( e.keyCode || e.which ) == 13 ) {
554 - var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
555 - button.click();
556 - e.preventDefault();
557 - }
558 - });
559 -
560 - // Make tabbing to a button and pressing
561 - // Enter do what people expect
562 - $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
563 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
564 - });
565 - }
 557+ // Change the value by calling val() doesn't trigger the change event, so let's do that
 558+ // ourselves
 559+ if ( typeof text != 'undefined' )
 560+ $( '#wikieditor-toolbar-link-int-text' ).val( text ).change();
 561+ if ( typeof target != 'undefined' )
 562+ $( '#wikieditor-toolbar-link-int-target' ).val( target ).change();
 563+ if ( typeof type != 'undefined' )
 564+ $( '#wikieditor-toolbar-link-' + type ).attr( 'checked', 'checked' );
566565 }
567 - }
568 - },
569 - 'insert-reference': {
570 - filters: [ '#wpTextbox1.toolbar-dialogs' ],
571 - titleMsg: 'wikieditor-toolbar-tool-reference-title',
572 - id: 'wikieditor-toolbar-reference-dialog',
573 - html: '\
574 - <div class="wikieditor-toolbar-dialog-wrapper">\
575 - <fieldset><div class="wikieditor-toolbar-table-form">\
576 - <div class="wikieditor-toolbar-field-wrapper">\
577 - <label for="wikieditor-toolbar-reference-text"\
578 - rel="wikieditor-toolbar-tool-reference-text"></label>\
579 - <input type="text" id="wikieditor-toolbar-reference-text" />\
580 - </div>\
581 - </div></fieldset>\
582 - </div>',
583 - init: function() {
584 - // Insert translated strings into labels
585 - $( this ).find( '[rel]' ).each( function() {
586 - $( this ).text( mediaWiki.msg.get( $( this ).attr( 'rel' ) ) );
587 - } );
 566+ $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched',
 567+ $( '#wikieditor-toolbar-link-int-text' ).val() ==
 568+ $( '#wikieditor-toolbar-link-int-target' ).val() ||
 569+ $( '#wikieditor-toolbar-link-int-text' ).hasClass( 'wikieditor-toolbar-dialog-hint' )
 570+ );
 571+ $( '#wikieditor-toolbar-link-int-target' ).suggestions();
588572
589 - },
590 - dialog: {
591 - dialogClass: 'wikiEditor-toolbar-dialog',
592 - width: 590,
593 - buttons: {
594 - 'wikieditor-toolbar-tool-reference-insert': function() {
595 - var insertText = $( '#wikieditor-toolbar-reference-text' ).val();
596 - var whitespace = $( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace' );
597 - var attributes = $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes' );
598 - // Close the dialog
599 - $( this ).dialog( 'close' );
600 - $.wikiEditor.modules.toolbar.fn.doAction(
601 - $( this ).data( 'context' ),
602 - {
603 - type: 'replace',
604 - options: {
605 - pre: whitespace[0] + '<ref' + attributes + '>',
606 - peri: insertText,
607 - post: '</ref>' + whitespace[1]
608 - }
609 - },
610 - $( this )
611 - );
612 - // Restore form state
613 - $( '#wikieditor-toolbar-reference-text' ).val( "" );
614 - },
615 - 'wikieditor-toolbar-tool-reference-cancel': function() {
616 - // Clear any saved selection state
617 - var context = $( this ).data( 'context' );
618 - context.fn.restoreStuffForIE();
619 - $( this ).dialog( 'close' );
620 - }
621 - },
622 - open: function() {
623 - // Pre-fill the text fields based on the current selection
624 - var context = $(this).data( 'context' );
625 - // Restore and immediately save selection state, needed for inserting stuff later
626 - context.fn.restoreStuffForIE();
627 - context.fn.saveStuffForIE();
628 - var selection = context.$textarea.textSelection( 'getSelection' );
629 - // set focus
630 - $( '#wikieditor-toolbar-reference-text' ).focus();
631 - $( '#wikieditor-toolbar-reference-dialog' )
632 - .data( 'whitespace', [ '', '' ] )
633 - .data( 'attributes', '' );
634 - if ( selection != '' ) {
635 - var matches, text;
636 - if ( ( matches = selection.match( /^(\s*)<ref([^\>]*)>([^\<]*)<\/ref\>(\s*)$/ ) ) ) {
637 - text = matches[3];
638 - // Preserve whitespace when replacing
639 - $( '#wikieditor-toolbar-reference-dialog' )
640 - .data( 'whitespace', [ matches[1], matches[4] ] );
641 - $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes', matches[2] );
642 - } else {
643 - text = selection;
644 - }
645 - $( '#wikieditor-toolbar-reference-text' ).val( text );
646 - }
647 - if ( !( $( this ).data( 'dialogkeypressset' ) ) ) {
648 - $( this ).data( 'dialogkeypressset', true );
649 - // Execute the action associated with the first button
650 - // when the user presses Enter
651 - $( this ).closest( '.ui-dialog' ).keypress( function( e ) {
652 - if ( ( e.keyCode || e.which ) == 13 ) {
653 - var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
654 - button.click();
655 - e.preventDefault();
656 - }
657 - } );
658 - // Make tabbing to a button and pressing
659 - // Enter do what people expect
660 - $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
661 - $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
662 - } );
663 - }
 573+ //don't overwrite user's text
 574+ if( selection != '' ){
 575+ $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', false );
664576 }
665 - }
666 - },
667 - 'insert-table': {
668 - filters: [ '#wpTextbox1.toolbar-dialogs' ],
669 - titleMsg: 'wikieditor-toolbar-tool-table-title',
670 - id: 'wikieditor-toolbar-table-dialog',
671 - // FIXME: Localize 'x'?
672 - html: '\
673 - <div class="wikieditor-toolbar-dialog-wrapper">\
674 - <fieldset><div class="wikieditor-toolbar-table-form">\
675 - <div class="wikieditor-toolbar-field-wrapper">\
676 - <input type="checkbox" id="wikieditor-toolbar-table-dimensions-header" checked />\
677 - <label for="wikieditor-toolbar-table-dimensions-header"\
678 - rel="wikieditor-toolbar-tool-table-dimensions-header"></label>\
679 - </div>\
680 - <div class="wikieditor-toolbar-field-wrapper">\
681 - <input type="checkbox" id="wikieditor-toolbar-table-wikitable" checked />\
682 - <label for="wikieditor-toolbar-table-wikitable" rel="wikieditor-toolbar-tool-table-wikitable"></label>\
683 - </div>\
684 - <div class="wikieditor-toolbar-field-wrapper">\
685 - <input type="checkbox" id="wikieditor-toolbar-table-sortable" />\
686 - <label for="wikieditor-toolbar-table-sortable" rel="wikieditor-toolbar-tool-table-sortable"></label>\
687 - </div>\
688 - <div class="wikieditor-toolbar-table-dimension-fields">\
689 - <div class="wikieditor-toolbar-field-wrapper">\
690 - <label for="wikieditor-toolbar-table-dimensions-rows"\
691 - rel="wikieditor-toolbar-tool-table-dimensions-rows"></label><br />\
692 - <input type="text" id="wikieditor-toolbar-table-dimensions-rows" size="4" />\
693 - </div>\
694 - <div class="wikieditor-toolbar-field-wrapper">\
695 - <label for="wikieditor-toolbar-table-dimensions-columns"\
696 - rel="wikieditor-toolbar-tool-table-dimensions-columns"></label><br />\
697 - <input type="text" id="wikieditor-toolbar-table-dimensions-columns" size="4" />\
698 - </div>\
699 - </div>\
700 - </div></fieldset>\
701 - <div class="wikieditor-toolbar-table-preview-wrapper" >\
702 - <span rel="wikieditor-toolbar-tool-table-example"></span>\
703 - <div class="wikieditor-toolbar-table-preview-content">\
704 - <table id="wikieditor-toolbar-table-preview" class="wikieditor-toolbar-table-preview wikitable">\
705 - <tr class="wikieditor-toolbar-table-preview-header">\
706 - <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
707 - <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
708 - <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
709 - </tr><tr class="wikieditor-toolbar-table-preview-hidden" style="display: none;">\
710 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
711 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
712 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
713 - </tr><tr>\
714 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
715 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
716 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
717 - </tr><tr>\
718 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
719 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
720 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
721 - </tr><tr>\
722 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
723 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
724 - <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
725 - </tr>\
726 - </table>\
727 - </div>\
728 - </div></div>',
729 - init: function() {
730 - $(this).find( '[rel]' ).each( function() {
731 - $(this).text( mediaWiki.msg.get( $(this).attr( 'rel' ) ) );
732 - });
733 - // Set tabindexes on form fields
734 - $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
735577
736 - $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
737 - $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
738 - $( '#wikieditor-toolbar-table-wikitable' ).click( function() {
739 - $( '.wikieditor-toolbar-table-preview' ).toggleClass( 'wikitable' );
740 - });
 578+ $( '#wikieditor-toolbar-link-int-text, #wikiedit-toolbar-link-int-target' )
 579+ .each( function() {
 580+ if ( $(this).val() == '' )
 581+ $(this).parent().find( 'label' ).show();
 582+ });
741583
742 - // Hack for sortable preview: dynamically adding
743 - // sortable class doesn't work, so we use a clone
744 - // FIXME: Relies on sortable table internals
745 - $( '#wikieditor-toolbar-table-preview' )
746 - .clone()
747 - .attr( 'id', 'wikieditor-toolbar-table-preview2' )
748 - .addClass( 'sortable' )
749 - .insertAfter( $( '#wikieditor-toolbar-table-preview' ) )
750 - .hide();
751 - if ( typeof ts_makeSortable == 'function' )
752 - ts_makeSortable( $( '#wikieditor-toolbar-table-preview2' ).get( 0 ) );
753 - $( '#wikieditor-toolbar-table-sortable' ).click( function() {
754 - // Swap the currently shown one clone with the other one
755 - $( '#wikieditor-toolbar-table-preview' )
756 - .hide()
757 - .attr( 'id', 'wikieditor-toolbar-table-preview3' );
758 - $( '#wikieditor-toolbar-table-preview2' )
759 - .attr( 'id', 'wikieditor-toolbar-table-preview' )
760 - .show();
761 - $( '#wikieditor-toolbar-table-preview3' ).attr( 'id', 'wikieditor-toolbar-table-preview2' );
762 - });
763 -
764 - $( '#wikieditor-toolbar-table-dimensions-header' ).click( function() {
765 - // Instead of show/hiding, switch the HTML around
766 - // We do this because the sortable tables script styles the first row,
767 - // visible or not
768 - var headerHTML = $( '.wikieditor-toolbar-table-preview-header' ).html();
769 - var hiddenHTML = $( '.wikieditor-toolbar-table-preview-hidden' ).html();
770 - $( '.wikieditor-toolbar-table-preview-header' ).html( hiddenHTML );
771 - $( '.wikieditor-toolbar-table-preview-hidden' ).html( headerHTML );
772 - if ( typeof ts_makeSortable == 'function' )
773 - ts_makeSortable(
774 - $( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' )
775 - .filter( '.sortable' )
776 - .get( 0 )
777 - );
778 - });
779 -
780 - },
781 - dialog: {
782 - resizable: false,
783 - dialogClass: 'wikiEditor-toolbar-dialog',
784 - width: 590,
785 - buttons: {
786 - 'wikieditor-toolbar-tool-table-insert': function() {
787 - var rowsVal = $( '#wikieditor-toolbar-table-dimensions-rows' ).val();
788 - var colsVal = $( '#wikieditor-toolbar-table-dimensions-columns' ).val();
789 - var rows = parseInt( rowsVal, 10 );
790 - var cols = parseInt( colsVal, 10 );
791 - var header = $( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) ? 1 : 0;
792 - var msg = mediaWiki.msg;
793 - if ( isNaN( rows ) || isNaN( cols ) || rows != rowsVal || cols != colsVal ) {
794 - alert( msg.get( 'wikieditor-toolbar-tool-table-invalidnumber' ) );
795 - return;
 584+ if ( !( $(this).data( 'dialogkeypressset' ) ) ) {
 585+ $(this).data( 'dialogkeypressset', true );
 586+ // Execute the action associated with the first button
 587+ // when the user presses Enter
 588+ $(this).closest( '.ui-dialog' ).keypress( function( e ) {
 589+ if ( ( e.keyCode || e.which ) == 13 ) {
 590+ var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
 591+ button.click();
 592+ e.preventDefault();
796593 }
797 - if ( rows + header == 0 || cols == 0 ) {
798 - alert( msg.get( 'wikieditor-toolbar-tool-table-zero' ) );
799 - return;
800 - }
801 - if ( rows * cols > 1000 ) {
802 - alert( msg.get( 'wikieditor-toolbar-tool-table-toomany', 1000 ) );
803 - return;
804 - }
805 - var headerText = msg.get( 'wikieditor-toolbar-tool-table-example-header' );
806 - var normalText = msg.get( 'wikieditor-toolbar-tool-table-example' );
807 - var table = "";
808 - for ( var r = 0; r < rows + header; r++ ) {
809 - table += "|-\n";
810 - for ( var c = 0; c < cols; c++ ) {
811 - var isHeader = ( header && r == 0 );
812 - var delim = isHeader ? '!' : '|';
813 - if ( c > 0 ) {
814 - delim += delim;
815 - }
816 - table += delim + ' ' + ( isHeader ? headerText : normalText ) + ' ';
817 - }
818 - // Replace trailing space by newline
819 - // table[table.length - 1] is read-only
820 - table = table.substr( 0, table.length - 1 ) + "\n";
821 - }
822 - var classes = [];
823 - if ( $( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
824 - classes.push( 'wikitable' );
825 - if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
826 - classes.push( 'sortable' );
827 - var classStr = classes.length > 0 ? ' class="' + classes.join( ' ' ) + '"' : '';
828 - $(this).dialog( 'close' );
829 - $.wikiEditor.modules.toolbar.fn.doAction(
830 - $(this).data( 'context' ),
831 - {
832 - type: 'replace',
833 - options: {
834 - pre: '{|' + classStr + "\n",
835 - peri: table,
836 - post: '|}',
837 - ownline: true
838 - }
839 - },
840 - $(this)
841 - );
842 -
843 - // Restore form state
844 - $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
845 - $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
846 - // Simulate clicks instead of setting values, so the according
847 - // actions are performed
848 - if ( !$( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) )
849 - $( '#wikieditor-toolbar-table-dimensions-header' ).click();
850 - if ( !$( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
851 - $( '#wikieditor-toolbar-table-wikitable' ).click();
852 - if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
853 - $( '#wikieditor-toolbar-table-sortable' ).click();
854 - },
855 - 'wikieditor-toolbar-tool-table-cancel': function() {
856 - $(this).dialog( 'close' );
857 - }
858 - },
859 - open: function() {
860 - $( '#wikieditor-toolbar-table-dimensions-rows' ).focus();
861 - if ( !( $(this).data( 'dialogkeypressset' ) ) ) {
862 - $(this).data( 'dialogkeypressset', true );
863 - // Execute the action associated with the first button
864 - // when the user presses Enter
865 - $(this).closest( '.ui-dialog' ).keypress( function( e ) {
866 - if ( ( e.keyCode || e.which ) == 13 ) {
867 - var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
868 - button.click();
869 - e.preventDefault();
870 - }
871 - });
872 -
873 - // Make tabbing to a button and pressing
874 - // Enter do what people expect
875 - $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
876 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
877 - });
878 - }
 594+ });
 595+
 596+ // Make tabbing to a button and pressing
 597+ // Enter do what people expect
 598+ $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 599+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
 600+ });
879601 }
880602 }
 603+ }
 604+ },
 605+ 'insert-reference': {
 606+ titleMsg: 'wikieditor-toolbar-tool-reference-title',
 607+ id: 'wikieditor-toolbar-reference-dialog',
 608+ html: '\
 609+ <div class="wikieditor-toolbar-dialog-wrapper">\
 610+ <fieldset><div class="wikieditor-toolbar-table-form">\
 611+ <div class="wikieditor-toolbar-field-wrapper">\
 612+ <label for="wikieditor-toolbar-reference-text"\
 613+ rel="wikieditor-toolbar-tool-reference-text"></label>\
 614+ <input type="text" id="wikieditor-toolbar-reference-text" />\
 615+ </div>\
 616+ </div></fieldset>\
 617+ </div>',
 618+ init: function() {
 619+ // Insert translated strings into labels
 620+ $( this ).find( '[rel]' ).each( function() {
 621+ $( this ).text( mediaWiki.msg.get( $( this ).attr( 'rel' ) ) );
 622+ } );
 623+
881624 },
882 - 'search-and-replace': {
883 - 'browsers': {
884 - // Left-to-right languages
885 - 'ltr': {
886 - 'msie': false,
887 - 'firefox': [['>=', 2]],
888 - 'opera': false,
889 - 'safari': [['>=', 3]],
890 - 'chrome': [['>=', 3]]
 625+ dialog: {
 626+ dialogClass: 'wikiEditor-toolbar-dialog',
 627+ width: 590,
 628+ buttons: {
 629+ 'wikieditor-toolbar-tool-reference-insert': function() {
 630+ var insertText = $( '#wikieditor-toolbar-reference-text' ).val();
 631+ var whitespace = $( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace' );
 632+ var attributes = $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes' );
 633+ // Close the dialog
 634+ $( this ).dialog( 'close' );
 635+ $.wikiEditor.modules.toolbar.fn.doAction(
 636+ $( this ).data( 'context' ),
 637+ {
 638+ type: 'replace',
 639+ options: {
 640+ pre: whitespace[0] + '<ref' + attributes + '>',
 641+ peri: insertText,
 642+ post: '</ref>' + whitespace[1]
 643+ }
 644+ },
 645+ $( this )
 646+ );
 647+ // Restore form state
 648+ $( '#wikieditor-toolbar-reference-text' ).val( "" );
891649 },
892 - // Right-to-left languages
893 - 'rtl': {
894 - 'msie': false,
895 - 'firefox': [['>=', 2]],
896 - 'opera': false,
897 - 'safari': [['>=', 3]],
898 - 'chrome': [['>=', 3]]
 650+ 'wikieditor-toolbar-tool-reference-cancel': function() {
 651+ // Clear any saved selection state
 652+ var context = $( this ).data( 'context' );
 653+ context.fn.restoreStuffForIE();
 654+ $( this ).dialog( 'close' );
899655 }
900656 },
901 - filters: [ '#wpTextbox1.toolbar-dialogs' ],
902 - titleMsg: 'wikieditor-toolbar-tool-replace-title',
903 - id: 'wikieditor-toolbar-replace-dialog',
904 - html: '\
905 - <div id="wikieditor-toolbar-replace-message">\
906 - <div id="wikieditor-toolbar-replace-nomatch" rel="wikieditor-toolbar-tool-replace-nomatch"></div>\
907 - <div id="wikieditor-toolbar-replace-success"></div>\
908 - <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\
909 - <div id="wikieditor-toolbar-replace-invalidregex"></div>\
 657+ open: function() {
 658+ // Pre-fill the text fields based on the current selection
 659+ var context = $(this).data( 'context' );
 660+ // Restore and immediately save selection state, needed for inserting stuff later
 661+ context.fn.restoreStuffForIE();
 662+ context.fn.saveStuffForIE();
 663+ var selection = context.$textarea.textSelection( 'getSelection' );
 664+ // set focus
 665+ $( '#wikieditor-toolbar-reference-text' ).focus();
 666+ $( '#wikieditor-toolbar-reference-dialog' )
 667+ .data( 'whitespace', [ '', '' ] )
 668+ .data( 'attributes', '' );
 669+ if ( selection != '' ) {
 670+ var matches, text;
 671+ if ( ( matches = selection.match( /^(\s*)<ref([^\>]*)>([^\<]*)<\/ref\>(\s*)$/ ) ) ) {
 672+ text = matches[3];
 673+ // Preserve whitespace when replacing
 674+ $( '#wikieditor-toolbar-reference-dialog' )
 675+ .data( 'whitespace', [ matches[1], matches[4] ] );
 676+ $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes', matches[2] );
 677+ } else {
 678+ text = selection;
 679+ }
 680+ $( '#wikieditor-toolbar-reference-text' ).val( text );
 681+ }
 682+ if ( !( $( this ).data( 'dialogkeypressset' ) ) ) {
 683+ $( this ).data( 'dialogkeypressset', true );
 684+ // Execute the action associated with the first button
 685+ // when the user presses Enter
 686+ $( this ).closest( '.ui-dialog' ).keypress( function( e ) {
 687+ if ( ( e.keyCode || e.which ) == 13 ) {
 688+ var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
 689+ button.click();
 690+ e.preventDefault();
 691+ }
 692+ } );
 693+ // Make tabbing to a button and pressing
 694+ // Enter do what people expect
 695+ $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 696+ $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
 697+ } );
 698+ }
 699+ }
 700+ }
 701+ },
 702+ 'insert-table': {
 703+ titleMsg: 'wikieditor-toolbar-tool-table-title',
 704+ id: 'wikieditor-toolbar-table-dialog',
 705+ // FIXME: Localize 'x'?
 706+ html: '\
 707+ <div class="wikieditor-toolbar-dialog-wrapper">\
 708+ <fieldset><div class="wikieditor-toolbar-table-form">\
 709+ <div class="wikieditor-toolbar-field-wrapper">\
 710+ <input type="checkbox" id="wikieditor-toolbar-table-dimensions-header" checked />\
 711+ <label for="wikieditor-toolbar-table-dimensions-header"\
 712+ rel="wikieditor-toolbar-tool-table-dimensions-header"></label>\
910713 </div>\
911 - <fieldset>\
 714+ <div class="wikieditor-toolbar-field-wrapper">\
 715+ <input type="checkbox" id="wikieditor-toolbar-table-wikitable" checked />\
 716+ <label for="wikieditor-toolbar-table-wikitable" rel="wikieditor-toolbar-tool-table-wikitable"></label>\
 717+ </div>\
 718+ <div class="wikieditor-toolbar-field-wrapper">\
 719+ <input type="checkbox" id="wikieditor-toolbar-table-sortable" />\
 720+ <label for="wikieditor-toolbar-table-sortable" rel="wikieditor-toolbar-tool-table-sortable"></label>\
 721+ </div>\
 722+ <div class="wikieditor-toolbar-table-dimension-fields">\
912723 <div class="wikieditor-toolbar-field-wrapper">\
913 - <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\
914 - <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;" />\
 724+ <label for="wikieditor-toolbar-table-dimensions-rows"\
 725+ rel="wikieditor-toolbar-tool-table-dimensions-rows"></label><br />\
 726+ <input type="text" id="wikieditor-toolbar-table-dimensions-rows" size="4" />\
915727 </div>\
916728 <div class="wikieditor-toolbar-field-wrapper">\
917 - <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\
918 - <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;" />\
 729+ <label for="wikieditor-toolbar-table-dimensions-columns"\
 730+ rel="wikieditor-toolbar-tool-table-dimensions-columns"></label><br />\
 731+ <input type="text" id="wikieditor-toolbar-table-dimensions-columns" size="4" />\
919732 </div>\
920 - <div class="wikieditor-toolbar-field-wrapper">\
921 - <input type="checkbox" id="wikieditor-toolbar-replace-case" />\
922 - <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\
923 - </div>\
924 - <div class="wikieditor-toolbar-field-wrapper">\
925 - <input type="checkbox" id="wikieditor-toolbar-replace-regex" />\
926 - <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\
927 - </div>\
928 - </fieldset>',
929 - init: function() {
930 - var msg = mediaWiki.msg;
931 - $(this).find( '[rel]' ).each( function() {
932 - $(this).text( msg.get( $(this).attr( 'rel' ) ) );
933 - });
934 - // Set tabindexes on form fields
935 - $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
936 -
937 - // TODO: Find a cleaner way to share this function
938 - $(this).data( 'replaceCallback', function( mode ) {
939 - $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
940 - var searchStr = $( '#wikieditor-toolbar-replace-search' ).val();
941 - if ( searchStr == '' ) {
942 - $( '#wikieditor-toolbar-replace-emptysearch' ).show();
 733+ </div>\
 734+ </div></fieldset>\
 735+ <div class="wikieditor-toolbar-table-preview-wrapper" >\
 736+ <span rel="wikieditor-toolbar-tool-table-example"></span>\
 737+ <div class="wikieditor-toolbar-table-preview-content">\
 738+ <table id="wikieditor-toolbar-table-preview" class="wikieditor-toolbar-table-preview wikitable">\
 739+ <tr class="wikieditor-toolbar-table-preview-header">\
 740+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
 741+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
 742+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
 743+ </tr><tr class="wikieditor-toolbar-table-preview-hidden" style="display: none;">\
 744+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 745+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 746+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 747+ </tr><tr>\
 748+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 749+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 750+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 751+ </tr><tr>\
 752+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 753+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 754+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 755+ </tr><tr>\
 756+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 757+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 758+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
 759+ </tr>\
 760+ </table>\
 761+ </div>\
 762+ </div></div>',
 763+ init: function() {
 764+ $(this).find( '[rel]' ).each( function() {
 765+ $(this).text( mediaWiki.msg.get( $(this).attr( 'rel' ) ) );
 766+ });
 767+ // Set tabindexes on form fields
 768+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
 769+
 770+ $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
 771+ $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
 772+ $( '#wikieditor-toolbar-table-wikitable' ).click( function() {
 773+ $( '.wikieditor-toolbar-table-preview' ).toggleClass( 'wikitable' );
 774+ });
 775+
 776+ // Hack for sortable preview: dynamically adding
 777+ // sortable class doesn't work, so we use a clone
 778+ // FIXME: Relies on sortable table internals
 779+ $( '#wikieditor-toolbar-table-preview' )
 780+ .clone()
 781+ .attr( 'id', 'wikieditor-toolbar-table-preview2' )
 782+ .addClass( 'sortable' )
 783+ .insertAfter( $( '#wikieditor-toolbar-table-preview' ) )
 784+ .hide();
 785+ if ( typeof ts_makeSortable == 'function' )
 786+ ts_makeSortable( $( '#wikieditor-toolbar-table-preview2' ).get( 0 ) );
 787+ $( '#wikieditor-toolbar-table-sortable' ).click( function() {
 788+ // Swap the currently shown one clone with the other one
 789+ $( '#wikieditor-toolbar-table-preview' )
 790+ .hide()
 791+ .attr( 'id', 'wikieditor-toolbar-table-preview3' );
 792+ $( '#wikieditor-toolbar-table-preview2' )
 793+ .attr( 'id', 'wikieditor-toolbar-table-preview' )
 794+ .show();
 795+ $( '#wikieditor-toolbar-table-preview3' ).attr( 'id', 'wikieditor-toolbar-table-preview2' );
 796+ });
 797+
 798+ $( '#wikieditor-toolbar-table-dimensions-header' ).click( function() {
 799+ // Instead of show/hiding, switch the HTML around
 800+ // We do this because the sortable tables script styles the first row,
 801+ // visible or not
 802+ var headerHTML = $( '.wikieditor-toolbar-table-preview-header' ).html();
 803+ var hiddenHTML = $( '.wikieditor-toolbar-table-preview-hidden' ).html();
 804+ $( '.wikieditor-toolbar-table-preview-header' ).html( hiddenHTML );
 805+ $( '.wikieditor-toolbar-table-preview-hidden' ).html( headerHTML );
 806+ if ( typeof ts_makeSortable == 'function' )
 807+ ts_makeSortable(
 808+ $( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' )
 809+ .filter( '.sortable' )
 810+ .get( 0 )
 811+ );
 812+ });
 813+ },
 814+ dialog: {
 815+ resizable: false,
 816+ dialogClass: 'wikiEditor-toolbar-dialog',
 817+ width: 590,
 818+ buttons: {
 819+ 'wikieditor-toolbar-tool-table-insert': function() {
 820+ var rowsVal = $( '#wikieditor-toolbar-table-dimensions-rows' ).val();
 821+ var colsVal = $( '#wikieditor-toolbar-table-dimensions-columns' ).val();
 822+ var rows = parseInt( rowsVal, 10 );
 823+ var cols = parseInt( colsVal, 10 );
 824+ var header = $( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) ? 1 : 0;
 825+ var msg = mediaWiki.msg;
 826+ if ( isNaN( rows ) || isNaN( cols ) || rows != rowsVal || cols != colsVal ) {
 827+ alert( msg.get( 'wikieditor-toolbar-tool-table-invalidnumber' ) );
943828 return;
944829 }
945 - var replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val();
946 - var flags = 'm';
947 - var matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' );
948 - var isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
949 - if ( !matchCase ) {
950 - flags += 'i';
 830+ if ( rows + header == 0 || cols == 0 ) {
 831+ alert( msg.get( 'wikieditor-toolbar-tool-table-zero' ) );
 832+ return;
951833 }
952 - if ( mode == 'replaceAll' ) {
953 - flags += 'g';
954 - }
955 - if ( !isRegex ) {
956 - searchStr = RegExp.escape( searchStr );
957 - }
958 - try {
959 - var regex = new RegExp( searchStr, flags );
960 - } catch( e ) {
961 - $( '#wikieditor-toolbar-replace-invalidregex' )
962 - .text( msg.get( 'wikieditor-toolbar-tool-replace-invalidregex',
963 - e.message ) )
964 - .show();
 834+ if ( rows * cols > 1000 ) {
 835+ alert( msg.get( 'wikieditor-toolbar-tool-table-toomany', 1000 ) );
965836 return;
966837 }
967 - var $textarea = $(this).data( 'context' ).$textarea;
968 - var text = $textarea.textSelection( 'getContents' );
969 - var match = false;
970 - var offset, s;
971 - if ( mode != 'replaceAll' ) {
972 - offset = $(this).data( 'offset' );
973 - s = text.substr( offset );
974 - match = s.match( regex );
 838+ var headerText = msg.get( 'wikieditor-toolbar-tool-table-example-header' );
 839+ var normalText = msg.get( 'wikieditor-toolbar-tool-table-example' );
 840+ var table = "";
 841+ for ( var r = 0; r < rows + header; r++ ) {
 842+ table += "|-\n";
 843+ for ( var c = 0; c < cols; c++ ) {
 844+ var isHeader = ( header && r == 0 );
 845+ var delim = isHeader ? '!' : '|';
 846+ if ( c > 0 ) {
 847+ delim += delim;
 848+ }
 849+ table += delim + ' ' + ( isHeader ? headerText : normalText ) + ' ';
 850+ }
 851+ // Replace trailing space by newline
 852+ // table[table.length - 1] is read-only
 853+ table = table.substr( 0, table.length - 1 ) + "\n";
975854 }
976 - if ( !match ) {
977 - // Search hit BOTTOM, continuing at TOP
978 - offset = 0;
979 - s = text;
980 - match = s.match( regex );
981 - }
 855+ var classes = [];
 856+ if ( $( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
 857+ classes.push( 'wikitable' );
 858+ if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
 859+ classes.push( 'sortable' );
 860+ var classStr = classes.length > 0 ? ' class="' + classes.join( ' ' ) + '"' : '';
 861+ $(this).dialog( 'close' );
 862+ $.wikiEditor.modules.toolbar.fn.doAction(
 863+ $(this).data( 'context' ),
 864+ {
 865+ type: 'replace',
 866+ options: {
 867+ pre: '{|' + classStr + "\n",
 868+ peri: table,
 869+ post: '|}',
 870+ ownline: true
 871+ }
 872+ },
 873+ $(this)
 874+ );
982875
983 - if ( !match ) {
984 - $( '#wikieditor-toolbar-replace-nomatch' ).show();
985 - } else if ( mode == 'replaceAll' ) {
986 - // Instead of using repetitive .match() calls, we use one .match() call with /g
987 - // and indexOf() followed by substr() to find the offsets. This is actually
988 - // faster because our indexOf+substr loop is faster than a match loop, and the
989 - // /g match is so ridiculously fast that it's negligible.
990 - // FIXME: Repetitively calling encapsulateSelection() is probably the best strategy
991 - // in Firefox/Webkit, but in IE replacing the entire content once is better.
992 - var index;
993 - for ( var i = 0; i < match.length; i++ ) {
994 - index = s.indexOf( match[i] );
995 - if ( index == -1 ) {
996 - // This shouldn't happen
997 - break;
998 - }
999 - var matchedText = s.substr( index, match[i].length );
1000 - s = s.substr( index + match[i].length );
1001 -
1002 - var start = index + offset;
1003 - var end = start + match[i].length;
1004 - // Make regex placeholder substitution ($1) work
1005 - var replace = isRegex ? matchedText.replace( regex, replaceStr ) : replaceStr;
1006 - var newEnd = start + replace.length;
1007 - $textarea
1008 - .textSelection( 'setSelection', { 'start': start, 'end': end } )
1009 - .textSelection( 'encapsulateSelection', {
1010 - 'peri': replace,
1011 - 'replace': true } )
1012 - .textSelection( 'setSelection', { 'start': start, 'end': newEnd } );
1013 - offset = newEnd;
 876+ // Restore form state
 877+ $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
 878+ $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
 879+ // Simulate clicks instead of setting values, so the according
 880+ // actions are performed
 881+ if ( !$( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) )
 882+ $( '#wikieditor-toolbar-table-dimensions-header' ).click();
 883+ if ( !$( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
 884+ $( '#wikieditor-toolbar-table-wikitable' ).click();
 885+ if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
 886+ $( '#wikieditor-toolbar-table-sortable' ).click();
 887+ },
 888+ 'wikieditor-toolbar-tool-table-cancel': function() {
 889+ $(this).dialog( 'close' );
 890+ }
 891+ },
 892+ open: function() {
 893+ $( '#wikieditor-toolbar-table-dimensions-rows' ).focus();
 894+ if ( !( $(this).data( 'dialogkeypressset' ) ) ) {
 895+ $(this).data( 'dialogkeypressset', true );
 896+ // Execute the action associated with the first button
 897+ // when the user presses Enter
 898+ $(this).closest( '.ui-dialog' ).keypress( function( e ) {
 899+ if ( ( e.keyCode || e.which ) == 13 ) {
 900+ var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
 901+ button.click();
 902+ e.preventDefault();
1014903 }
1015 - $( '#wikieditor-toolbar-replace-success' )
1016 - .text( msg.get( 'wikieditor-toolbar-tool-replace-success', match.length ) )
1017 - .show();
1018 - $(this).data( 'offset', 0 );
1019 - } else {
 904+ });
 905+
 906+ // Make tabbing to a button and pressing
 907+ // Enter do what people expect
 908+ $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 909+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
 910+ });
 911+ }
 912+ }
 913+ }
 914+ },
 915+ 'search-and-replace': {
 916+ 'browsers': {
 917+ // Left-to-right languages
 918+ 'ltr': {
 919+ 'msie': false,
 920+ 'firefox': [['>=', 2]],
 921+ 'opera': false,
 922+ 'safari': [['>=', 3]],
 923+ 'chrome': [['>=', 3]]
 924+ },
 925+ // Right-to-left languages
 926+ 'rtl': {
 927+ 'msie': false,
 928+ 'firefox': [['>=', 2]],
 929+ 'opera': false,
 930+ 'safari': [['>=', 3]],
 931+ 'chrome': [['>=', 3]]
 932+ }
 933+ },
 934+ titleMsg: 'wikieditor-toolbar-tool-replace-title',
 935+ id: 'wikieditor-toolbar-replace-dialog',
 936+ html: '\
 937+ <div id="wikieditor-toolbar-replace-message">\
 938+ <div id="wikieditor-toolbar-replace-nomatch" rel="wikieditor-toolbar-tool-replace-nomatch"></div>\
 939+ <div id="wikieditor-toolbar-replace-success"></div>\
 940+ <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\
 941+ <div id="wikieditor-toolbar-replace-invalidregex"></div>\
 942+ </div>\
 943+ <fieldset>\
 944+ <div class="wikieditor-toolbar-field-wrapper">\
 945+ <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\
 946+ <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;" />\
 947+ </div>\
 948+ <div class="wikieditor-toolbar-field-wrapper">\
 949+ <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\
 950+ <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;" />\
 951+ </div>\
 952+ <div class="wikieditor-toolbar-field-wrapper">\
 953+ <input type="checkbox" id="wikieditor-toolbar-replace-case" />\
 954+ <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\
 955+ </div>\
 956+ <div class="wikieditor-toolbar-field-wrapper">\
 957+ <input type="checkbox" id="wikieditor-toolbar-replace-regex" />\
 958+ <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\
 959+ </div>\
 960+ </fieldset>',
 961+ init: function() {
 962+ var msg = mediaWiki.msg;
 963+ $(this).find( '[rel]' ).each( function() {
 964+ $(this).text( msg.get( $(this).attr( 'rel' ) ) );
 965+ });
 966+ // Set tabindexes on form fields
 967+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
 968+
 969+ // TODO: Find a cleaner way to share this function
 970+ $(this).data( 'replaceCallback', function( mode ) {
 971+ $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
 972+ var searchStr = $( '#wikieditor-toolbar-replace-search' ).val();
 973+ if ( searchStr == '' ) {
 974+ $( '#wikieditor-toolbar-replace-emptysearch' ).show();
 975+ return;
 976+ }
 977+ var replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val();
 978+ var flags = 'm';
 979+ var matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' );
 980+ var isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
 981+ if ( !matchCase ) {
 982+ flags += 'i';
 983+ }
 984+ if ( mode == 'replaceAll' ) {
 985+ flags += 'g';
 986+ }
 987+ if ( !isRegex ) {
 988+ searchStr = RegExp.escape( searchStr );
 989+ }
 990+ try {
 991+ var regex = new RegExp( searchStr, flags );
 992+ } catch( e ) {
 993+ $( '#wikieditor-toolbar-replace-invalidregex' )
 994+ .text( msg.get( 'wikieditor-toolbar-tool-replace-invalidregex',
 995+ e.message ) )
 996+ .show();
 997+ return;
 998+ }
 999+ var $textarea = $(this).data( 'context' ).$textarea;
 1000+ var text = $textarea.textSelection( 'getContents' );
 1001+ var match = false;
 1002+ var offset, s;
 1003+ if ( mode != 'replaceAll' ) {
 1004+ offset = $(this).data( 'offset' );
 1005+ s = text.substr( offset );
 1006+ match = s.match( regex );
 1007+ }
 1008+ if ( !match ) {
 1009+ // Search hit BOTTOM, continuing at TOP
 1010+ offset = 0;
 1011+ s = text;
 1012+ match = s.match( regex );
 1013+ }
 1014+
 1015+ if ( !match ) {
 1016+ $( '#wikieditor-toolbar-replace-nomatch' ).show();
 1017+ } else if ( mode == 'replaceAll' ) {
 1018+ // Instead of using repetitive .match() calls, we use one .match() call with /g
 1019+ // and indexOf() followed by substr() to find the offsets. This is actually
 1020+ // faster because our indexOf+substr loop is faster than a match loop, and the
 1021+ // /g match is so ridiculously fast that it's negligible.
 1022+ // FIXME: Repetitively calling encapsulateSelection() is probably the best strategy
 1023+ // in Firefox/Webkit, but in IE replacing the entire content once is better.
 1024+ var index;
 1025+ for ( var i = 0; i < match.length; i++ ) {
 1026+ index = s.indexOf( match[i] );
 1027+ if ( index == -1 ) {
 1028+ // This shouldn't happen
 1029+ break;
 1030+ }
 1031+ var matchedText = s.substr( index, match[i].length );
 1032+ s = s.substr( index + match[i].length );
 1033+
 1034+ var start = index + offset;
 1035+ var end = start + match[i].length;
10201036 // Make regex placeholder substitution ($1) work
1021 - var replace = isRegex ? match[0].replace( regex, replaceStr ): replaceStr;
1022 - var start = match.index + offset;
1023 - var end = start + match[0].length;
 1037+ var replace = isRegex ? matchedText.replace( regex, replaceStr ) : replaceStr;
10241038 var newEnd = start + replace.length;
1025 - var context = $( this ).data( 'context' );
1026 - $textarea.textSelection( 'setSelection', { 'start': start,
1027 - 'end': end } );
1028 - if ( mode == 'replace' ) {
1029 - $textarea
1030 - .textSelection( 'encapsulateSelection', {
 1039+ $textarea
 1040+ .textSelection( 'setSelection', { 'start': start, 'end': end } )
 1041+ .textSelection( 'encapsulateSelection', {
10311042 'peri': replace,
10321043 'replace': true } )
1033 - .textSelection( 'setSelection', {
1034 - 'start': start,
1035 - 'end': newEnd } );
1036 - }
1037 - $textarea.textSelection( 'scrollToCaretPosition' );
1038 - $textarea.textSelection( 'setSelection', { 'start': start,
1039 - 'end': mode == 'replace' ? newEnd : end } );
1040 - $( this ).data( 'offset', mode == 'replace' ? newEnd : end );
1041 - var textbox = typeof context.$iframe != 'undefined' ?
1042 - context.$iframe[0].contentWindow : $textarea[0];
1043 - textbox.focus();
 1044+ .textSelection( 'setSelection', { 'start': start, 'end': newEnd } );
 1045+ offset = newEnd;
10441046 }
1045 - });
1046 - },
1047 - dialog: {
1048 - width: 500,
1049 - dialogClass: 'wikiEditor-toolbar-dialog',
1050 - buttons: {
1051 - 'wikieditor-toolbar-tool-replace-button-findnext': function( e ) {
1052 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
1053 - $(this).data( 'replaceCallback' ).call( this, 'find' );
1054 - },
1055 - 'wikieditor-toolbar-tool-replace-button-replacenext': function( e ) {
1056 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
1057 - $(this).data( 'replaceCallback' ).call( this, 'replace' );
1058 - },
1059 - 'wikieditor-toolbar-tool-replace-button-replaceall': function( e ) {
1060 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
1061 - $(this).data( 'replaceCallback' ).call( this, 'replaceAll' );
1062 - },
1063 - 'wikieditor-toolbar-tool-replace-close': function() {
1064 - $(this).dialog( 'close' );
1065 - }
1066 - },
1067 - open: function() {
 1047+ $( '#wikieditor-toolbar-replace-success' )
 1048+ .text( msg.get( 'wikieditor-toolbar-tool-replace-success', match.length ) )
 1049+ .show();
10681050 $(this).data( 'offset', 0 );
1069 - $( '#wikieditor-toolbar-replace-search' ).focus();
1070 - $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
1071 - if ( !( $(this).data( 'onetimeonlystuff' ) ) ) {
1072 - $(this).data( 'onetimeonlystuff', true );
1073 - // Execute the action associated with the first button
1074 - // when the user presses Enter
1075 - $(this).closest( '.ui-dialog' ).keypress( function( e ) {
1076 - if ( ( e.keyCode || e.which ) == 13 ) {
1077 - var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
1078 - button.click();
1079 - e.preventDefault();
1080 - }
1081 - });
1082 - // Make tabbing to a button and pressing
1083 - // Enter do what people expect
1084 - $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
1085 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
1086 - });
 1051+ } else {
 1052+ // Make regex placeholder substitution ($1) work
 1053+ var replace = isRegex ? match[0].replace( regex, replaceStr ): replaceStr;
 1054+ var start = match.index + offset;
 1055+ var end = start + match[0].length;
 1056+ var newEnd = start + replace.length;
 1057+ var context = $( this ).data( 'context' );
 1058+ $textarea.textSelection( 'setSelection', { 'start': start,
 1059+ 'end': end } );
 1060+ if ( mode == 'replace' ) {
 1061+ $textarea
 1062+ .textSelection( 'encapsulateSelection', {
 1063+ 'peri': replace,
 1064+ 'replace': true } )
 1065+ .textSelection( 'setSelection', {
 1066+ 'start': start,
 1067+ 'end': newEnd } );
10871068 }
1088 - var dialog = $(this).closest( '.ui-dialog' );
1089 - var that = this;
1090 - var context = $(this).data( 'context' );
 1069+ $textarea.textSelection( 'scrollToCaretPosition' );
 1070+ $textarea.textSelection( 'setSelection', { 'start': start,
 1071+ 'end': mode == 'replace' ? newEnd : end } );
 1072+ $( this ).data( 'offset', mode == 'replace' ? newEnd : end );
10911073 var textbox = typeof context.$iframe != 'undefined' ?
1092 - context.$iframe[0].contentWindow.document : context.$textarea;
1093 -
1094 - $( textbox )
1095 - .bind( 'keypress.srdialog', function( e ) {
1096 - if ( ( e.keyCode || e.which ) == 13 ) {
1097 - // Enter
1098 - var button = dialog.data( 'dialogaction' ) || dialog.find( 'button:first' );
1099 - button.click();
1100 - e.preventDefault();
1101 - } else if ( ( e.keyCode || e.which ) == 27 ) {
1102 - // Escape
1103 - $(that).dialog( 'close' );
1104 - }
1105 - });
 1074+ context.$iframe[0].contentWindow : $textarea[0];
 1075+ textbox.focus();
 1076+ }
 1077+ });
 1078+ },
 1079+ dialog: {
 1080+ width: 500,
 1081+ dialogClass: 'wikiEditor-toolbar-dialog',
 1082+ buttons: {
 1083+ 'wikieditor-toolbar-tool-replace-button-findnext': function( e ) {
 1084+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
 1085+ $(this).data( 'replaceCallback' ).call( this, 'find' );
11061086 },
1107 - close: function() {
1108 - var context = $(this).data( 'context' );
1109 - var textbox = typeof context.$iframe != 'undefined' ?
1110 - context.$iframe[0].contentWindow.document : context.$textarea;
1111 - $( textbox ).unbind( 'keypress.srdialog' );
1112 - $(this).closest( '.ui-dialog' ).data( 'dialogaction', false );
 1087+ 'wikieditor-toolbar-tool-replace-button-replacenext': function( e ) {
 1088+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
 1089+ $(this).data( 'replaceCallback' ).call( this, 'replace' );
 1090+ },
 1091+ 'wikieditor-toolbar-tool-replace-button-replaceall': function( e ) {
 1092+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
 1093+ $(this).data( 'replaceCallback' ).call( this, 'replaceAll' );
 1094+ },
 1095+ 'wikieditor-toolbar-tool-replace-close': function() {
 1096+ $(this).dialog( 'close' );
11131097 }
 1098+ },
 1099+ open: function() {
 1100+ $(this).data( 'offset', 0 );
 1101+ $( '#wikieditor-toolbar-replace-search' ).focus();
 1102+ $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
 1103+ if ( !( $(this).data( 'onetimeonlystuff' ) ) ) {
 1104+ $(this).data( 'onetimeonlystuff', true );
 1105+ // Execute the action associated with the first button
 1106+ // when the user presses Enter
 1107+ $(this).closest( '.ui-dialog' ).keypress( function( e ) {
 1108+ if ( ( e.keyCode || e.which ) == 13 ) {
 1109+ var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
 1110+ button.click();
 1111+ e.preventDefault();
 1112+ }
 1113+ });
 1114+ // Make tabbing to a button and pressing
 1115+ // Enter do what people expect
 1116+ $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
 1117+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
 1118+ });
 1119+ }
 1120+ var dialog = $(this).closest( '.ui-dialog' );
 1121+ var that = this;
 1122+ var context = $(this).data( 'context' );
 1123+ var textbox = typeof context.$iframe != 'undefined' ?
 1124+ context.$iframe[0].contentWindow.document : context.$textarea;
 1125+
 1126+ $( textbox )
 1127+ .bind( 'keypress.srdialog', function( e ) {
 1128+ if ( ( e.keyCode || e.which ) == 13 ) {
 1129+ // Enter
 1130+ var button = dialog.data( 'dialogaction' ) || dialog.find( 'button:first' );
 1131+ button.click();
 1132+ e.preventDefault();
 1133+ } else if ( ( e.keyCode || e.which ) == 27 ) {
 1134+ // Escape
 1135+ $(that).dialog( 'close' );
 1136+ }
 1137+ });
 1138+ },
 1139+ close: function() {
 1140+ var context = $(this).data( 'context' );
 1141+ var textbox = typeof context.$iframe != 'undefined' ?
 1142+ context.$iframe[0].contentWindow.document : context.$textarea;
 1143+ $( textbox ).unbind( 'keypress.srdialog' );
 1144+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', false );
11141145 }
11151146 }
1116 - } } );
1117 - } );
 1147+ }
 1148+ } } );
11181149 } );
\ No newline at end of file
Index: trunk/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.js
@@ -415,17 +415,6 @@
416416 }
417417 }
418418 },
419 - 'tableCGD': {
420 - 'labelMsg': 'wikieditor-toolbar-tool-table',
421 - 'type': 'button',
422 - 'icon': 'insert-table.png',
423 - 'offset': [2, -1942],
424 - 'filters': [ '#wpTextbox1.toolbar-dialogs' ],
425 - 'action': {
426 - 'type': 'dialog',
427 - 'module': 'insert-table'
428 - }
429 - },
430419 'table': {
431420 'labelMsg': 'wikieditor-toolbar-tool-table',
432421 'type': 'button',
@@ -458,21 +447,6 @@
459448 }
460449 }
461450 }
462 - },
463 - 'search': {
464 - 'tools': {
465 - 'replace': {
466 - 'labelMsg': 'wikieditor-toolbar-tool-replace',
467 - 'type': 'button',
468 - 'icon': 'search-replace.png',
469 - 'offset': [-70, -214],
470 - 'filters': [ '#wpTextbox1.toolbar-dialogs' ],
471 - 'action': {
472 - 'type': 'dialog',
473 - 'module': 'search-and-replace'
474 - }
475 - }
476 - }
477451 }
478452 }
479453 },
Index: trunk/extensions/WikiEditor/WikiEditor.i18n.php
@@ -22,8 +22,6 @@
2323 'wikieditor-loading' => 'Loading',
2424 /* AddMediaWizard */
2525 'wikieditor-addMediaWizard-preference' => 'Enable "Add media wizard" gadget',
26 - /* Highlight */
27 - 'wikieditor-highlight-preference' => 'Enable syntax highlighting when editing',
2826 /* Preview */
2927 'wikieditor-preview-preference' => 'Enable side-by-side preview',
3028 'wikieditor-preview-tab' => 'Preview',

Follow-up revisions

RevisionCommit summaryAuthorDate
r827691. Re-introduces check if the browser supports the dialogs, which was removed...janpaul12300:01, 25 February 2011

Status & tagging log