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