wplink.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  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.keydown( wpLink.keydown );
  48. inputs.dialog.keyup( wpLink.keyup );
  49. inputs.submit.click( function( event ) {
  50. event.preventDefault();
  51. wpLink.update();
  52. });
  53. inputs.close.add( inputs.backdrop ).add( '#wp-link-cancel button' ).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 = $.trim( inputs.url.val() );
  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.focus().blur();
  147. } else {
  148. // Focus the URL field and highlight its contents.
  149. // If this is moved above the selection changes,
  150. // IE will show a flashing cursor over the dialog.
  151. window.setTimeout( function() {
  152. inputs.url[0].select();
  153. inputs.url.focus();
  154. } );
  155. }
  156. // Load the most recent results if this is the first time opening the panel.
  157. if ( ! rivers.recent.ul.children().length ) {
  158. rivers.recent.ajax();
  159. }
  160. correctedURL = inputs.url.val().replace( /^http:\/\//, '' );
  161. },
  162. hasSelectedText: function( linkNode ) {
  163. var node, nodes, i, html = editor.selection.getContent();
  164. // Partial html and not a fully selected anchor element
  165. if ( /</.test( html ) && ( ! /^<a [^>]+>[^<]+<\/a>$/.test( html ) || html.indexOf('href=') === -1 ) ) {
  166. return false;
  167. }
  168. if ( linkNode.length ) {
  169. nodes = linkNode[0].childNodes;
  170. if ( ! nodes || ! nodes.length ) {
  171. return false;
  172. }
  173. for ( i = nodes.length - 1; i >= 0; i-- ) {
  174. node = nodes[i];
  175. if ( node.nodeType != 3 && ! window.tinymce.dom.BookmarkManager.isBookmarkNode( node ) ) {
  176. return false;
  177. }
  178. }
  179. }
  180. return true;
  181. },
  182. mceRefresh: function( searchStr, text ) {
  183. var linkText, href,
  184. linkNode = getLink(),
  185. onlyText = this.hasSelectedText( linkNode );
  186. if ( linkNode.length ) {
  187. linkText = linkNode.text();
  188. href = linkNode.attr( 'href' );
  189. if ( ! $.trim( linkText ) ) {
  190. linkText = text || '';
  191. }
  192. if ( searchStr && ( urlRegexp.test( searchStr ) || emailRegexp.test( searchStr ) ) ) {
  193. href = searchStr;
  194. }
  195. if ( href !== '_wp_link_placeholder' ) {
  196. inputs.url.val( href );
  197. inputs.openInNewTab.prop( 'checked', '_blank' === linkNode.attr( 'target' ) );
  198. inputs.submit.val( wpLinkL10n.update );
  199. } else {
  200. this.setDefaultValues( linkText );
  201. }
  202. if ( searchStr && searchStr !== href ) {
  203. // The user has typed something in the inline dialog. Trigger a search with it.
  204. inputs.search.val( searchStr );
  205. } else {
  206. inputs.search.val( '' );
  207. }
  208. // Always reset the search
  209. window.setTimeout( function() {
  210. wpLink.searchInternalLinks();
  211. } );
  212. } else {
  213. linkText = editor.selection.getContent({ format: 'text' }) || text || '';
  214. this.setDefaultValues( linkText );
  215. }
  216. if ( onlyText ) {
  217. inputs.text.val( linkText );
  218. inputs.wrap.addClass( 'has-text-field' );
  219. } else {
  220. inputs.text.val( '' );
  221. inputs.wrap.removeClass( 'has-text-field' );
  222. }
  223. },
  224. close: function( reset ) {
  225. $( document.body ).removeClass( 'modal-open' );
  226. wpLink.modalOpen = false;
  227. if ( reset !== 'noReset' ) {
  228. if ( ! wpLink.isMCE() ) {
  229. wpLink.textarea.focus();
  230. if ( wpLink.range ) {
  231. wpLink.range.moveToBookmark( wpLink.range.getBookmark() );
  232. wpLink.range.select();
  233. }
  234. } else {
  235. if ( editor.plugins.wplink ) {
  236. editor.plugins.wplink.close();
  237. }
  238. editor.focus();
  239. }
  240. }
  241. inputs.backdrop.hide();
  242. inputs.wrap.hide();
  243. correctedURL = false;
  244. $( document ).trigger( 'wplink-close', inputs.wrap );
  245. },
  246. getAttrs: function() {
  247. wpLink.correctURL();
  248. return {
  249. href: $.trim( inputs.url.val() ),
  250. target: inputs.openInNewTab.prop( 'checked' ) ? '_blank' : null
  251. };
  252. },
  253. buildHtml: function(attrs) {
  254. var html = '<a href="' + attrs.href + '"';
  255. if ( attrs.target ) {
  256. html += ' rel="noopener" target="' + attrs.target + '"';
  257. }
  258. return html + '>';
  259. },
  260. update: function() {
  261. if ( wpLink.isMCE() ) {
  262. wpLink.mceUpdate();
  263. } else {
  264. wpLink.htmlUpdate();
  265. }
  266. },
  267. htmlUpdate: function() {
  268. var attrs, text, html, begin, end, cursor, selection,
  269. textarea = wpLink.textarea;
  270. if ( ! textarea ) {
  271. return;
  272. }
  273. attrs = wpLink.getAttrs();
  274. text = inputs.text.val();
  275. var parser = document.createElement( 'a' );
  276. parser.href = attrs.href;
  277. if ( 'javascript:' === parser.protocol || 'data:' === parser.protocol ) { // jshint ignore:line
  278. attrs.href = '';
  279. }
  280. // If there's no href, return.
  281. if ( ! attrs.href ) {
  282. return;
  283. }
  284. html = wpLink.buildHtml(attrs);
  285. // Insert HTML
  286. if ( document.selection && wpLink.range ) {
  287. // IE
  288. // Note: If no text is selected, IE will not place the cursor
  289. // inside the closing tag.
  290. textarea.focus();
  291. wpLink.range.text = html + ( text || wpLink.range.text ) + '</a>';
  292. wpLink.range.moveToBookmark( wpLink.range.getBookmark() );
  293. wpLink.range.select();
  294. wpLink.range = null;
  295. } else if ( typeof textarea.selectionStart !== 'undefined' ) {
  296. // W3C
  297. begin = textarea.selectionStart;
  298. end = textarea.selectionEnd;
  299. selection = text || textarea.value.substring( begin, end );
  300. html = html + selection + '</a>';
  301. cursor = begin + html.length;
  302. // If no text is selected, place the cursor inside the closing tag.
  303. if ( begin === end && ! selection ) {
  304. cursor -= 4;
  305. }
  306. textarea.value = (
  307. textarea.value.substring( 0, begin ) +
  308. html +
  309. textarea.value.substring( end, textarea.value.length )
  310. );
  311. // Update cursor position
  312. textarea.selectionStart = textarea.selectionEnd = cursor;
  313. }
  314. wpLink.close();
  315. textarea.focus();
  316. $( textarea ).trigger( 'change' );
  317. // Audible confirmation message when a link has been inserted in the Editor.
  318. wp.a11y.speak( wpLinkL10n.linkInserted );
  319. },
  320. mceUpdate: function() {
  321. var attrs = wpLink.getAttrs(),
  322. $link, text, hasText;
  323. var parser = document.createElement( 'a' );
  324. parser.href = attrs.href;
  325. if ( 'javascript:' === parser.protocol || 'data:' === parser.protocol ) { // jshint ignore:line
  326. attrs.href = '';
  327. }
  328. if ( ! attrs.href ) {
  329. editor.execCommand( 'unlink' );
  330. wpLink.close();
  331. return;
  332. }
  333. $link = getLink();
  334. editor.undoManager.transact( function() {
  335. if ( ! $link.length ) {
  336. editor.execCommand( 'mceInsertLink', false, { href: '_wp_link_placeholder', 'data-wp-temp-link': 1 } );
  337. $link = editor.$( 'a[data-wp-temp-link="1"]' ).removeAttr( 'data-wp-temp-link' );
  338. hasText = $.trim( $link.text() );
  339. }
  340. if ( ! $link.length ) {
  341. editor.execCommand( 'unlink' );
  342. } else {
  343. if ( inputs.wrap.hasClass( 'has-text-field' ) ) {
  344. text = inputs.text.val();
  345. if ( text ) {
  346. $link.text( text );
  347. } else if ( ! hasText ) {
  348. $link.text( attrs.href );
  349. }
  350. }
  351. attrs['data-wplink-edit'] = null;
  352. attrs['data-mce-href'] = attrs.href;
  353. $link.attr( attrs );
  354. }
  355. } );
  356. wpLink.close( 'noReset' );
  357. editor.focus();
  358. if ( $link.length ) {
  359. editor.selection.select( $link[0] );
  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. if ( inputs.wrap.hasClass( 'has-text-field' ) && ! inputs.text.val() ) {
  371. inputs.text.val( li.children( '.item-title' ).text() );
  372. }
  373. },
  374. getUrlFromSelection: function( selection ) {
  375. if ( ! selection ) {
  376. if ( this.isMCE() ) {
  377. selection = editor.selection.getContent({ format: 'text' });
  378. } else if ( document.selection && wpLink.range ) {
  379. selection = wpLink.range.text;
  380. } else if ( typeof this.textarea.selectionStart !== 'undefined' ) {
  381. selection = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd );
  382. }
  383. }
  384. selection = $.trim( selection );
  385. if ( selection && emailRegexp.test( selection ) ) {
  386. // Selection is email address
  387. return 'mailto:' + selection;
  388. } else if ( selection && urlRegexp.test( selection ) ) {
  389. // Selection is URL
  390. return selection.replace( /&amp;|&#0?38;/gi, '&' );
  391. }
  392. return '';
  393. },
  394. setDefaultValues: function( selection ) {
  395. inputs.url.val( this.getUrlFromSelection( selection ) );
  396. // Empty the search field and swap the "rivers".
  397. inputs.search.val('');
  398. wpLink.searchInternalLinks();
  399. // Update save prompt.
  400. inputs.submit.val( wpLinkL10n.save );
  401. },
  402. searchInternalLinks: function() {
  403. var waiting,
  404. search = inputs.search.val() || '';
  405. if ( search.length > 2 ) {
  406. rivers.recent.hide();
  407. rivers.search.show();
  408. // Don't search if the keypress didn't change the title.
  409. if ( wpLink.lastSearch == search )
  410. return;
  411. wpLink.lastSearch = search;
  412. waiting = inputs.search.parent().find( '.spinner' ).addClass( 'is-active' );
  413. rivers.search.change( search );
  414. rivers.search.ajax( function() {
  415. waiting.removeClass( 'is-active' );
  416. });
  417. } else {
  418. rivers.search.hide();
  419. rivers.recent.show();
  420. }
  421. },
  422. next: function() {
  423. rivers.search.next();
  424. rivers.recent.next();
  425. },
  426. prev: function() {
  427. rivers.search.prev();
  428. rivers.recent.prev();
  429. },
  430. keydown: function( event ) {
  431. var fn, id;
  432. // Escape key.
  433. if ( 27 === event.keyCode ) {
  434. wpLink.close();
  435. event.stopImmediatePropagation();
  436. // Tab key.
  437. } else if ( 9 === event.keyCode ) {
  438. id = event.target.id;
  439. // wp-link-submit must always be the last focusable element in the dialog.
  440. // following focusable elements will be skipped on keyboard navigation.
  441. if ( id === 'wp-link-submit' && ! event.shiftKey ) {
  442. inputs.close.focus();
  443. event.preventDefault();
  444. } else if ( id === 'wp-link-close' && event.shiftKey ) {
  445. inputs.submit.focus();
  446. event.preventDefault();
  447. }
  448. }
  449. // Up Arrow and Down Arrow keys.
  450. if ( event.shiftKey || ( 38 !== event.keyCode && 40 !== event.keyCode ) ) {
  451. return;
  452. }
  453. if ( document.activeElement &&
  454. ( document.activeElement.id === 'link-title-field' || document.activeElement.id === 'url-field' ) ) {
  455. return;
  456. }
  457. // Up Arrow key.
  458. fn = 38 === event.keyCode ? 'prev' : 'next';
  459. clearInterval( wpLink.keyInterval );
  460. wpLink[ fn ]();
  461. wpLink.keyInterval = setInterval( wpLink[ fn ], wpLink.keySensitivity );
  462. event.preventDefault();
  463. },
  464. keyup: function( event ) {
  465. // Up Arrow and Down Arrow keys.
  466. if ( 38 === event.keyCode || 40 === event.keyCode ) {
  467. clearInterval( wpLink.keyInterval );
  468. event.preventDefault();
  469. }
  470. },
  471. delayedCallback: function( func, delay ) {
  472. var timeoutTriggered, funcTriggered, funcArgs, funcContext;
  473. if ( ! delay )
  474. return func;
  475. setTimeout( function() {
  476. if ( funcTriggered )
  477. return func.apply( funcContext, funcArgs );
  478. // Otherwise, wait.
  479. timeoutTriggered = true;
  480. }, delay );
  481. return function() {
  482. if ( timeoutTriggered )
  483. return func.apply( this, arguments );
  484. // Otherwise, wait.
  485. funcArgs = arguments;
  486. funcContext = this;
  487. funcTriggered = true;
  488. };
  489. }
  490. };
  491. River = function( element, search ) {
  492. var self = this;
  493. this.element = element;
  494. this.ul = element.children( 'ul' );
  495. this.contentHeight = element.children( '#link-selector-height' );
  496. this.waiting = element.find('.river-waiting');
  497. this.change( search );
  498. this.refresh();
  499. $( '#wp-link .query-results, #wp-link #link-selector' ).scroll( function() {
  500. self.maybeLoad();
  501. });
  502. element.on( 'click', 'li', function( event ) {
  503. self.select( $( this ), event );
  504. });
  505. };
  506. $.extend( River.prototype, {
  507. refresh: function() {
  508. this.deselect();
  509. this.visible = this.element.is( ':visible' );
  510. },
  511. show: function() {
  512. if ( ! this.visible ) {
  513. this.deselect();
  514. this.element.show();
  515. this.visible = true;
  516. }
  517. },
  518. hide: function() {
  519. this.element.hide();
  520. this.visible = false;
  521. },
  522. // Selects a list item and triggers the river-select event.
  523. select: function( li, event ) {
  524. var liHeight, elHeight, liTop, elTop;
  525. if ( li.hasClass( 'unselectable' ) || li == this.selected )
  526. return;
  527. this.deselect();
  528. this.selected = li.addClass( 'selected' );
  529. // Make sure the element is visible
  530. liHeight = li.outerHeight();
  531. elHeight = this.element.height();
  532. liTop = li.position().top;
  533. elTop = this.element.scrollTop();
  534. if ( liTop < 0 ) // Make first visible element
  535. this.element.scrollTop( elTop + liTop );
  536. else if ( liTop + liHeight > elHeight ) // Make last visible element
  537. this.element.scrollTop( elTop + liTop - elHeight + liHeight );
  538. // Trigger the river-select event
  539. this.element.trigger( 'river-select', [ li, event, this ] );
  540. },
  541. deselect: function() {
  542. if ( this.selected )
  543. this.selected.removeClass( 'selected' );
  544. this.selected = false;
  545. },
  546. prev: function() {
  547. if ( ! this.visible )
  548. return;
  549. var to;
  550. if ( this.selected ) {
  551. to = this.selected.prev( 'li' );
  552. if ( to.length )
  553. this.select( to );
  554. }
  555. },
  556. next: function() {
  557. if ( ! this.visible )
  558. return;
  559. var to = this.selected ? this.selected.next( 'li' ) : $( 'li:not(.unselectable):first', this.element );
  560. if ( to.length )
  561. this.select( to );
  562. },
  563. ajax: function( callback ) {
  564. var self = this,
  565. delay = this.query.page == 1 ? 0 : wpLink.minRiverAJAXDuration,
  566. response = wpLink.delayedCallback( function( results, params ) {
  567. self.process( results, params );
  568. if ( callback )
  569. callback( results, params );
  570. }, delay );
  571. this.query.ajax( response );
  572. },
  573. change: function( search ) {
  574. if ( this.query && this._search == search )
  575. return;
  576. this._search = search;
  577. this.query = new Query( search );
  578. this.element.scrollTop( 0 );
  579. },
  580. process: function( results, params ) {
  581. var list = '', alt = true, classes = '',
  582. firstPage = params.page == 1;
  583. if ( ! results ) {
  584. if ( firstPage ) {
  585. list += '<li class="unselectable no-matches-found"><span class="item-title"><em>' +
  586. wpLinkL10n.noMatchesFound + '</em></span></li>';
  587. }
  588. } else {
  589. $.each( results, function() {
  590. classes = alt ? 'alternate' : '';
  591. classes += this.title ? '' : ' no-title';
  592. list += classes ? '<li class="' + classes + '">' : '<li>';
  593. list += '<input type="hidden" class="item-permalink" value="' + this.permalink + '" />';
  594. list += '<span class="item-title">';
  595. list += this.title ? this.title : wpLinkL10n.noTitle;
  596. list += '</span><span class="item-info">' + this.info + '</span></li>';
  597. alt = ! alt;
  598. });
  599. }
  600. this.ul[ firstPage ? 'html' : 'append' ]( list );
  601. },
  602. maybeLoad: function() {
  603. var self = this,
  604. el = this.element,
  605. bottom = el.scrollTop() + el.height();
  606. if ( ! this.query.ready() || bottom < this.contentHeight.height() - wpLink.riverBottomThreshold )
  607. return;
  608. setTimeout(function() {
  609. var newTop = el.scrollTop(),
  610. newBottom = newTop + el.height();
  611. if ( ! self.query.ready() || newBottom < self.contentHeight.height() - wpLink.riverBottomThreshold )
  612. return;
  613. self.waiting.addClass( 'is-active' );
  614. el.scrollTop( newTop + self.waiting.outerHeight() );
  615. self.ajax( function() {
  616. self.waiting.removeClass( 'is-active' );
  617. });
  618. }, wpLink.timeToTriggerRiver );
  619. }
  620. });
  621. Query = function( search ) {
  622. this.page = 1;
  623. this.allLoaded = false;
  624. this.querying = false;
  625. this.search = search;
  626. };
  627. $.extend( Query.prototype, {
  628. ready: function() {
  629. return ! ( this.querying || this.allLoaded );
  630. },
  631. ajax: function( callback ) {
  632. var self = this,
  633. query = {
  634. action : 'wp-link-ajax',
  635. page : this.page,
  636. '_ajax_linking_nonce' : inputs.nonce.val()
  637. };
  638. if ( this.search )
  639. query.search = this.search;
  640. this.querying = true;
  641. $.post( window.ajaxurl, query, function( r ) {
  642. self.page++;
  643. self.querying = false;
  644. self.allLoaded = ! r;
  645. callback( r, query );
  646. }, 'json' );
  647. }
  648. });
  649. $( document ).ready( wpLink.init );
  650. })( jQuery, window.wpLinkL10n, window.wp );