[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 /** 2 * Configuration of Dialog module for wikiEditor 3 */ 4 /*jshint curly:false, noarg:false, quotmark:false, onevar:false */ 5 /*global alert */ 6 ( function ( $, mw ) { 7 8 var hasOwn = Object.prototype.hasOwnProperty; 9 10 $.wikiEditor.modules.dialogs.config = { 11 12 replaceIcons: function ( $textarea ) { 13 $textarea 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: 'file' } ) 17 .wikiEditor( 'removeFromToolbar', { section: 'main', group: 'insert', tool: 'reference' } ) 18 .wikiEditor( 'removeFromToolbar', { section: 'advanced', group: 'insert', tool: 'table' } ) 19 .wikiEditor( 'addToToolbar', { 20 section: 'main', 21 group: 'insert', 22 tools: { 23 'link': { 24 labelMsg: 'wikieditor-toolbar-tool-link', 25 type: 'button', 26 icon: 'insert-link.png', 27 offset: [2, -1654], 28 action: { 29 type: 'dialog', 30 module: 'insert-link' 31 } 32 }, 33 'file': { 34 labelMsg: 'wikieditor-toolbar-tool-file', 35 type: 'button', 36 icon: 'insert-file.png', 37 offset: [2, -1438], 38 action: { 39 type: 'dialog', 40 module: 'insert-file' 41 } 42 }, 43 'reference': { 44 labelMsg: 'wikieditor-toolbar-tool-reference', 45 filters: [ 'body.ns-subject' ], 46 type: 'button', 47 icon: 'insert-reference.png', 48 offset: [2, -1798], 49 action: { 50 type: 'dialog', 51 module: 'insert-reference' 52 } 53 } 54 } 55 } ) 56 .wikiEditor( 'addToToolbar', { 57 section: 'advanced', 58 group: 'insert', 59 tools: { 60 'table': { 61 labelMsg: 'wikieditor-toolbar-tool-table', 62 type: 'button', 63 icon: 'insert-table.png', 64 offset: [2, -1942], 65 action: { 66 type: 'dialog', 67 module: 'insert-table' 68 } 69 } 70 } 71 } ) 72 .wikiEditor( 'addToToolbar', { 73 section: 'advanced', 74 groups: { 75 'search': { 76 tools: { 77 'replace': { 78 labelMsg: 'wikieditor-toolbar-tool-replace', 79 type: 'button', 80 icon: 'search-replace.png', 81 offset: [-70, -214], 82 action: { 83 type: 'dialog', 84 module: 'search-and-replace' 85 } 86 } 87 } 88 } 89 } 90 } ); 91 }, 92 93 getDefaultConfig: function () { 94 return { 'dialogs': { 95 'insert-link': { 96 titleMsg: 'wikieditor-toolbar-tool-link-title', 97 id: 'wikieditor-toolbar-link-dialog', 98 html: '\ 99 <fieldset>\ 100 <div class="wikieditor-toolbar-field-wrapper">\ 101 <label for="wikieditor-toolbar-link-int-target" rel="wikieditor-toolbar-tool-link-int-target" id="wikieditor-toolbar-tool-link-int-target-label"></label>\ 102 <div id="wikieditor-toolbar-link-int-target-status"></div>\ 103 <input type="text" id="wikieditor-toolbar-link-int-target"/>\ 104 </div>\ 105 <div class="wikieditor-toolbar-field-wrapper">\ 106 <label for="wikieditor-toolbar-link-int-text" rel="wikieditor-toolbar-tool-link-int-text"></label>\ 107 <input type="text" id="wikieditor-toolbar-link-int-text"/>\ 108 </div>\ 109 <div class="wikieditor-toolbar-field-wrapper">\ 110 <div class="wikieditor-toolbar-floated-field-wrapper">\ 111 <input type="radio" id="wikieditor-toolbar-link-type-int" name="wikieditor-toolbar-link-type" selected/>\ 112 <label for="wikieditor-toolbar-link-type-int" rel="wikieditor-toolbar-tool-link-int"></label>\ 113 </div>\ 114 <div class="wikieditor-toolbar-floated-field-wrapper">\ 115 <input type="radio" id="wikieditor-toolbar-link-type-ext" name="wikieditor-toolbar-link-type"/>\ 116 <label for="wikieditor-toolbar-link-type-ext" rel="wikieditor-toolbar-tool-link-ext"></label>\ 117 </div>\ 118 </div>\ 119 </fieldset>', 120 121 init: function () { 122 function isExternalLink( s ) { 123 // The following things are considered to be external links: 124 // * Starts a URL protocol 125 // * Starts with www. 126 // All of these are potentially valid titles, and the latter two categories match about 6300 127 // titles in enwiki's ns0. Out of 6.9M titles, that's 0.09% 128 if ( typeof arguments.callee.regex === 'undefined' ) { 129 // Cache the regex 130 arguments.callee.regex = 131 new RegExp( "^(" + mw.config.get( 'wgUrlProtocols' ) + "|www\\.)", 'i' ); 132 } 133 return s.match( arguments.callee.regex ); 134 } 135 136 // Updates the status indicator above the target link 137 function updateWidget( status ) { 138 $( '#wikieditor-toolbar-link-int-target-status' ).children().hide(); 139 $( '#wikieditor-toolbar-link-int-target' ).parent() 140 .removeClass( 141 'status-invalid status-external status-notexists status-exists status-loading' 142 ); 143 if ( status ) { 144 $( '#wikieditor-toolbar-link-int-target-status-' + status ).show(); 145 $( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status ); 146 } 147 if ( status === 'invalid' ) { 148 $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' ) 149 .attr( 'disabled', true ) 150 .addClass( 'disabled' ); 151 } else { 152 $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' ) 153 .removeAttr( 'disabled' ) 154 .removeClass( 'disabled' ); 155 } 156 } 157 158 // Updates the UI to show if the page title being inputed by the user exists or not 159 // accepts parameter internal for bypassing external link detection 160 function updateExistence( internal ) { 161 // ensure the internal parameter is a boolean 162 if ( internal !== true ) { 163 internal = false; 164 } 165 // Abort previous request 166 var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' ); 167 if ( request ) { 168 request.abort(); 169 } 170 var target = $( '#wikieditor-toolbar-link-int-target' ).val(); 171 var cache = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'existencecache' ); 172 if ( hasOwn.call( cache, target ) ) { 173 updateWidget( cache[target] ); 174 return; 175 } 176 if ( target.replace( /^\s+$/,'' ) === '' ) { 177 // Hide the widget when the textbox is empty 178 updateWidget( false ); 179 return; 180 } 181 // If the forced internal paremter was not true, check if the target is an external link 182 if ( !internal && isExternalLink( target ) ) { 183 updateWidget( 'external' ); 184 return; 185 } 186 if ( target.indexOf( '|' ) !== -1 ) { 187 // Title contains | , which means it's invalid 188 // but confuses the API. Show invalid and bypass API 189 updateWidget( 'invalid' ); 190 return; 191 } 192 // Show loading spinner while waiting for the API to respond 193 updateWidget( 'loading' ); 194 // Call the API to check page status, saving the request object so it can be aborted if 195 // necessary. 196 // This used to request a page that would show whether or not the target exists, but we can 197 // also check whether it has the disambiguation property and still get existence information. 198 // If the Disambiguator extension is not installed then such a property won't be set. 199 $( '#wikieditor-toolbar-link-int-target-status' ).data( 200 'request', 201 ( new mw.Api() ).get( { 202 action: 'query', 203 prop: 'pageprops', 204 titles: target, 205 ppprop: 'disambiguation', 206 indexpageids: true 207 } ).done( function ( data ) { 208 var status; 209 if ( !data.query || !data.query.pages ) { 210 // This happens in some weird cases like interwiki links 211 status = false; 212 } else { 213 var page = data.query.pages[data.query.pageids[0]]; 214 status = 'exists'; 215 if ( page.missing !== undefined ) { 216 status = 'notexists'; 217 } else if ( page.invalid !== undefined ) { 218 status = 'invalid'; 219 } else if ( page.pageprops !== undefined ) { 220 status = 'disambig'; 221 } 222 } 223 // Cache the status of the link target if the force internal 224 // parameter was not passed 225 if ( !internal ) { 226 cache[target] = status; 227 } 228 updateWidget( status ); 229 } ) 230 ); 231 } 232 $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).click( function () { 233 if ( $( '#wikieditor-toolbar-link-type-ext' ).prop( 'checked' ) ) { 234 // Abort previous request 235 var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' ); 236 if ( request ) { 237 request.abort(); 238 } 239 updateWidget( 'external' ); 240 } 241 if ( $( '#wikieditor-toolbar-link-type-int' ).prop( 'checked' ) ) { 242 updateExistence( true ); 243 } 244 } ); 245 // Set labels of tabs based on rel values 246 $( this ).find( '[rel]' ).each( function () { 247 $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) ); 248 } ); 249 // Set tabindexes on form fields 250 $.wikiEditor.modules.dialogs.fn.setTabindexes( $( this ).find( 'input' ).not( '[tabindex]' ) ); 251 // Setup the tooltips in the textboxes 252 $( '#wikieditor-toolbar-link-int-target' ) 253 .data( 'tooltip', mw.msg( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) ); 254 $( '#wikieditor-toolbar-link-int-text' ) 255 .data( 'tooltip', mw.msg( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) ); 256 $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ) 257 .each( function () { 258 if ( $( this ).val() === '' ) { 259 $( this ) 260 .addClass( 'wikieditor-toolbar-dialog-hint' ) 261 .val( $( this ).data( 'tooltip' ) ) 262 .data( 'tooltip-mode', true ); 263 } 264 } ) 265 .focus( function () { 266 if ( $( this ).val() === $( this ).data( 'tooltip' ) ) { 267 $( this ) 268 .val( '' ) 269 .removeClass( 'wikieditor-toolbar-dialog-hint' ) 270 .data( 'tooltip-mode', false ); 271 } 272 } ) 273 .bind( 'change', function () { 274 if ( $( this ).val() !== $( this ).data( 'tooltip' ) ) { 275 $( this ) 276 .removeClass( 'wikieditor-toolbar-dialog-hint' ) 277 .data( 'tooltip-mode', false ); 278 } 279 } ) 280 .bind( 'blur', function () { 281 if ( $( this ).val() === '' ) { 282 $( this ) 283 .addClass( 'wikieditor-toolbar-dialog-hint' ) 284 .val( $( this ).data( 'tooltip' ) ) 285 .data( 'tooltip-mode', true ); 286 } 287 } ); 288 289 // Automatically copy the value of the internal link page title field to the link text field unless the 290 // user has changed the link text field - this is a convenience thing since most link texts are going to 291 // be the the same as the page title - Also change the internal/external radio button accordingly 292 $( '#wikieditor-toolbar-link-int-target' ).bind( 'change keydown paste cut', function () { 293 // $( this ).val() is the old value, before the keypress - Defer this until $( this ).val() has 294 // been updated 295 setTimeout( function () { 296 if ( isExternalLink( $( '#wikieditor-toolbar-link-int-target' ).val() ) ) { 297 $( '#wikieditor-toolbar-link-type-ext' ).prop( 'checked', true ); 298 updateWidget( 'external' ); 299 } else { 300 $( '#wikieditor-toolbar-link-type-int' ).prop( 'checked', true ); 301 updateExistence(); 302 } 303 /*jshint eqeqeq:false */ 304 if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched' ) ) { 305 if ( $( '#wikieditor-toolbar-link-int-target' ).val() == 306 $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip' ) 307 ) { 308 $( '#wikieditor-toolbar-link-int-text' ) 309 .addClass( 'wikieditor-toolbar-dialog-hint' ) 310 .val( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip' ) ) 311 .change(); 312 } else { 313 $( '#wikieditor-toolbar-link-int-text' ) 314 .val( $( '#wikieditor-toolbar-link-int-target' ).val() ) 315 .change(); 316 } 317 } 318 }, 0 ); 319 } ); 320 $( '#wikieditor-toolbar-link-int-text' ).bind( 'change keydown paste cut', function () { 321 var oldVal = $( this ).val(); 322 var that = this; 323 setTimeout( function () { 324 if ( $( that ).val() !== oldVal ) { 325 $( that ).data( 'untouched', false ); 326 } 327 }, 0 ); 328 } ); 329 // Add images to the page existence widget, which will be shown mutually exclusively to communicate if 330 // the page exists, does not exist or the title is invalid (like if it contains a | character) 331 var existsMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-exists' ); 332 var notexistsMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-notexists' ); 333 var invalidMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-invalid' ); 334 var externalMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-external' ); 335 var loadingMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-loading' ); 336 var disambigMsg = mw.msg( 'wikieditor-toolbar-tool-link-int-target-status-disambig' ); 337 $( '#wikieditor-toolbar-link-int-target-status' ) 338 .append( $( '<div>' ) 339 .attr( 'id', 'wikieditor-toolbar-link-int-target-status-exists' ) 340 .append( existsMsg ) 341 ) 342 .append( $( '<div>' ) 343 .attr( 'id', 'wikieditor-toolbar-link-int-target-status-notexists' ) 344 .append( notexistsMsg ) 345 ) 346 .append( $( '<div>' ) 347 .attr( 'id', 'wikieditor-toolbar-link-int-target-status-invalid' ) 348 .append( invalidMsg ) 349 ) 350 .append( $( '<div>' ) 351 .attr( 'id', 'wikieditor-toolbar-link-int-target-status-external' ) 352 .append( externalMsg ) 353 ) 354 .append( $( '<div>' ) 355 .attr( 'id', 'wikieditor-toolbar-link-int-target-status-loading' ) 356 .append( $( '<img>' ).attr( { 357 'src': $.wikiEditor.imgPath + 'dialogs/' + 'loading-small.gif', 358 'alt': loadingMsg, 359 'title': loadingMsg 360 } ) ) 361 ) 362 .append( $( '<div>' ) 363 .attr( 'id', 'wikieditor-toolbar-link-int-target-status-disambig' ) 364 .append( disambigMsg ) 365 ) 366 .data( 'existencecache', {} ) 367 .children().hide(); 368 369 $( '#wikieditor-toolbar-link-int-target' ) 370 .bind( 'keyup paste cut', function () { 371 // Cancel the running timer if applicable 372 if ( typeof $( this ).data( 'timerID' ) !== 'undefined' ) { 373 clearTimeout( $( this ).data( 'timerID' ) ); 374 } 375 // Delay fetch for a while 376 // FIXME: Make 120 configurable elsewhere 377 var timerID = setTimeout( updateExistence, 120 ); 378 $( this ).data( 'timerID', timerID ); 379 } ) 380 .change( function () { 381 // Cancel the running timer if applicable 382 if ( typeof $( this ).data( 'timerID' ) !== 'undefined' ) { 383 clearTimeout( $( this ).data( 'timerID' ) ); 384 } 385 // Fetch right now 386 updateExistence(); 387 } ); 388 389 // Title suggestions 390 $( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( { 391 fetch: function () { 392 var that = this; 393 var title = $( this ).val(); 394 395 if ( isExternalLink( title ) || title.indexOf( '|' ) !== -1 || title === '' ) { 396 $( this ).suggestions( 'suggestions', [] ); 397 return; 398 } 399 400 var cache = $( this ).data( 'suggcache' ); 401 if ( hasOwn.call( cache, title ) ) { 402 $( this ).suggestions( 'suggestions', cache[title] ); 403 return; 404 } 405 406 var request = $.ajax( { 407 url: mw.util.wikiScript( 'api' ), 408 data: { 409 action: 'opensearch', 410 search: title, 411 namespace: 0, 412 suggest: '', 413 format: 'json' 414 }, 415 dataType: 'json', 416 success: function ( data ) { 417 cache[title] = data[1]; 418 $( that ).suggestions( 'suggestions', data[1] ); 419 } 420 } ); 421 $( this ).data( 'request', request ); 422 }, 423 cancel: function () { 424 var request = $( this ).data( 'request' ); 425 if ( request ) 426 request.abort(); 427 } 428 } ); 429 }, 430 dialog: { 431 width: 500, 432 dialogClass: 'wikiEditor-toolbar-dialog', 433 buttons: { 434 'wikieditor-toolbar-tool-link-insert': function () { 435 function escapeInternalText( s ) { 436 return s.replace( /(\]{2,})/g, '<nowiki>$1</nowiki>' ); 437 } 438 function escapeExternalTarget( s ) { 439 return s.replace( / /g, '%20' ) 440 .replace( /\[/g, '%5B' ) 441 .replace( /\]/g, '%5D' ); 442 } 443 function escapeExternalText( s ) { 444 return s.replace( /(\]+)/g, '<nowiki>$1</nowiki>' ); 445 } 446 var insertText = ''; 447 var whitespace = $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace' ); 448 var target = $( '#wikieditor-toolbar-link-int-target' ).val(); 449 var text = $( '#wikieditor-toolbar-link-int-text' ).val(); 450 // check if the tooltips were passed as target or text 451 if ( $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip-mode' ) ) 452 target = ""; 453 if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip-mode' ) ) 454 text = ""; 455 if ( target === '' ) { 456 alert( mw.msg( 'wikieditor-toolbar-tool-link-empty' ) ); 457 return; 458 } 459 if ( $.trim( text ) === '' ) { 460 // [[Foo| ]] creates an invisible link 461 // Instead, generate [[Foo|]] 462 text = ''; 463 } 464 if ( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) ) { 465 // FIXME: Exactly how fragile is this? 466 if ( $( '#wikieditor-toolbar-link-int-target-status-invalid' ).is( ':visible' ) ) { 467 // Refuse to add links to invalid titles 468 alert( mw.msg( 'wikieditor-toolbar-tool-link-int-invalid' ) ); 469 return; 470 } 471 472 if ( target === text || !text.length ) 473 insertText = '[[' + target + ']]'; 474 else 475 insertText = '[[' + target + '|' + escapeInternalText( text ) + ']]'; 476 } else { 477 // Prepend http:// if there is no protocol 478 if ( !target.match( /^[a-z]+:\/\/./ ) ) 479 target = 'http://' + target; 480 481 // Detect if this is really an internal link in disguise 482 var match = target.match( $( this ).data( 'articlePathRegex' ) ); 483 if ( match && !$( this ).data( 'ignoreLooksInternal' ) ) { 484 var buttons = { }; 485 var that = this; 486 buttons[ mw.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal-int' ) ] = 487 function () { 488 $( '#wikieditor-toolbar-link-int-target' ).val( match[1] ).change(); 489 $( this ).dialog( 'close' ); 490 }; 491 buttons[ mw.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal-ext' ) ] = 492 function () { 493 $( that ).data( 'ignoreLooksInternal', true ); 494 $( that ).closest( '.ui-dialog' ).find( 'button:first' ).click(); 495 $( that ).data( 'ignoreLooksInternal', false ); 496 $( this ).dialog( 'close' ); 497 }; 498 $.wikiEditor.modules.dialogs.quickDialog( 499 mw.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal', match[1] ), 500 { buttons: buttons } 501 ); 502 return; 503 } 504 505 var escTarget = escapeExternalTarget( target ); 506 var escText = escapeExternalText( text ); 507 508 if ( escTarget === escText ) 509 insertText = escTarget; 510 else if ( text === '' ) 511 insertText = '[' + escTarget + ']'; 512 else 513 insertText = '[' + escTarget + ' ' + escText + ']'; 514 } 515 // Preserve whitespace in selection when replacing 516 if ( whitespace ) { 517 insertText = whitespace[0] + insertText + whitespace[1]; 518 } 519 $( this ).dialog( 'close' ); 520 $.wikiEditor.modules.toolbar.fn.doAction( $( this ).data( 'context' ), { 521 type: 'replace', 522 options: { 523 pre: insertText 524 } 525 }, $( this ) ); 526 527 // Blank form 528 $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' ); 529 $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ) 530 .attr( 'checked', '' ); 531 }, 532 'wikieditor-toolbar-tool-link-cancel': function () { 533 // Clear any saved selection state 534 var context = $( this ).data( 'context' ); 535 context.fn.restoreCursorAndScrollTop(); 536 $( this ).dialog( 'close' ); 537 } 538 }, 539 open: function () { 540 var target, text, type, matches; 541 542 // Obtain the server name without the protocol. wgServer may be protocol-relative 543 var serverName = mw.config.get( 'wgServer' ).replace( /^(https?:)?\/\//, '' ); 544 // Cache the articlepath regex 545 $( this ).data( 'articlePathRegex', new RegExp( 546 '^https?://' + $.escapeRE( serverName + mw.config.get( 'wgArticlePath' ) ) 547 .replace( /\\\$1/g, '(.*)' ) + '$' 548 ) ); 549 // Pre-fill the text fields based on the current selection 550 var context = $( this ).data( 'context' ); 551 // Restore and immediately save selection state, needed for inserting stuff later 552 context.fn.restoreCursorAndScrollTop(); 553 context.fn.saveCursorAndScrollTop(); 554 var selection = context.$textarea.textSelection( 'getSelection' ); 555 $( '#wikieditor-toolbar-link-int-target' ).focus(); 556 // Trigger the change event, so the link status indicator is up to date 557 $( '#wikieditor-toolbar-link-int-target' ).change(); 558 $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] ); 559 if ( selection !== '' ) { 560 if ( ( matches = selection.match( /^(\s*)\[\[([^\]\|]+)(\|([^\]\|]*))?\]\](\s*)$/ ) ) ) { 561 // [[foo|bar]] or [[foo]] 562 target = matches[2]; 563 text = ( matches[4] ? matches[4] : matches[2] ); 564 type = 'int'; 565 // Preserve whitespace when replacing 566 $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] ); 567 } else if ( ( matches = selection.match( /^(\s*)\[([^\] ]+)( ([^\]]+))?\](\s*)$/ ) ) ) { 568 // [http://www.example.com foo] or [http://www.example.com] 569 target = matches[2]; 570 text = ( matches[4] || '' ); 571 type = 'ext'; 572 // Preserve whitespace when replacing 573 $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] ); 574 } else { 575 // Trim any leading and trailing whitespace from the selection, 576 // but preserve it when replacing 577 target = text = $.trim( selection ); 578 if ( target.length < selection.length ) { 579 $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ 580 selection.substr( 0, selection.indexOf( target.charAt( 0 ) ) ), 581 selection.substr( 582 selection.lastIndexOf( target.charAt( target.length - 1 ) ) + 1 583 ) ] 584 ); 585 } 586 } 587 588 // Change the value by calling val() doesn't trigger the change event, so let's do that 589 // ourselves 590 if ( typeof text !== 'undefined' ) 591 $( '#wikieditor-toolbar-link-int-text' ).val( text ).change(); 592 if ( typeof target !== 'undefined' ) 593 $( '#wikieditor-toolbar-link-int-target' ).val( target ).change(); 594 if ( typeof type !== 'undefined' ) 595 $( '#wikieditor-toolbar-link-' + type ).prop( 'checked', true ); 596 } 597 $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', 598 $( '#wikieditor-toolbar-link-int-text' ).val() === 599 $( '#wikieditor-toolbar-link-int-target' ).val() || 600 $( '#wikieditor-toolbar-link-int-text' ).hasClass( 'wikieditor-toolbar-dialog-hint' ) 601 ); 602 $( '#wikieditor-toolbar-link-int-target' ).suggestions(); 603 604 // don't overwrite user's text 605 if ( selection !== '' ){ 606 $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', false ); 607 } 608 609 $( '#wikieditor-toolbar-link-int-text, #wikiedit-toolbar-link-int-target' ) 610 .each( function () { 611 if ( $( this ).val() === '' ) 612 $( this ).parent().find( 'label' ).show(); 613 } ); 614 615 if ( !$( this ).data( 'dialogkeypressset' ) ) { 616 $( this ).data( 'dialogkeypressset', true ); 617 // Execute the action associated with the first button 618 // when the user presses Enter 619 $( this ).closest( '.ui-dialog' ).keypress( function ( e ) { 620 if ( ( e.keyCode || e.which ) === 13 ) { 621 var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' ); 622 button.click(); 623 e.preventDefault(); 624 } 625 } ); 626 627 // Make tabbing to a button and pressing 628 // Enter do what people expect 629 $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () { 630 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this ); 631 } ); 632 } 633 } 634 } 635 }, 636 'insert-reference': { 637 titleMsg: 'wikieditor-toolbar-tool-reference-title', 638 id: 'wikieditor-toolbar-reference-dialog', 639 html: '\ 640 <div class="wikieditor-toolbar-dialog-wrapper">\ 641 <fieldset><div class="wikieditor-toolbar-table-form">\ 642 <div class="wikieditor-toolbar-field-wrapper">\ 643 <label for="wikieditor-toolbar-reference-text"\ 644 rel="wikieditor-toolbar-tool-reference-text"></label>\ 645 <input type="text" id="wikieditor-toolbar-reference-text"/>\ 646 </div>\ 647 </div></fieldset>\ 648 </div>', 649 init: function () { 650 // Insert translated strings into labels 651 $( this ).find( '[rel]' ).each( function () { 652 $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) ); 653 } ); 654 655 }, 656 dialog: { 657 dialogClass: 'wikiEditor-toolbar-dialog', 658 width: 590, 659 buttons: { 660 'wikieditor-toolbar-tool-reference-insert': function () { 661 var insertText = $( '#wikieditor-toolbar-reference-text' ).val(); 662 var whitespace = $( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace' ); 663 var attributes = $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes' ); 664 // Close the dialog 665 $( this ).dialog( 'close' ); 666 $.wikiEditor.modules.toolbar.fn.doAction( 667 $( this ).data( 'context' ), 668 { 669 type: 'replace', 670 options: { 671 pre: whitespace[0] + '<ref' + attributes + '>', 672 peri: insertText, 673 post: '</ref>' + whitespace[1] 674 } 675 }, 676 $( this ) 677 ); 678 // Restore form state 679 $( '#wikieditor-toolbar-reference-text' ).val( '' ); 680 }, 681 'wikieditor-toolbar-tool-reference-cancel': function () { 682 // Clear any saved selection state 683 var context = $( this ).data( 'context' ); 684 context.fn.restoreCursorAndScrollTop(); 685 $( this ).dialog( 'close' ); 686 } 687 }, 688 open: function () { 689 // Pre-fill the text fields based on the current selection 690 var context = $( this ).data( 'context' ); 691 // Restore and immediately save selection state, needed for inserting stuff later 692 context.fn.restoreCursorAndScrollTop(); 693 context.fn.saveCursorAndScrollTop(); 694 var selection = context.$textarea.textSelection( 'getSelection' ); 695 // set focus 696 $( '#wikieditor-toolbar-reference-text' ).focus(); 697 $( '#wikieditor-toolbar-reference-dialog' ) 698 .data( 'whitespace', [ '', '' ] ) 699 .data( 'attributes', '' ); 700 if ( selection !== '' ) { 701 var matches, text; 702 if ( ( matches = selection.match( /^(\s*)<ref([^\>]*)>([^<]*)<\/ref\>(\s*)$/ ) ) ) { 703 text = matches[3]; 704 // Preserve whitespace when replacing 705 $( '#wikieditor-toolbar-reference-dialog' ) 706 .data( 'whitespace', [ matches[1], matches[4] ] ); 707 $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes', matches[2] ); 708 } else { 709 text = selection; 710 } 711 $( '#wikieditor-toolbar-reference-text' ).val( text ); 712 } 713 if ( !( $( this ).data( 'dialogkeypressset' ) ) ) { 714 $( this ).data( 'dialogkeypressset', true ); 715 // Execute the action associated with the first button 716 // when the user presses Enter 717 $( this ).closest( '.ui-dialog' ).keypress( function ( e ) { 718 if ( ( e.keyCode || e.which ) === 13 ) { 719 var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' ); 720 button.click(); 721 e.preventDefault(); 722 } 723 } ); 724 // Make tabbing to a button and pressing 725 // Enter do what people expect 726 $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () { 727 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this ); 728 } ); 729 } 730 } 731 } 732 }, 733 'insert-file': { 734 titleMsg: 'wikieditor-toolbar-tool-file-title', 735 id: 'wikieditor-toolbar-file-dialog', 736 html: '\ 737 <fieldset>\ 738 <div class="wikieditor-toolbar-field-wrapper">\ 739 <label for="wikieditor-toolbar-file-target" rel="wikieditor-toolbar-file-target" id="wikieditor-toolbar-tool-file-target-label"></label>\ 740 <input type="text" id="wikieditor-toolbar-file-target"/>\ 741 </div>\ 742 <div class="wikieditor-toolbar-field-wrapper">\ 743 <label for="wikieditor-toolbar-file-caption" rel="wikieditor-toolbar-file-caption"></label>\ 744 <input type="text" id="wikieditor-toolbar-file-caption"/>\ 745 </div>\ 746 <div class="wikieditor-toolbar-file-options">\ 747 <div class="wikieditor-toolbar-field-wrapper">\ 748 <label for="wikieditor-toolbar-file-size" rel="wikieditor-toolbar-file-size"></label><br/>\ 749 <input type="text" id="wikieditor-toolbar-file-size" size="5"/>\ 750 </div>\ 751 <div class="wikieditor-toolbar-field-wrapper">\ 752 <label for="wikieditor-toolbar-file-float" rel="wikieditor-toolbar-file-float"></label><br/>\ 753 <select type="text" id="wikieditor-toolbar-file-float">\ 754 <option value="default" selected="selected" rel="wikieditor-toolbar-file-default"></option>\ 755 <option data-i18n-magic="img_none"></option>\ 756 <option data-i18n-magic="img_center"></option>\ 757 <option data-i18n-magic="img_left"></option>\ 758 <option data-i18n-magic="img_right"></option>\ 759 </select>\ 760 </div>\ 761 <div class="wikieditor-toolbar-field-wrapper">\ 762 <label for="wikieditor-toolbar-file-format" rel="wikieditor-toolbar-file-format"></label><br/>\ 763 <select type="text" id="wikieditor-toolbar-file-format">\ 764 <option selected="selected" data-i18n-magic="img_thumbnail">thumb</option>\ 765 <option data-i18n-magic="img_framed"></option>\ 766 <option data-i18n-magic="img_frameless"></option>\ 767 <option value="default" rel="wikieditor-toolbar-file-format-none"></option>\ 768 </select>\ 769 </div>\ 770 </div>\ 771 </fieldset>', 772 init: function () { 773 var magicWordsI18N = mw.config.get( 'wgWikiEditorMagicWords' ); 774 var defaultMsg = mw.msg( 'wikieditor-toolbar-file-default' ); 775 $( this ) 776 .find( '[data-i18n-magic]' ) 777 .text( function () { 778 return magicWordsI18N[ $( this ).attr( 'data-i18n-magic' ) ]; 779 } ) 780 .removeAttr( 'data-i18n-magic' ) 781 .end() 782 .find( '#wikieditor-toolbar-file-size' ) 783 .attr( 'placeholder', defaultMsg ) 784 // The message may be long in some languages 785 .attr( 'size', defaultMsg.length ) 786 .end() 787 .find( '[rel]' ) 788 .text( function () { 789 return mw.msg( $( this ).attr( 'rel' ) ); 790 } ) 791 .removeAttr( 'rel' ) 792 .end(); 793 }, 794 dialog: { 795 resizable: false, 796 dialogClass: 'wikiEditor-toolbar-dialog', 797 width: 590, 798 buttons: { 799 'wikieditor-toolbar-tool-file-insert': function () { 800 var fileName, caption, fileFloat, fileFormat, fileSize, fileTitle, 801 options, fileUse, 802 hasPxRgx = /.+px$/, 803 magicWordsI18N = mw.config.get( 'wgWikiEditorMagicWords' ); 804 fileName = $( '#wikieditor-toolbar-file-target' ).val(); 805 caption = $( '#wikieditor-toolbar-file-caption' ).val(); 806 fileFloat = $( '#wikieditor-toolbar-file-float' ).val(); 807 fileFormat = $( '#wikieditor-toolbar-file-format' ).val(); 808 fileSize = $( '#wikieditor-toolbar-file-size' ).val(); 809 // Append px to end to size if not already contains it 810 if ( fileSize !== '' && !hasPxRgx.test( fileSize ) ) { 811 fileSize += 'px'; 812 } 813 if ( fileName !== '' ) { 814 fileTitle = new mw.Title( fileName ); 815 // Append file namespace prefix to filename if not already contains it 816 if ( fileTitle.getNamespaceId() !== 6 ){ 817 fileTitle = new mw.Title( fileName, 6 ); 818 } 819 fileName = fileTitle.toText(); 820 } 821 options = [ fileSize, fileFormat, fileFloat ]; 822 // Filter empty values 823 options = $.grep( options, function ( val ) { 824 return val.length && val !== 'default'; 825 } ); 826 if ( caption.length ) { 827 options.push( caption ); 828 } 829 fileUse = options.length === 0 ? fileName : ( fileName + '|' + options.join( '|' ) ); 830 $( this ).dialog( 'close' ); 831 $.wikiEditor.modules.toolbar.fn.doAction( 832 $( this ).data( 'context' ), 833 { 834 type: 'replace', 835 options: { 836 pre: '[[', 837 peri: fileUse, 838 post: ']]', 839 ownline: true 840 } 841 }, 842 $( this ) 843 ); 844 845 // Restore form state 846 $( ['#wikieditor-toolbar-file-target', 847 '#wikieditor-toolbar-file-caption', 848 '#wikieditor-toolbar-file-size'].join( ',' ) 849 ).val( '' ); 850 $( '#wikieditor-toolbar-file-float' ).val( 'default' ); 851 /*jshint camelcase: false */ 852 $( '#wikieditor-toolbar-file-format' ).val( magicWordsI18N.img_thumbnail ); 853 }, 854 'wikieditor-toolbar-tool-file-cancel': function () { 855 $( this ).dialog( 'close' ); 856 } 857 }, 858 open: function () { 859 $( '#wikieditor-toolbar-file-target' ).focus(); 860 if ( !( $( this ).data( 'dialogkeypressset' ) ) ) { 861 $( this ).data( 'dialogkeypressset', true ); 862 // Execute the action associated with the first button 863 // when the user presses Enter 864 $( this ).closest( '.ui-dialog' ).keypress( function ( e ) { 865 if ( e.which === 13 ) { 866 var button = $( this ).data( 'dialogaction' ) || 867 $( 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 } 879 } 880 } 881 }, 882 'insert-table': { 883 titleMsg: 'wikieditor-toolbar-tool-table-title', 884 id: 'wikieditor-toolbar-table-dialog', 885 // FIXME: Localize 'x'? 886 html: '\ 887 <div class="wikieditor-toolbar-dialog-wrapper">\ 888 <fieldset><div class="wikieditor-toolbar-table-form">\ 889 <div class="wikieditor-toolbar-field-wrapper">\ 890 <input type="checkbox" id="wikieditor-toolbar-table-dimensions-header" checked/>\ 891 <label for="wikieditor-toolbar-table-dimensions-header"\ 892 rel="wikieditor-toolbar-tool-table-dimensions-header"></label>\ 893 </div>\ 894 <div class="wikieditor-toolbar-field-wrapper">\ 895 <input type="checkbox" id="wikieditor-toolbar-table-wikitable" checked/>\ 896 <label for="wikieditor-toolbar-table-wikitable" rel="wikieditor-toolbar-tool-table-wikitable"></label>\ 897 </div>\ 898 <div class="wikieditor-toolbar-field-wrapper">\ 899 <input type="checkbox" id="wikieditor-toolbar-table-sortable"/>\ 900 <label for="wikieditor-toolbar-table-sortable" rel="wikieditor-toolbar-tool-table-sortable"></label>\ 901 </div>\ 902 <div class="wikieditor-toolbar-table-dimension-fields">\ 903 <div class="wikieditor-toolbar-field-wrapper">\ 904 <label for="wikieditor-toolbar-table-dimensions-rows"\ 905 rel="wikieditor-toolbar-tool-table-dimensions-rows"></label><br/>\ 906 <input type="number" min="1" max="1000" id="wikieditor-toolbar-table-dimensions-rows" size="4"/>\ 907 </div>\ 908 <div class="wikieditor-toolbar-field-wrapper">\ 909 <label for="wikieditor-toolbar-table-dimensions-columns"\ 910 rel="wikieditor-toolbar-tool-table-dimensions-columns"></label><br/>\ 911 <input type="number" min="1" max="1000" id="wikieditor-toolbar-table-dimensions-columns" size="4"/>\ 912 </div>\ 913 </div>\ 914 </div></fieldset>\ 915 <div class="wikieditor-toolbar-table-preview-wrapper" >\ 916 <span rel="wikieditor-toolbar-tool-table-example"></span>\ 917 <div class="wikieditor-toolbar-table-preview-content">\ 918 <table id="wikieditor-toolbar-table-preview" class="wikieditor-toolbar-table-preview wikitable">\ 919 <thead>\ 920 <tr class="wikieditor-toolbar-table-preview-header">\ 921 <th rel="wikieditor-toolbar-tool-table-example-header"></th>\ 922 <th rel="wikieditor-toolbar-tool-table-example-header"></th>\ 923 <th rel="wikieditor-toolbar-tool-table-example-header"></th>\ 924 </tr>\ 925 </thead><tbody>\ 926 <tr class="wikieditor-toolbar-table-preview-hidden" style="display: none;">\ 927 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 928 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 929 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 930 </tr><tr>\ 931 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 932 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 933 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 934 </tr><tr>\ 935 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 936 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 937 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 938 </tr><tr>\ 939 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 940 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 941 <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\ 942 </tr>\ 943 </tbody>\ 944 </table>\ 945 </div>\ 946 </div></div>', 947 init: function () { 948 $( this ).find( '[rel]' ).each( function () { 949 $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) ); 950 } ); 951 // Set tabindexes on form fields 952 $.wikiEditor.modules.dialogs.fn.setTabindexes( $( this ).find( 'input' ).not( '[tabindex]' ) ); 953 954 $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 ); 955 $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 ); 956 $( '#wikieditor-toolbar-table-wikitable' ).click( function () { 957 $( '.wikieditor-toolbar-table-preview' ).toggleClass( 'wikitable' ); 958 } ); 959 960 // Hack for sortable preview: dynamically adding 961 // sortable class doesn't work, so we use a clone 962 $( '#wikieditor-toolbar-table-preview' ) 963 .clone() 964 .attr( 'id', 'wikieditor-toolbar-table-preview2' ) 965 .addClass( 'sortable' ) 966 .insertAfter( $( '#wikieditor-toolbar-table-preview' ) ) 967 .hide(); 968 969 mw.loader.using( 'jquery.tablesorter', function () { 970 $( '#wikieditor-toolbar-table-preview2' ).tablesorter(); 971 } ); 972 973 $( '#wikieditor-toolbar-table-sortable' ).click( function () { 974 // Swap the currently shown one clone with the other one 975 $( '#wikieditor-toolbar-table-preview' ) 976 .hide() 977 .attr( 'id', 'wikieditor-toolbar-table-preview3' ); 978 $( '#wikieditor-toolbar-table-preview2' ) 979 .attr( 'id', 'wikieditor-toolbar-table-preview' ) 980 .show(); 981 $( '#wikieditor-toolbar-table-preview3' ).attr( 'id', 'wikieditor-toolbar-table-preview2' ); 982 } ); 983 984 $( '#wikieditor-toolbar-table-dimensions-header' ).click( function () { 985 // Instead of show/hiding, switch the HTML around 986 // We do this because the sortable tables script styles the first row, 987 // visible or not 988 var headerHTML = $( '.wikieditor-toolbar-table-preview-header' ).html(); 989 var hiddenHTML = $( '.wikieditor-toolbar-table-preview-hidden' ).html(); 990 $( '.wikieditor-toolbar-table-preview-header' ).html( hiddenHTML ); 991 $( '.wikieditor-toolbar-table-preview-hidden' ).html( headerHTML ); 992 if ( typeof jQuery.fn.tablesorter === 'function' ) { 993 $( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' ) 994 .filter( '.sortable' ) 995 .tablesorter(); 996 } 997 } ); 998 }, 999 dialog: { 1000 resizable: false, 1001 dialogClass: 'wikiEditor-toolbar-dialog', 1002 width: 590, 1003 buttons: { 1004 'wikieditor-toolbar-tool-table-insert': function () { 1005 var rowsVal = $( '#wikieditor-toolbar-table-dimensions-rows' ).val(); 1006 var colsVal = $( '#wikieditor-toolbar-table-dimensions-columns' ).val(); 1007 var rows = parseInt( rowsVal, 10 ); 1008 var cols = parseInt( colsVal, 10 ); 1009 var header = $( '#wikieditor-toolbar-table-dimensions-header' ).prop( 'checked' ) ? 1 : 0; 1010 if ( isNaN( rows ) || isNaN( cols ) || String( rows ) !== rowsVal || String( cols ) !== colsVal || rowsVal < 0 || colsVal < 0 ) { 1011 alert( mw.msg( 'wikieditor-toolbar-tool-table-invalidnumber' ) ); 1012 return; 1013 } 1014 if ( rows + header === 0 || cols === 0 ) { 1015 alert( mw.msg( 'wikieditor-toolbar-tool-table-zero' ) ); 1016 return; 1017 } 1018 if ( ( rows * cols ) > 1000 ) { 1019 // 1000 is in the English message. The parameter replacement is kept for BC. 1020 alert( mw.msg( 'wikieditor-toolbar-tool-table-toomany', 1000 ) ); 1021 return; 1022 } 1023 var headerText = mw.msg( 'wikieditor-toolbar-tool-table-example-header' ); 1024 var normalText = mw.msg( 'wikieditor-toolbar-tool-table-example' ); 1025 var table = ""; 1026 for ( var r = 0; r < rows + header; r++ ) { 1027 table += "|-\n"; 1028 for ( var c = 0; c < cols; c++ ) { 1029 var isHeader = ( header && r === 0 ); 1030 var delim = isHeader ? '!' : '|'; 1031 if ( c > 0 ) { 1032 delim += delim; 1033 } 1034 table += delim + ' ' + ( isHeader ? headerText : normalText ) + ' '; 1035 } 1036 // Replace trailing space by newline 1037 // table[table.length - 1] is read-only 1038 table = table.substr( 0, table.length - 1 ) + "\n"; 1039 } 1040 var classes = []; 1041 if ( $( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) ) 1042 classes.push( 'wikitable' ); 1043 if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) ) 1044 classes.push( 'sortable' ); 1045 var classStr = classes.length > 0 ? ' class="' + classes.join( ' ' ) + '"' : ''; 1046 $( this ).dialog( 'close' ); 1047 $.wikiEditor.modules.toolbar.fn.doAction( 1048 $( this ).data( 'context' ), 1049 { 1050 type: 'replace', 1051 options: { 1052 pre: '{|' + classStr + "\n", 1053 peri: table, 1054 post: '|}', 1055 ownline: true 1056 } 1057 }, 1058 $( this ) 1059 ); 1060 1061 // Restore form state 1062 $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 ); 1063 $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 ); 1064 // Simulate clicks instead of setting values, so the according 1065 // actions are performed 1066 if ( !$( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) ) 1067 $( '#wikieditor-toolbar-table-dimensions-header' ).click(); 1068 if ( !$( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) ) 1069 $( '#wikieditor-toolbar-table-wikitable' ).click(); 1070 if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) ) 1071 $( '#wikieditor-toolbar-table-sortable' ).click(); 1072 }, 1073 'wikieditor-toolbar-tool-table-cancel': function () { 1074 $( this ).dialog( 'close' ); 1075 } 1076 }, 1077 open: function () { 1078 $( '#wikieditor-toolbar-table-dimensions-rows' ).focus(); 1079 if ( !( $( this ).data( 'dialogkeypressset' ) ) ) { 1080 $( this ).data( 'dialogkeypressset', true ); 1081 // Execute the action associated with the first button 1082 // when the user presses Enter 1083 $( this ).closest( '.ui-dialog' ).keypress( function ( e ) { 1084 if ( ( e.keyCode || e.which ) === 13 ) { 1085 var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' ); 1086 button.click(); 1087 e.preventDefault(); 1088 } 1089 } ); 1090 1091 // Make tabbing to a button and pressing 1092 // Enter do what people expect 1093 $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () { 1094 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this ); 1095 } ); 1096 } 1097 } 1098 } 1099 }, 1100 'search-and-replace': { 1101 'browsers': { 1102 // Left-to-right languages 1103 'ltr': { 1104 'msie': false, 1105 'firefox': [['>=', 2]], 1106 'opera': false, 1107 'safari': [['>=', 3]], 1108 'chrome': [['>=', 3]] 1109 }, 1110 // Right-to-left languages 1111 'rtl': { 1112 'msie': false, 1113 'firefox': [['>=', 2]], 1114 'opera': false, 1115 'safari': [['>=', 3]], 1116 'chrome': [['>=', 3]] 1117 } 1118 }, 1119 titleMsg: 'wikieditor-toolbar-tool-replace-title', 1120 id: 'wikieditor-toolbar-replace-dialog', 1121 html: '\ 1122 <div id="wikieditor-toolbar-replace-message">\ 1123 <div id="wikieditor-toolbar-replace-nomatch" rel="wikieditor-toolbar-tool-replace-nomatch"></div>\ 1124 <div id="wikieditor-toolbar-replace-success"></div>\ 1125 <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\ 1126 <div id="wikieditor-toolbar-replace-invalidregex"></div>\ 1127 </div>\ 1128 <fieldset>\ 1129 <div class="wikieditor-toolbar-field-wrapper">\ 1130 <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\ 1131 <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;"/>\ 1132 </div>\ 1133 <div class="wikieditor-toolbar-field-wrapper">\ 1134 <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\ 1135 <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;"/>\ 1136 </div>\ 1137 <div class="wikieditor-toolbar-field-wrapper">\ 1138 <input type="checkbox" id="wikieditor-toolbar-replace-case"/>\ 1139 <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\ 1140 </div>\ 1141 <div class="wikieditor-toolbar-field-wrapper">\ 1142 <input type="checkbox" id="wikieditor-toolbar-replace-regex"/>\ 1143 <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\ 1144 </div>\ 1145 </fieldset>', 1146 init: function () { 1147 $( this ).find( '[rel]' ).each( function () { 1148 $( this ).text( mw.msg( $( this ).attr( 'rel' ) ) ); 1149 } ); 1150 // Set tabindexes on form fields 1151 $.wikiEditor.modules.dialogs.fn.setTabindexes( $( this ).find( 'input' ).not( '[tabindex]' ) ); 1152 1153 // TODO: Find a cleaner way to share this function 1154 $( this ).data( 'replaceCallback', function ( mode ) { 1155 var offset, textRemainder, regex, index, i, 1156 start, end; 1157 1158 $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide(); 1159 1160 // Search string cannot be empty 1161 var searchStr = $( '#wikieditor-toolbar-replace-search' ).val(); 1162 if ( searchStr === '' ) { 1163 $( '#wikieditor-toolbar-replace-emptysearch' ).show(); 1164 return; 1165 } 1166 1167 // Replace string can be empty 1168 var replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val(); 1169 1170 // Prepare the regular expression flags 1171 var flags = 'm'; 1172 var matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' ); 1173 if ( !matchCase ) { 1174 flags += 'i'; 1175 } 1176 var isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' ); 1177 if ( !isRegex ) { 1178 searchStr = $.escapeRE( searchStr ); 1179 } 1180 if ( mode === 'replaceAll' ) { 1181 flags += 'g'; 1182 } 1183 1184 try { 1185 regex = new RegExp( searchStr, flags ); 1186 } catch ( e ) { 1187 $( '#wikieditor-toolbar-replace-invalidregex' ) 1188 .text( mw.msg( 'wikieditor-toolbar-tool-replace-invalidregex', 1189 e.message ) ) 1190 .show(); 1191 return; 1192 } 1193 1194 var $textarea = $( this ).data( 'context' ).$textarea; 1195 var text = $textarea.textSelection( 'getContents' ); 1196 var match = false; 1197 if ( mode !== 'replaceAll' ) { 1198 if ( mode === 'replace' ) { 1199 offset = $( this ).data( 'matchIndex' ); 1200 } else { 1201 offset = $( this ).data( 'offset' ); 1202 } 1203 textRemainder = text.substr( offset ); 1204 match = textRemainder.match( regex ); 1205 } 1206 if ( !match ) { 1207 // Search hit BOTTOM, continuing at TOP 1208 // TODO: Add a "Wrap around" option. 1209 offset = 0; 1210 textRemainder = text; 1211 match = textRemainder.match( regex ); 1212 } 1213 1214 if ( !match ) { 1215 $( '#wikieditor-toolbar-replace-nomatch' ).show(); 1216 } else if ( mode === 'replaceAll' ) { 1217 // Instead of using repetitive .match() calls, we use one .match() call with /g 1218 // and indexOf() followed by substr() to find the offsets. This is actually 1219 // faster because our indexOf+substr loop is faster than a match loop, and the 1220 // /g match is so ridiculously fast that it's negligible. 1221 // FIXME: Repetitively calling encapsulateSelection() is probably the best strategy 1222 // in Firefox/Webkit, but in IE replacing the entire content once is better. 1223 for ( i = 0; i < match.length; i++ ) { 1224 index = textRemainder.indexOf( match[i] ); 1225 if ( index === -1 ) { 1226 // This shouldn't happen 1227 break; 1228 } 1229 var matchedText = textRemainder.substr( index, match[i].length ); 1230 textRemainder = textRemainder.substr( index + match[i].length ); 1231 1232 start = index + offset; 1233 end = start + match[i].length; 1234 // Make regex placeholder substitution ($1) work 1235 var replace = isRegex ? matchedText.replace( regex, replaceStr ) : replaceStr; 1236 var newEnd = start + replace.length; 1237 $textarea 1238 .textSelection( 'setSelection', { 'start': start, 'end': end } ) 1239 .textSelection( 'encapsulateSelection', { 1240 'peri': replace, 1241 'replace': true } ) 1242 .textSelection( 'setSelection', { 'start': start, 'end': newEnd } ); 1243 offset = newEnd; 1244 } 1245 $( '#wikieditor-toolbar-replace-success' ) 1246 .text( mw.msg( 'wikieditor-toolbar-tool-replace-success', match.length ) ) 1247 .show(); 1248 $( this ).data( 'offset', 0 ); 1249 } else { 1250 1251 if ( mode === 'replace' ) { 1252 var actualReplacement; 1253 1254 if ( isRegex ) { 1255 // If backreferences (like $1) are used, the actual actual replacement string will be different 1256 actualReplacement = match[0].replace( regex, replaceStr ); 1257 } else { 1258 actualReplacement = replaceStr; 1259 } 1260 1261 if ( match ) { 1262 // Do the replacement 1263 $textarea.textSelection( 'encapsulateSelection', { 1264 'peri': actualReplacement, 1265 'replace': true } ); 1266 // Reload the text after replacement 1267 text = $textarea.textSelection( 'getContents' ); 1268 } 1269 1270 // Find the next instance 1271 offset = offset + match[0].length + actualReplacement.length; 1272 textRemainder = text.substr( offset ); 1273 match = textRemainder.match( regex ); 1274 1275 if ( match ) { 1276 start = offset + match.index; 1277 end = start + match[0].length; 1278 } else { 1279 // If no new string was found, try searching from the beginning. 1280 // TODO: Add a "Wrap around" option. 1281 textRemainder = text; 1282 match = textRemainder.match( regex ); 1283 if ( match ) { 1284 start = match.index; 1285 end = start + match[0].length; 1286 } else { 1287 // Give up 1288 start = 0; 1289 end = 0; 1290 } 1291 } 1292 } else { 1293 start = offset + match.index; 1294 end = start + match[0].length; 1295 } 1296 1297 $( this ).data( 'matchIndex', start ); 1298 1299 $textarea.textSelection( 'setSelection', { 1300 'start': start, 1301 'end': end 1302 } ); 1303 $textarea.textSelection( 'scrollToCaretPosition' ); 1304 $( this ).data( 'offset', end ); 1305 $textarea[0].focus(); 1306 } 1307 } ); 1308 }, 1309 dialog: { 1310 width: 500, 1311 dialogClass: 'wikiEditor-toolbar-dialog', 1312 modal: false, 1313 buttons: { 1314 'wikieditor-toolbar-tool-replace-button-findnext': function ( e ) { 1315 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', e.target ); 1316 $( this ).data( 'replaceCallback' ).call( this, 'find' ); 1317 }, 1318 'wikieditor-toolbar-tool-replace-button-replace': function ( e ) { 1319 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', e.target ); 1320 $( this ).data( 'replaceCallback' ).call( this, 'replace' ); 1321 }, 1322 'wikieditor-toolbar-tool-replace-button-replaceall': function ( e ) { 1323 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', e.target ); 1324 $( this ).data( 'replaceCallback' ).call( this, 'replaceAll' ); 1325 }, 1326 'wikieditor-toolbar-tool-replace-close': function () { 1327 $( this ).dialog( 'close' ); 1328 } 1329 }, 1330 open: function () { 1331 $( this ).data( 'offset', 0 ); 1332 $( this ).data( 'matchIndex', 0 ); 1333 1334 $( '#wikieditor-toolbar-replace-search' ).focus(); 1335 $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide(); 1336 if ( !( $( this ).data( 'onetimeonlystuff' ) ) ) { 1337 $( this ).data( 'onetimeonlystuff', true ); 1338 // Execute the action associated with the first button 1339 // when the user presses Enter 1340 $( this ).closest( '.ui-dialog' ).keypress( function ( e ) { 1341 if ( ( e.keyCode || e.which ) === 13 ) { 1342 var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' ); 1343 button.click(); 1344 e.preventDefault(); 1345 } 1346 } ); 1347 // Make tabbing to a button and pressing 1348 // Enter do what people expect 1349 $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function () { 1350 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this ); 1351 } ); 1352 } 1353 var dialog = $( this ).closest( '.ui-dialog' ); 1354 var that = this; 1355 var context = $( this ).data( 'context' ); 1356 var textbox = context.$textarea; 1357 1358 $( textbox ) 1359 .bind( 'keypress.srdialog', function ( e ) { 1360 if ( e.which === 13 ) { 1361 // Enter 1362 var button = dialog.data( 'dialogaction' ) || dialog.find( 'button:first' ); 1363 button.click(); 1364 e.preventDefault(); 1365 } else if ( e.which === 27 ) { 1366 // Escape 1367 $( that ).dialog( 'close' ); 1368 } 1369 } ); 1370 }, 1371 close: function () { 1372 var context = $( this ).data( 'context' ); 1373 var textbox = context.$textarea; 1374 $( textbox ).unbind( 'keypress.srdialog' ); 1375 $( this ).closest( '.ui-dialog' ).data( 'dialogaction', false ); 1376 } 1377 } 1378 } 1379 } }; 1380 } 1381 1382 }; 1383 1384 }( jQuery, mediaWiki ) );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |