| [ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 var wpLink; 2 3 (function($){ 4 var inputs = {}, rivers = {}, ed, River, Query; 5 6 wpLink = { 7 timeToTriggerRiver: 150, 8 minRiverAJAXDuration: 200, 9 riverBottomThreshold: 5, 10 keySensitivity: 100, 11 lastSearch: '', 12 textarea: '', 13 14 init : function() { 15 inputs.dialog = $('#wp-link'); 16 inputs.submit = $('#wp-link-submit'); 17 // URL 18 inputs.url = $('#url-field'); 19 inputs.nonce = $('#_ajax_linking_nonce'); 20 // Secondary options 21 inputs.title = $('#link-title-field'); 22 // Advanced Options 23 inputs.openInNewTab = $('#link-target-checkbox'); 24 inputs.search = $('#search-field'); 25 // Build Rivers 26 rivers.search = new River( $('#search-results') ); 27 rivers.recent = new River( $('#most-recent-results') ); 28 rivers.elements = $('.query-results', inputs.dialog); 29 30 // Bind event handlers 31 inputs.dialog.keydown( wpLink.keydown ); 32 inputs.dialog.keyup( wpLink.keyup ); 33 inputs.submit.click( function(e){ 34 e.preventDefault(); 35 wpLink.update(); 36 }); 37 $('#wp-link-cancel').click( function(e){ 38 e.preventDefault(); 39 wpLink.close(); 40 }); 41 $('#internal-toggle').click( wpLink.toggleInternalLinking ); 42 43 rivers.elements.bind('river-select', wpLink.updateFields ); 44 45 inputs.search.keyup( wpLink.searchInternalLinks ); 46 47 inputs.dialog.bind('wpdialogrefresh', wpLink.refresh); 48 inputs.dialog.bind('wpdialogbeforeopen', wpLink.beforeOpen); 49 inputs.dialog.bind('wpdialogclose', wpLink.onClose); 50 }, 51 52 beforeOpen : function() { 53 wpLink.range = null; 54 55 if ( ! wpLink.isMCE() && document.selection ) { 56 wpLink.textarea.focus(); 57 wpLink.range = document.selection.createRange(); 58 } 59 }, 60 61 open : function() { 62 if ( !wpActiveEditor ) 63 return; 64 65 this.textarea = $('#'+wpActiveEditor).get(0); 66 67 // Initialize the dialog if necessary (html mode). 68 if ( ! inputs.dialog.data('wpdialog') ) { 69 inputs.dialog.wpdialog({ 70 title: wpLinkL10n.title, 71 width: 480, 72 height: 'auto', 73 modal: true, 74 dialogClass: 'wp-dialog', 75 zIndex: 300000 76 }); 77 } 78 79 inputs.dialog.wpdialog('open'); 80 }, 81 82 isMCE : function() { 83 return tinyMCEPopup && ( ed = tinyMCEPopup.editor ) && ! ed.isHidden(); 84 }, 85 86 refresh : function() { 87 // Refresh rivers (clear links, check visibility) 88 rivers.search.refresh(); 89 rivers.recent.refresh(); 90 91 if ( wpLink.isMCE() ) 92 wpLink.mceRefresh(); 93 else 94 wpLink.setDefaultValues(); 95 96 // Focus the URL field and highlight its contents. 97 // If this is moved above the selection changes, 98 // IE will show a flashing cursor over the dialog. 99 inputs.url.focus()[0].select(); 100 // Load the most recent results if this is the first time opening the panel. 101 if ( ! rivers.recent.ul.children().length ) 102 rivers.recent.ajax(); 103 }, 104 105 mceRefresh : function() { 106 var e; 107 ed = tinyMCEPopup.editor; 108 109 tinyMCEPopup.restoreSelection(); 110 111 // If link exists, select proper values. 112 if ( e = ed.dom.getParent(ed.selection.getNode(), 'A') ) { 113 // Set URL and description. 114 inputs.url.val( ed.dom.getAttrib(e, 'href') ); 115 inputs.title.val( ed.dom.getAttrib(e, 'title') ); 116 // Set open in new tab. 117 if ( "_blank" == ed.dom.getAttrib(e, 'target') ) 118 inputs.openInNewTab.prop('checked', true); 119 // Update save prompt. 120 inputs.submit.val( wpLinkL10n.update ); 121 122 // If there's no link, set the default values. 123 } else { 124 wpLink.setDefaultValues(); 125 } 126 127 tinyMCEPopup.storeSelection(); 128 }, 129 130 close : function() { 131 if ( wpLink.isMCE() ) 132 tinyMCEPopup.close(); 133 else 134 inputs.dialog.wpdialog('close'); 135 }, 136 137 onClose: function() { 138 if ( ! wpLink.isMCE() ) { 139 wpLink.textarea.focus(); 140 if ( wpLink.range ) { 141 wpLink.range.moveToBookmark( wpLink.range.getBookmark() ); 142 wpLink.range.select(); 143 } 144 } 145 }, 146 147 getAttrs : function() { 148 return { 149 href : inputs.url.val(), 150 title : inputs.title.val(), 151 target : inputs.openInNewTab.prop('checked') ? '_blank' : '' 152 }; 153 }, 154 155 update : function() { 156 if ( wpLink.isMCE() ) 157 wpLink.mceUpdate(); 158 else 159 wpLink.htmlUpdate(); 160 }, 161 162 htmlUpdate : function() { 163 var attrs, html, begin, end, cursor, 164 textarea = wpLink.textarea; 165 166 if ( ! textarea ) 167 return; 168 169 attrs = wpLink.getAttrs(); 170 171 // If there's no href, return. 172 if ( ! attrs.href || attrs.href == 'http://' ) 173 return; 174 175 // Build HTML 176 html = '<a href="' + attrs.href + '"'; 177 178 if ( attrs.title ) 179 html += ' title="' + attrs.title + '"'; 180 if ( attrs.target ) 181 html += ' target="' + attrs.target + '"'; 182 183 html += '>'; 184 185 // Insert HTML 186 if ( document.selection && wpLink.range ) { 187 // IE 188 // Note: If no text is selected, IE will not place the cursor 189 // inside the closing tag. 190 textarea.focus(); 191 wpLink.range.text = html + wpLink.range.text + '</a>'; 192 wpLink.range.moveToBookmark( wpLink.range.getBookmark() ); 193 wpLink.range.select(); 194 195 wpLink.range = null; 196 } else if ( typeof textarea.selectionStart !== 'undefined' ) { 197 // W3C 198 begin = textarea.selectionStart; 199 end = textarea.selectionEnd; 200 selection = textarea.value.substring( begin, end ); 201 html = html + selection + '</a>'; 202 cursor = begin + html.length; 203 204 // If no next is selected, place the cursor inside the closing tag. 205 if ( begin == end ) 206 cursor -= '</a>'.length; 207 208 textarea.value = textarea.value.substring( 0, begin ) 209 + html 210 + textarea.value.substring( end, textarea.value.length ); 211 212 // Update cursor position 213 textarea.selectionStart = textarea.selectionEnd = cursor; 214 } 215 216 wpLink.close(); 217 textarea.focus(); 218 }, 219 220 mceUpdate : function() { 221 var ed = tinyMCEPopup.editor, 222 attrs = wpLink.getAttrs(), 223 e, b; 224 225 tinyMCEPopup.restoreSelection(); 226 e = ed.dom.getParent(ed.selection.getNode(), 'A'); 227 228 // If the values are empty, unlink and return 229 if ( ! attrs.href || attrs.href == 'http://' ) { 230 if ( e ) { 231 tinyMCEPopup.execCommand("mceBeginUndoLevel"); 232 b = ed.selection.getBookmark(); 233 ed.dom.remove(e, 1); 234 ed.selection.moveToBookmark(b); 235 tinyMCEPopup.execCommand("mceEndUndoLevel"); 236 wpLink.close(); 237 } 238 return; 239 } 240 241 tinyMCEPopup.execCommand("mceBeginUndoLevel"); 242 243 if (e == null) { 244 ed.getDoc().execCommand("unlink", false, null); 245 tinyMCEPopup.execCommand("mceInsertLink", false, "#mce_temp_url#", {skip_undo : 1}); 246 247 tinymce.each(ed.dom.select("a"), function(n) { 248 if (ed.dom.getAttrib(n, 'href') == '#mce_temp_url#') { 249 e = n; 250 ed.dom.setAttribs(e, attrs); 251 } 252 }); 253 254 // Sometimes WebKit lets a user create a link where 255 // they shouldn't be able to. In this case, CreateLink 256 // injects "#mce_temp_url#" into their content. Fix it. 257 if ( $(e).text() == '#mce_temp_url#' ) { 258 ed.dom.remove(e); 259 e = null; 260 } 261 } else { 262 ed.dom.setAttribs(e, attrs); 263 } 264 265 // Don't move caret if selection was image 266 if ( e && (e.childNodes.length != 1 || e.firstChild.nodeName != 'IMG') ) { 267 ed.focus(); 268 ed.selection.select(e); 269 ed.selection.collapse(0); 270 tinyMCEPopup.storeSelection(); 271 } 272 273 tinyMCEPopup.execCommand("mceEndUndoLevel"); 274 wpLink.close(); 275 }, 276 277 updateFields : function( e, li, originalEvent ) { 278 inputs.url.val( li.children('.item-permalink').val() ); 279 inputs.title.val( li.hasClass('no-title') ? '' : li.children('.item-title').text() ); 280 if ( originalEvent && originalEvent.type == "click" ) 281 inputs.url.focus(); 282 }, 283 setDefaultValues : function() { 284 // Set URL and description to defaults. 285 // Leave the new tab setting as-is. 286 inputs.url.val('http://'); 287 inputs.title.val(''); 288 289 // Update save prompt. 290 inputs.submit.val( wpLinkL10n.save ); 291 }, 292 293 searchInternalLinks : function() { 294 var t = $(this), waiting, 295 search = t.val(); 296 297 if ( search.length > 2 ) { 298 rivers.recent.hide(); 299 rivers.search.show(); 300 301 // Don't search if the keypress didn't change the title. 302 if ( wpLink.lastSearch == search ) 303 return; 304 305 wpLink.lastSearch = search; 306 waiting = t.siblings('img.waiting').show(); 307 308 rivers.search.change( search ); 309 rivers.search.ajax( function(){ waiting.hide(); }); 310 } else { 311 rivers.search.hide(); 312 rivers.recent.show(); 313 } 314 }, 315 316 next : function() { 317 rivers.search.next(); 318 rivers.recent.next(); 319 }, 320 prev : function() { 321 rivers.search.prev(); 322 rivers.recent.prev(); 323 }, 324 325 keydown : function( event ) { 326 var fn, key = $.ui.keyCode; 327 328 switch( event.which ) { 329 case key.UP: 330 fn = 'prev'; 331 case key.DOWN: 332 fn = fn || 'next'; 333 clearInterval( wpLink.keyInterval ); 334 wpLink[ fn ](); 335 wpLink.keyInterval = setInterval( wpLink[ fn ], wpLink.keySensitivity ); 336 break; 337 default: 338 return; 339 } 340 event.preventDefault(); 341 }, 342 keyup: function( event ) { 343 var key = $.ui.keyCode; 344 345 switch( event.which ) { 346 case key.ESCAPE: 347 event.stopImmediatePropagation(); 348 if ( ! $(document).triggerHandler( 'wp_CloseOnEscape', [{ event: event, what: 'wplink', cb: wpLink.close }] ) ) 349 wpLink.close(); 350 351 return false; 352 break; 353 case key.UP: 354 case key.DOWN: 355 clearInterval( wpLink.keyInterval ); 356 break; 357 default: 358 return; 359 } 360 event.preventDefault(); 361 }, 362 363 delayedCallback : function( func, delay ) { 364 var timeoutTriggered, funcTriggered, funcArgs, funcContext; 365 366 if ( ! delay ) 367 return func; 368 369 setTimeout( function() { 370 if ( funcTriggered ) 371 return func.apply( funcContext, funcArgs ); 372 // Otherwise, wait. 373 timeoutTriggered = true; 374 }, delay); 375 376 return function() { 377 if ( timeoutTriggered ) 378 return func.apply( this, arguments ); 379 // Otherwise, wait. 380 funcArgs = arguments; 381 funcContext = this; 382 funcTriggered = true; 383 }; 384 }, 385 386 toggleInternalLinking : function( event ) { 387 var panel = $('#search-panel'), 388 widget = inputs.dialog.wpdialog('widget'), 389 // We're about to toggle visibility; it's currently the opposite 390 visible = !panel.is(':visible'), 391 win = $(window); 392 393 $(this).toggleClass('toggle-arrow-active', visible); 394 395 inputs.dialog.height('auto'); 396 panel.slideToggle( 300, function() { 397 setUserSetting('wplink', visible ? '1' : '0'); 398 inputs[ visible ? 'search' : 'url' ].focus(); 399 400 // Move the box if the box is now expanded, was opened in a collapsed state, 401 // and if it needs to be moved. (Judged by bottom not being positive or 402 // bottom being smaller than top.) 403 var scroll = win.scrollTop(), 404 top = widget.offset().top, 405 bottom = top + widget.outerHeight(), 406 diff = bottom - win.height(); 407 408 if ( diff > scroll ) { 409 widget.animate({'top': diff < top ? top - diff : scroll }, 200); 410 } 411 }); 412 event.preventDefault(); 413 } 414 } 415 416 River = function( element, search ) { 417 var self = this; 418 this.element = element; 419 this.ul = element.children('ul'); 420 this.waiting = element.find('.river-waiting'); 421 422 this.change( search ); 423 this.refresh(); 424 425 element.scroll( function(){ self.maybeLoad(); }); 426 element.delegate('li', 'click', function(e){ self.select( $(this), e ); }); 427 }; 428 429 $.extend( River.prototype, { 430 refresh: function() { 431 this.deselect(); 432 this.visible = this.element.is(':visible'); 433 }, 434 show: function() { 435 if ( ! this.visible ) { 436 this.deselect(); 437 this.element.show(); 438 this.visible = true; 439 } 440 }, 441 hide: function() { 442 this.element.hide(); 443 this.visible = false; 444 }, 445 // Selects a list item and triggers the river-select event. 446 select: function( li, event ) { 447 var liHeight, elHeight, liTop, elTop; 448 449 if ( li.hasClass('unselectable') || li == this.selected ) 450 return; 451 452 this.deselect(); 453 this.selected = li.addClass('selected'); 454 // Make sure the element is visible 455 liHeight = li.outerHeight(); 456 elHeight = this.element.height(); 457 liTop = li.position().top; 458 elTop = this.element.scrollTop(); 459 460 if ( liTop < 0 ) // Make first visible element 461 this.element.scrollTop( elTop + liTop ); 462 else if ( liTop + liHeight > elHeight ) // Make last visible element 463 this.element.scrollTop( elTop + liTop - elHeight + liHeight ); 464 465 // Trigger the river-select event 466 this.element.trigger('river-select', [ li, event, this ]); 467 }, 468 deselect: function() { 469 if ( this.selected ) 470 this.selected.removeClass('selected'); 471 this.selected = false; 472 }, 473 prev: function() { 474 if ( ! this.visible ) 475 return; 476 477 var to; 478 if ( this.selected ) { 479 to = this.selected.prev('li'); 480 if ( to.length ) 481 this.select( to ); 482 } 483 }, 484 next: function() { 485 if ( ! this.visible ) 486 return; 487 488 var to = this.selected ? this.selected.next('li') : $('li:not(.unselectable):first', this.element); 489 if ( to.length ) 490 this.select( to ); 491 }, 492 ajax: function( callback ) { 493 var self = this, 494 delay = this.query.page == 1 ? 0 : wpLink.minRiverAJAXDuration, 495 response = wpLink.delayedCallback( function( results, params ) { 496 self.process( results, params ); 497 if ( callback ) 498 callback( results, params ); 499 }, delay ); 500 501 this.query.ajax( response ); 502 }, 503 change: function( search ) { 504 if ( this.query && this._search == search ) 505 return; 506 507 this._search = search; 508 this.query = new Query( search ); 509 this.element.scrollTop(0); 510 }, 511 process: function( results, params ) { 512 var list = '', alt = true, classes = '', 513 firstPage = params.page == 1; 514 515 if ( !results ) { 516 if ( firstPage ) { 517 list += '<li class="unselectable"><span class="item-title"><em>' 518 + wpLinkL10n.noMatchesFound 519 + '</em></span></li>'; 520 } 521 } else { 522 $.each( results, function() { 523 classes = alt ? 'alternate' : ''; 524 classes += this['title'] ? '' : ' no-title'; 525 list += classes ? '<li class="' + classes + '">' : '<li>'; 526 list += '<input type="hidden" class="item-permalink" value="' + this['permalink'] + '" />'; 527 list += '<span class="item-title">'; 528 list += this['title'] ? this['title'] : wpLinkL10n.noTitle; 529 list += '</span><span class="item-info">' + this['info'] + '</span></li>'; 530 alt = ! alt; 531 }); 532 } 533 534 this.ul[ firstPage ? 'html' : 'append' ]( list ); 535 }, 536 maybeLoad: function() { 537 var self = this, 538 el = this.element, 539 bottom = el.scrollTop() + el.height(); 540 541 if ( ! this.query.ready() || bottom < this.ul.height() - wpLink.riverBottomThreshold ) 542 return; 543 544 setTimeout(function() { 545 var newTop = el.scrollTop(), 546 newBottom = newTop + el.height(); 547 548 if ( ! self.query.ready() || newBottom < self.ul.height() - wpLink.riverBottomThreshold ) 549 return; 550 551 self.waiting.show(); 552 el.scrollTop( newTop + self.waiting.outerHeight() ); 553 554 self.ajax( function() { self.waiting.hide(); }); 555 }, wpLink.timeToTriggerRiver ); 556 } 557 }); 558 559 Query = function( search ) { 560 this.page = 1; 561 this.allLoaded = false; 562 this.querying = false; 563 this.search = search; 564 }; 565 566 $.extend( Query.prototype, { 567 ready: function() { 568 return !( this.querying || this.allLoaded ); 569 }, 570 ajax: function( callback ) { 571 var self = this, 572 query = { 573 action : 'wp-link-ajax', 574 page : this.page, 575 '_ajax_linking_nonce' : inputs.nonce.val() 576 }; 577 578 if ( this.search ) 579 query.search = this.search; 580 581 this.querying = true; 582 583 $.post( ajaxurl, query, function(r) { 584 self.page++; 585 self.querying = false; 586 self.allLoaded = !r; 587 callback( r, query ); 588 }, "json" ); 589 } 590 }); 591 592 $(document).ready( wpLink.init ); 593 })(jQuery);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Fri May 25 03:56:23 2012 | Hosted by follow the white rabbit. |