wplink.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. /**
  2. * @output wp-includes/js/wplink.js
  3. */
  4. /* global wpLink */
  5. ( function( $, wpLinkL10n, wp ) {
  6. var editor, searchTimer, River, Query, correctedURL,
  7. emailRegexp = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i,
  8. urlRegexp = /^(https?|ftp):\/\/[A-Z0-9.-]+\.[A-Z]{2,63}[^ "]*$/i,
  9. inputs = {},
  10. rivers = {},
  11. isTouch = ( 'ontouchend' in document );
  12. function getLink() {
  13. if ( editor ) {
  14. return editor.$( 'a[data-wplink-edit="true"]' );
  15. }
  16. return null;
  17. }
  18. window.wpLink = {
  19. timeToTriggerRiver: 150,
  20. minRiverAJAXDuration: 200,
  21. riverBottomThreshold: 5,
  22. keySensitivity: 100,
  23. lastSearch: '',
  24. textarea: '',
  25. modalOpen: false,
  26. init: function() {
  27. inputs.wrap = $('#wp-link-wrap');
  28. inputs.dialog = $( '#wp-link' );
  29. inputs.backdrop = $( '#wp-link-backdrop' );
  30. inputs.submit = $( '#wp-link-submit' );
  31. inputs.close = $( '#wp-link-close' );
  32. // Input.
  33. inputs.text = $( '#wp-link-text' );
  34. inputs.url = $( '#wp-link-url' );
  35. inputs.nonce = $( '#_ajax_linking_nonce' );
  36. inputs.openInNewTab = $( '#wp-link-target' );
  37. inputs.search = $( '#wp-link-search' );
  38. // Build rivers.
  39. rivers.search = new River( $( '#search-results' ) );
  40. rivers.recent = new River( $( '#most-recent-results' ) );
  41. rivers.elements = inputs.dialog.find( '.query-results' );
  42. // Get search notice text.
  43. inputs.queryNotice = $( '#query-notice-message' );
  44. inputs.queryNoticeTextDefault = inputs.queryNotice.find( '.query-notice-default' );
  45. inputs.queryNoticeTextHint = inputs.queryNotice.find( '.query-notice-hint' );
  46. // Bind event handlers.
  47. inputs.dialog.on( 'keydown', wpLink.keydown );
  48. inputs.dialog.on( 'keyup', wpLink.keyup );
  49. inputs.submit.on( 'click', function( event ) {
  50. event.preventDefault();
  51. wpLink.update();
  52. });
  53. inputs.close.add( inputs.backdrop ).add( '#wp-link-cancel button' ).on( 'click', function( event ) {
  54. event.preventDefault();
  55. wpLink.close();
  56. });
  57. rivers.elements.on( 'river-select', wpLink.updateFields );
  58. // Display 'hint' message when search field or 'query-results' box are focused.
  59. inputs.search.on( 'focus.wplink', function() {
  60. inputs.queryNoticeTextDefault.hide();
  61. inputs.queryNoticeTextHint.removeClass( 'screen-reader-text' ).show();
  62. } ).on( 'blur.wplink', function() {
  63. inputs.queryNoticeTextDefault.show();
  64. inputs.queryNoticeTextHint.addClass( 'screen-reader-text' ).hide();
  65. } );
  66. inputs.search.on( 'keyup input', function() {
  67. window.clearTimeout( searchTimer );
  68. searchTimer = window.setTimeout( function() {
  69. wpLink.searchInternalLinks();
  70. }, 500 );
  71. });
  72. inputs.url.on( 'paste', function() {
  73. setTimeout( wpLink.correctURL, 0 );
  74. } );
  75. inputs.url.on( 'blur', wpLink.correctURL );
  76. },
  77. // If URL wasn't corrected last time and doesn't start with http:, https:, ? # or /, prepend http://.
  78. correctURL: function () {
  79. var url = inputs.url.val().trim();
  80. if ( url && correctedURL !== url && ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( url ) ) {
  81. inputs.url.val( 'http://' + url );
  82. correctedURL = url;
  83. }
  84. },
  85. open: function( editorId, url, text ) {
  86. var ed,
  87. $body = $( document.body );
  88. $body.addClass( 'modal-open' );
  89. wpLink.modalOpen = true;
  90. wpLink.range = null;
  91. if ( editorId ) {
  92. window.wpActiveEditor = editorId;
  93. }
  94. if ( ! window.wpActiveEditor ) {
  95. return;
  96. }
  97. this.textarea = $( '#' + window.wpActiveEditor ).get( 0 );
  98. if ( typeof window.tinymce !== 'undefined' ) {
  99. // Make sure the link wrapper is the last element in the body,
  100. // or the inline editor toolbar may show above the backdrop.
  101. $body.append( inputs.backdrop, inputs.wrap );
  102. ed = window.tinymce.get( window.wpActiveEditor );
  103. if ( ed && ! ed.isHidden() ) {
  104. editor = ed;
  105. } else {
  106. editor = null;
  107. }
  108. }
  109. if ( ! wpLink.isMCE() && document.selection ) {
  110. this.textarea.focus();
  111. this.range = document.selection.createRange();
  112. }
  113. inputs.wrap.show();
  114. inputs.backdrop.show();
  115. wpLink.refresh( url, text );
  116. $( document ).trigger( 'wplink-open', inputs.wrap );
  117. },
  118. isMCE: function() {
  119. return editor && ! editor.isHidden();
  120. },
  121. refresh: function( url, text ) {
  122. var linkText = '';
  123. // Refresh rivers (clear links, check visibility).
  124. rivers.search.refresh();
  125. rivers.recent.refresh();
  126. if ( wpLink.isMCE() ) {
  127. wpLink.mceRefresh( url, text );
  128. } else {
  129. // For the Text editor the "Link text" field is always shown.
  130. if ( ! inputs.wrap.hasClass( 'has-text-field' ) ) {
  131. inputs.wrap.addClass( 'has-text-field' );
  132. }
  133. if ( document.selection ) {
  134. // Old IE.
  135. linkText = document.selection.createRange().text || text || '';
  136. } else if ( typeof this.textarea.selectionStart !== 'undefined' &&
  137. ( this.textarea.selectionStart !== this.textarea.selectionEnd ) ) {
  138. // W3C.
  139. text = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd ) || text || '';
  140. }
  141. inputs.text.val( text );
  142. wpLink.setDefaultValues();
  143. }
  144. if ( isTouch ) {
  145. // Close the onscreen keyboard.
  146. inputs.url.trigger( 'focus' ).trigger( 'blur' );
  147. } else {
  148. /*
  149. * Focus the URL field and highlight its contents.
  150. * If this is moved above the selection changes,
  151. * IE will show a flashing cursor over the dialog.
  152. */
  153. window.setTimeout( function() {
  154. inputs.url[0].select();
  155. inputs.url.trigger( 'focus' );
  156. } );
  157. }
  158. // Load the most recent results if this is the first time opening the panel.
  159. if ( ! rivers.recent.ul.children().length ) {
  160. rivers.recent.ajax();
  161. }
  162. correctedURL = inputs.url.val().replace( /^http:\/\//, '' );
  163. },
  164. hasSelectedText: function( linkNode ) {
  165. var node, nodes, i, html = editor.selection.getContent();
  166. // Partial html and not a fully selected anchor element.
  167. if ( /</.test( html ) && ( ! /^<a [^>]+>[^<]+<\/a>$/.test( html ) || html.indexOf('href=') === -1 ) ) {
  168. return false;
  169. }
  170. if ( linkNode.length ) {
  171. nodes = linkNode[0].childNodes;
  172. if ( ! nodes || ! nodes.length ) {
  173. return false;
  174. }
  175. for ( i = nodes.length - 1; i >= 0; i-- ) {
  176. node = nodes[i];
  177. if ( node.nodeType != 3 && ! window.tinymce.dom.BookmarkManager.isBookmarkNode( node ) ) {
  178. return false;
  179. }
  180. }
  181. }
  182. return true;
  183. },
  184. mceRefresh: function( searchStr, text ) {
  185. var linkText, href,
  186. linkNode = getLink(),
  187. onlyText = this.hasSelectedText( linkNode );
  188. if ( linkNode.length ) {
  189. linkText = linkNode.text();
  190. href = linkNode.attr( 'href' );
  191. if ( ! linkText.trim() ) {
  192. linkText = text || '';
  193. }
  194. if ( searchStr && ( urlRegexp.test( searchStr ) || emailRegexp.test( searchStr ) ) ) {
  195. href = searchStr;
  196. }
  197. if ( href !== '_wp_link_placeholder' ) {
  198. inputs.url.val( href );
  199. inputs.openInNewTab.prop( 'checked', '_blank' === linkNode.attr( 'target' ) );
  200. inputs.submit.val( wpLinkL10n.update );
  201. } else {
  202. this.setDefaultValues( linkText );
  203. }
  204. if ( searchStr && searchStr !== href ) {
  205. // The user has typed something in the inline dialog. Trigger a search with it.
  206. inputs.search.val( searchStr );
  207. } else {
  208. inputs.search.val( '' );
  209. }
  210. // Always reset the search.
  211. window.setTimeout( function() {
  212. wpLink.searchInternalLinks();
  213. } );
  214. } else {
  215. linkText = editor.selection.getContent({ format: 'text' }) || text || '';
  216. this.setDefaultValues( linkText );
  217. }
  218. if ( onlyText ) {
  219. inputs.text.val( linkText );
  220. inputs.wrap.addClass( 'has-text-field' );
  221. } else {
  222. inputs.text.val( '' );
  223. inputs.wrap.removeClass( 'has-text-field' );
  224. }
  225. },
  226. close: function( reset ) {
  227. $( document.body ).removeClass( 'modal-open' );
  228. wpLink.modalOpen = false;
  229. if ( reset !== 'noReset' ) {
  230. if ( ! wpLink.isMCE() ) {
  231. wpLink.textarea.focus();
  232. if ( wpLink.range ) {
  233. wpLink.range.moveToBookmark( wpLink.range.getBookmark() );
  234. wpLink.range.select();
  235. }
  236. } else {
  237. if ( editor.plugins.wplink ) {
  238. editor.plugins.wplink.close();
  239. }
  240. editor.focus();
  241. }
  242. }
  243. inputs.backdrop.hide();
  244. inputs.wrap.hide();
  245. correctedURL = false;
  246. $( document ).trigger( 'wplink-close', inputs.wrap );
  247. },
  248. getAttrs: function() {
  249. wpLink.correctURL();
  250. return {
  251. href: inputs.url.val().trim(),
  252. target: inputs.openInNewTab.prop( 'checked' ) ? '_blank' : null
  253. };
  254. },
  255. buildHtml: function(attrs) {
  256. var html = '<a href="' + attrs.href + '"';
  257. if ( attrs.target ) {
  258. html += ' rel="noopener" target="' + attrs.target + '"';
  259. }
  260. return html + '>';
  261. },
  262. update: function() {
  263. if ( wpLink.isMCE() ) {
  264. wpLink.mceUpdate();
  265. } else {
  266. wpLink.htmlUpdate();
  267. }
  268. },
  269. htmlUpdate: function() {
  270. var attrs, text, html, begin, end, cursor, selection,
  271. textarea = wpLink.textarea;
  272. if ( ! textarea ) {
  273. return;
  274. }
  275. attrs = wpLink.getAttrs();
  276. text = inputs.text.val();
  277. var parser = document.createElement( 'a' );
  278. parser.href = attrs.href;
  279. if ( 'javascript:' === parser.protocol || 'data:' === parser.protocol ) { // jshint ignore:line
  280. attrs.href = '';
  281. }
  282. // If there's no href, return.
  283. if ( ! attrs.href ) {
  284. return;
  285. }
  286. html = wpLink.buildHtml(attrs);
  287. // Insert HTML.
  288. if ( document.selection && wpLink.range ) {
  289. // IE.
  290. // Note: If no text is selected, IE will not place the cursor
  291. // inside the closing tag.
  292. textarea.focus();
  293. wpLink.range.text = html + ( text || wpLink.range.text ) + '</a>';
  294. wpLink.range.moveToBookmark( wpLink.range.getBookmark() );
  295. wpLink.range.select();
  296. wpLink.range = null;
  297. } else if ( typeof textarea.selectionStart !== 'undefined' ) {
  298. // W3C.
  299. begin = textarea.selectionStart;
  300. end = textarea.selectionEnd;
  301. selection = text || textarea.value.substring( begin, end );
  302. html = html + selection + '</a>';
  303. cursor = begin + html.length;
  304. // If no text is selected, place the cursor inside the closing tag.
  305. if ( begin === end && ! selection ) {
  306. cursor -= 4;
  307. }
  308. textarea.value = (
  309. textarea.value.substring( 0, begin ) +
  310. html +
  311. textarea.value.substring( end, textarea.value.length )
  312. );
  313. // Update cursor position.
  314. textarea.selectionStart = textarea.selectionEnd = cursor;
  315. }
  316. wpLink.close();
  317. textarea.focus();
  318. $( textarea ).trigger( 'change' );
  319. // Audible confirmation message when a link has been inserted in the Editor.
  320. wp.a11y.speak( wpLinkL10n.linkInserted );
  321. },
  322. mceUpdate: function() {
  323. var attrs = wpLink.getAttrs(),
  324. $link, text, hasText;
  325. var parser = document.createElement( 'a' );
  326. parser.href = attrs.href;
  327. if ( 'javascript:' === parser.protocol || 'data:' === parser.protocol ) { // jshint ignore:line
  328. attrs.href = '';
  329. }
  330. if ( ! attrs.href ) {
  331. editor.execCommand( 'unlink' );
  332. wpLink.close();
  333. return;
  334. }
  335. $link = getLink();
  336. editor.undoManager.transact( function() {
  337. if ( ! $link.length ) {
  338. editor.execCommand( 'mceInsertLink', false, { href: '_wp_link_placeholder', 'data-wp-temp-link': 1 } );
  339. $link = editor.$( 'a[data-wp-temp-link="1"]' ).removeAttr( 'data-wp-temp-link' );
  340. hasText = $link.text().trim();
  341. }
  342. if ( ! $link.length ) {
  343. editor.execCommand( 'unlink' );
  344. } else {
  345. if ( inputs.wrap.hasClass( 'has-text-field' ) ) {
  346. text = inputs.text.val();
  347. if ( text ) {
  348. $link.text( text );
  349. } else if ( ! hasText ) {
  350. $link.text( attrs.href );
  351. }
  352. }
  353. attrs['data-wplink-edit'] = null;
  354. attrs['data-mce-href'] = attrs.href;
  355. $link.attr( attrs );
  356. }
  357. } );
  358. wpLink.close( 'noReset' );
  359. editor.focus();
  360. if ( $link.length ) {
  361. editor.selection.select( $link[0] );
  362. if ( editor.plugins.wplink ) {
  363. editor.plugins.wplink.checkLink( $link[0] );
  364. }
  365. }
  366. editor.nodeChanged();
  367. // Audible confirmation message when a link has been inserted in the Editor.
  368. wp.a11y.speak( wpLinkL10n.linkInserted );
  369. },
  370. updateFields: function( e, li ) {
  371. inputs.url.val( li.children( '.item-permalink' ).val() );
  372. if ( inputs.wrap.hasClass( 'has-text-field' ) && ! inputs.text.val() ) {
  373. inputs.text.val( li.children( '.item-title' ).text() );
  374. }
  375. },
  376. getUrlFromSelection: function( selection ) {
  377. if ( ! selection ) {
  378. if ( this.isMCE() ) {
  379. selection = editor.selection.getContent({ format: 'text' });
  380. } else if ( document.selection && wpLink.range ) {
  381. selection = wpLink.range.text;
  382. } else if ( typeof this.textarea.selectionStart !== 'undefined' ) {
  383. selection = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd );
  384. }
  385. }
  386. selection = selection || '';
  387. selection = selection.trim();
  388. if ( selection && emailRegexp.test( selection ) ) {
  389. // Selection is email address.
  390. return 'mailto:' + selection;
  391. } else if ( selection && urlRegexp.test( selection ) ) {
  392. // Selection is URL.
  393. return selection.replace( /&amp;|&#0?38;/gi, '&' );
  394. }
  395. return '';
  396. },
  397. setDefaultValues: function( selection ) {
  398. inputs.url.val( this.getUrlFromSelection( selection ) );
  399. // Empty the search field and swap the "rivers".
  400. inputs.search.val('');
  401. wpLink.searchInternalLinks();
  402. // Update save prompt.
  403. inputs.submit.val( wpLinkL10n.save );
  404. },
  405. searchInternalLinks: function() {
  406. var waiting,
  407. search = inputs.search.val() || '',
  408. minInputLength = parseInt( wpLinkL10n.minInputLength, 10 ) || 3;
  409. if ( search.length >= minInputLength ) {
  410. rivers.recent.hide();
  411. rivers.search.show();
  412. // Don't search if the keypress didn't change the title.
  413. if ( wpLink.lastSearch == search )
  414. return;
  415. wpLink.lastSearch = search;
  416. waiting = inputs.search.parent().find( '.spinner' ).addClass( 'is-active' );
  417. rivers.search.change( search );
  418. rivers.search.ajax( function() {
  419. waiting.removeClass( 'is-active' );
  420. });
  421. } else {
  422. rivers.search.hide();
  423. rivers.recent.show();
  424. }
  425. },
  426. next: function() {
  427. rivers.search.next();
  428. rivers.recent.next();
  429. },
  430. prev: function() {
  431. rivers.search.prev();
  432. rivers.recent.prev();
  433. },
  434. keydown: function( event ) {
  435. var fn, id;
  436. // Escape key.
  437. if ( 27 === event.keyCode ) {
  438. wpLink.close();
  439. event.stopImmediatePropagation();
  440. // Tab key.
  441. } else if ( 9 === event.keyCode ) {
  442. id = event.target.id;
  443. // wp-link-submit must always be the last focusable element in the dialog.
  444. // Following focusable elements will be skipped on keyboard navigation.
  445. if ( id === 'wp-link-submit' && ! event.shiftKey ) {
  446. inputs.close.trigger( 'focus' );
  447. event.preventDefault();
  448. } else if ( id === 'wp-link-close' && event.shiftKey ) {
  449. inputs.submit.trigger( 'focus' );
  450. event.preventDefault();
  451. }
  452. }
  453. // Up Arrow and Down Arrow keys.
  454. if ( event.shiftKey || ( 38 !== event.keyCode && 40 !== event.keyCode ) ) {
  455. return;
  456. }
  457. if ( document.activeElement &&
  458. ( document.activeElement.id === 'link-title-field' || document.activeElement.id === 'url-field' ) ) {
  459. return;
  460. }
  461. // Up Arrow key.
  462. fn = 38 === event.keyCode ? 'prev' : 'next';
  463. clearInterval( wpLink.keyInterval );
  464. wpLink[ fn ]();
  465. wpLink.keyInterval = setInterval( wpLink[ fn ], wpLink.keySensitivity );
  466. event.preventDefault();
  467. },
  468. keyup: function( event ) {
  469. // Up Arrow and Down Arrow keys.
  470. if ( 38 === event.keyCode || 40 === event.keyCode ) {
  471. clearInterval( wpLink.keyInterval );
  472. event.preventDefault();
  473. }
  474. },
  475. delayedCallback: function( func, delay ) {
  476. var timeoutTriggered, funcTriggered, funcArgs, funcContext;
  477. if ( ! delay )
  478. return func;
  479. setTimeout( function() {
  480. if ( funcTriggered )
  481. return func.apply( funcContext, funcArgs );
  482. // Otherwise, wait.
  483. timeoutTriggered = true;
  484. }, delay );
  485. return function() {
  486. if ( timeoutTriggered )
  487. return func.apply( this, arguments );
  488. // Otherwise, wait.
  489. funcArgs = arguments;
  490. funcContext = this;
  491. funcTriggered = true;
  492. };
  493. }
  494. };
  495. River = function( element, search ) {
  496. var self = this;
  497. this.element = element;
  498. this.ul = element.children( 'ul' );
  499. this.contentHeight = element.children( '#link-selector-height' );
  500. this.waiting = element.find('.river-waiting');
  501. this.change( search );
  502. this.refresh();
  503. $( '#wp-link .query-results, #wp-link #link-selector' ).on( 'scroll', function() {
  504. self.maybeLoad();
  505. });
  506. element.on( 'click', 'li', function( event ) {
  507. self.select( $( this ), event );
  508. });
  509. };
  510. $.extend( River.prototype, {
  511. refresh: function() {
  512. this.deselect();
  513. this.visible = this.element.is( ':visible' );
  514. },
  515. show: function() {
  516. if ( ! this.visible ) {
  517. this.deselect();
  518. this.element.show();
  519. this.visible = true;
  520. }
  521. },
  522. hide: function() {
  523. this.element.hide();
  524. this.visible = false;
  525. },
  526. // Selects a list item and triggers the river-select event.
  527. select: function( li, event ) {
  528. var liHeight, elHeight, liTop, elTop;
  529. if ( li.hasClass( 'unselectable' ) || li == this.selected )
  530. return;
  531. this.deselect();
  532. this.selected = li.addClass( 'selected' );
  533. // Make sure the element is visible.
  534. liHeight = li.outerHeight();
  535. elHeight = this.element.height();
  536. liTop = li.position().top;
  537. elTop = this.element.scrollTop();
  538. if ( liTop < 0 ) // Make first visible element.
  539. this.element.scrollTop( elTop + liTop );
  540. else if ( liTop + liHeight > elHeight ) // Make last visible element.
  541. this.element.scrollTop( elTop + liTop - elHeight + liHeight );
  542. // Trigger the river-select event.
  543. this.element.trigger( 'river-select', [ li, event, this ] );
  544. },
  545. deselect: function() {
  546. if ( this.selected )
  547. this.selected.removeClass( 'selected' );
  548. this.selected = false;
  549. },
  550. prev: function() {
  551. if ( ! this.visible )
  552. return;
  553. var to;
  554. if ( this.selected ) {
  555. to = this.selected.prev( 'li' );
  556. if ( to.length )
  557. this.select( to );
  558. }
  559. },
  560. next: function() {
  561. if ( ! this.visible )
  562. return;
  563. var to = this.selected ? this.selected.next( 'li' ) : $( 'li:not(.unselectable):first', this.element );
  564. if ( to.length )
  565. this.select( to );
  566. },
  567. ajax: function( callback ) {
  568. var self = this,
  569. delay = this.query.page == 1 ? 0 : wpLink.minRiverAJAXDuration,
  570. response = wpLink.delayedCallback( function( results, params ) {
  571. self.process( results, params );
  572. if ( callback )
  573. callback( results, params );
  574. }, delay );
  575. this.query.ajax( response );
  576. },
  577. change: function( search ) {
  578. if ( this.query && this._search == search )
  579. return;
  580. this._search = search;
  581. this.query = new Query( search );
  582. this.element.scrollTop( 0 );
  583. },
  584. process: function( results, params ) {
  585. var list = '', alt = true, classes = '',
  586. firstPage = params.page == 1;
  587. if ( ! results ) {
  588. if ( firstPage ) {
  589. list += '<li class="unselectable no-matches-found"><span class="item-title"><em>' +
  590. wpLinkL10n.noMatchesFound + '</em></span></li>';
  591. }
  592. } else {
  593. $.each( results, function() {
  594. classes = alt ? 'alternate' : '';
  595. classes += this.title ? '' : ' no-title';
  596. list += classes ? '<li class="' + classes + '">' : '<li>';
  597. list += '<input type="hidden" class="item-permalink" value="' + this.permalink + '" />';
  598. list += '<span class="item-title">';
  599. list += this.title ? this.title : wpLinkL10n.noTitle;
  600. list += '</span><span class="item-info">' + this.info + '</span></li>';
  601. alt = ! alt;
  602. });
  603. }
  604. this.ul[ firstPage ? 'html' : 'append' ]( list );
  605. },
  606. maybeLoad: function() {
  607. var self = this,
  608. el = this.element,
  609. bottom = el.scrollTop() + el.height();
  610. if ( ! this.query.ready() || bottom < this.contentHeight.height() - wpLink.riverBottomThreshold )
  611. return;
  612. setTimeout(function() {
  613. var newTop = el.scrollTop(),
  614. newBottom = newTop + el.height();
  615. if ( ! self.query.ready() || newBottom < self.contentHeight.height() - wpLink.riverBottomThreshold )
  616. return;
  617. self.waiting.addClass( 'is-active' );
  618. el.scrollTop( newTop + self.waiting.outerHeight() );
  619. self.ajax( function() {
  620. self.waiting.removeClass( 'is-active' );
  621. });
  622. }, wpLink.timeToTriggerRiver );
  623. }
  624. });
  625. Query = function( search ) {
  626. this.page = 1;
  627. this.allLoaded = false;
  628. this.querying = false;
  629. this.search = search;
  630. };
  631. $.extend( Query.prototype, {
  632. ready: function() {
  633. return ! ( this.querying || this.allLoaded );
  634. },
  635. ajax: function( callback ) {
  636. var self = this,
  637. query = {
  638. action : 'wp-link-ajax',
  639. page : this.page,
  640. '_ajax_linking_nonce' : inputs.nonce.val()
  641. };
  642. if ( this.search )
  643. query.search = this.search;
  644. this.querying = true;
  645. $.post( window.ajaxurl, query, function( r ) {
  646. self.page++;
  647. self.querying = false;
  648. self.allLoaded = ! r;
  649. callback( r, query );
  650. }, 'json' );
  651. }
  652. });
  653. $( wpLink.init );
  654. })( jQuery, window.wpLinkL10n, window.wp );