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