wplink.js 21 KB

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