+ });
+
+ // When doing undo and redo with keyboard shortcuts (Ctrl|Cmd+Z, Ctrl|Cmd+Shift+Z, Ctrl|Cmd+Y),
+ // set a flag to not focus the inline dialog. The editor has to remain focused so the users can do consecutive undo/redo.
+ editor.on( 'keydown', function( event ) {
+ if ( event.altKey || ( tinymce.Env.mac && ( ! event.metaKey || event.ctrlKey ) ) ||
+ ( ! tinymce.Env.mac && ! event.ctrlKey ) ) {
+
+ return;
+ }
+
+ if ( event.keyCode === 89 || event.keyCode === 90 ) { // Y or Z
+ doingUndoRedo = true;
+
+ window.clearTimeout( doingUndoRedoTimer );
+ doingUndoRedoTimer = window.setTimeout( function() {
+ doingUndoRedo = false;
+ }, 500 );
+ }
+ } );
+
+ editor.addButton( 'wp_link_preview', {
+ type: 'WPLinkPreview',
+ onPostRender: function() {
+ previewInstance = this;
+ }
+ } );
+
+ editor.addButton( 'wp_link_input', {
+ type: 'WPLinkInput',
+ onPostRender: function() {
+ var element = this.getEl(),
+ input = element.firstChild,
+ $input, cache, last;
+
+ inputInstance = this;
+
+ if ( $ && $.ui && $.ui.autocomplete ) {
+ $input = $( input );
+
+ $input.on( 'keydown', function() {
+ $input.removeAttr( 'aria-activedescendant' );
+ } )
+ .autocomplete( {
+ source: function( request, response ) {
+ if ( last === request.term ) {
+ response( cache );
+ return;
+ }
+
+ if ( /^https?:/.test( request.term ) || request.term.indexOf( '.' ) !== -1 ) {
+ return response();
+ }
+
+ $.post( window.ajaxurl, {
+ action: 'wp-link-ajax',
+ page: 1,
+ search: request.term,
+ _ajax_linking_nonce: $( '#_ajax_linking_nonce' ).val()
+ }, function( data ) {
+ cache = data;
+ response( data );
+ }, 'json' );
+
+ last = request.term;
+ },
+ focus: function( event, ui ) {
+ $input.attr( 'aria-activedescendant', 'mce-wp-autocomplete-' + ui.item.ID );
+ /*
+ * Don't empty the URL input field, when using the arrow keys to
+ * highlight items. See api.jqueryui.com/autocomplete/#event-focus
+ */
+ event.preventDefault();
+ },
+ select: function( event, ui ) {
+ $input.val( ui.item.permalink );
+ $( element.firstChild.nextSibling ).val( ui.item.title );
+
+ if ( 9 === event.keyCode && typeof window.wp !== 'undefined' &&
+ window.wp.a11y && typeof window.wpLinkL10n !== 'undefined' ) {
+ // Audible confirmation message when a link has been selected.
+ window.wp.a11y.speak( window.wpLinkL10n.linkSelected );
+ }
+
+ return false;
+ },
+ open: function() {
+ $input.attr( 'aria-expanded', 'true' );
+ editToolbar.blockHide = true;
+ },
+ close: function() {
+ $input.attr( 'aria-expanded', 'false' );
+ editToolbar.blockHide = false;
+ },
+ minLength: 2,
+ position: {
+ my: 'left top+2'
+ },
+ messages: {
+ noResults: ( typeof window.uiAutocompleteL10n !== 'undefined' ) ? window.uiAutocompleteL10n.noResults : '',
+ results: function( number ) {
+ if ( typeof window.uiAutocompleteL10n !== 'undefined' ) {
+ if ( number > 1 ) {
+ return window.uiAutocompleteL10n.manyResults.replace( '%d', number );
+ }
+
+ return window.uiAutocompleteL10n.oneResult;
+ }
+ }
+ }
+ } ).autocomplete( 'instance' )._renderItem = function( ul, item ) {
+ return $( '<li role="option" id="mce-wp-autocomplete-' + item.ID + '">' )
+ .append( '<span>' + item.title + '</span> <span class="wp-editor-float-right">' + item.info + '</span>' )
+ .appendTo( ul );
+ };
+
+ $input.attr( {
+ 'role': 'combobox',
+ 'aria-autocomplete': 'list',
+ 'aria-expanded': 'false',
+ 'aria-owns': $input.autocomplete( 'widget' ).attr( 'id' )
+ } )
+ .on( 'focus', function() {
+ var inputValue = $input.val();
+ /*
+ * Don't trigger a search if the URL field already has a link or is empty.
+ * Also, avoids screen readers announce `No search results`.
+ */
+ if ( inputValue && ! /^https?:/.test( inputValue ) ) {
+ $input.autocomplete( 'search' );
+ }
+ } )
+ // Returns a jQuery object containing the menu element.
+ .autocomplete( 'widget' )
+ .addClass( 'wplink-autocomplete' )
+ .attr( 'role', 'listbox' )
+ .removeAttr( 'tabindex' ) // Remove the `tabindex=0` attribute added by jQuery UI.
+ /*
+ * Looks like Safari and VoiceOver need an `aria-selected` attribute. See ticket #33301.
+ * The `menufocus` and `menublur` events are the same events used to add and remove
+ * the `ui-state-focus` CSS class on the menu items. See jQuery UI Menu Widget.
+ */
+ .on( 'menufocus', function( event, ui ) {
+ ui.item.attr( 'aria-selected', 'true' );
+ })
+ .on( 'menublur', function() {
+ /*
+ * The `menublur` event returns an object where the item is `null`
+ * so we need to find the active item with other means.
+ */
+ $( this ).find( '[aria-selected="true"]' ).removeAttr( 'aria-selected' );
+ });
+ }
+
+ tinymce.$( input ).on( 'keydown', function( event ) {
+ if ( event.keyCode === 13 ) {
+ editor.execCommand( 'wp_link_apply' );
+ event.preventDefault();
+ }
+ } );
+ }
+ } );
+
+ editor.on( 'wptoolbar', function( event ) {
+ var linkNode = editor.dom.getParent( event.element, 'a' ),
+ $linkNode, href, edit;
+
+ if ( tinymce.$( document.body ).hasClass( 'modal-open' ) ) {
+ editToolbar.tempHide = true;
+ return;
+ }
+
+ editToolbar.tempHide = false;
+
+ if ( linkNode ) {
+ $linkNode = editor.$( linkNode );
+ href = $linkNode.attr( 'href' );
+ edit = $linkNode.attr( 'data-wplink-edit' );
+
+ if ( href === '_wp_link_placeholder' || edit ) {
+ if ( edit && ! inputInstance.getURL() ) {
+ inputInstance.setURL( href );
+ }
+
+ event.element = linkNode;
+ event.toolbar = editToolbar;
+ } else if ( href && ! $linkNode.find( 'img' ).length ) {
+ previewInstance.setURL( href );
+ event.element = linkNode;
+ event.toolbar = toolbar;
+ }
+ }
+ } );
+
+ editor.addButton( 'wp_link_edit', {
+ tooltip: 'Edit ', // trailing space is needed, used for context
+ icon: 'dashicon dashicons-edit',
+ cmd: 'WP_Link'
+ } );
+
+ editor.addButton( 'wp_link_remove', {
+ tooltip: 'Remove',
+ icon: 'dashicon dashicons-no',
+ cmd: 'unlink'
+ } );
+
+ editor.addButton( 'wp_link_advanced', {
+ tooltip: 'Link options',
+ icon: 'dashicon dashicons-admin-generic',
+ onclick: function() {
+ if ( typeof window.wpLink !== 'undefined' ) {
+ var url = inputInstance.getURL() || null,
+ text = inputInstance.getLinkText() || null;
+
+ /*
+ * Accessibility note: moving focus back to the editor confuses
+ * screen readers. They will announce again the Editor ARIA role
+ * `application` and the iframe `title` attribute.
+ *
+ * Unfortunately IE looses the selection when the editor iframe
+ * looses focus, so without returning focus to the editor, the code
+ * in the modal will not be able to get the selection, place the caret
+ * at the same location, etc.
+ */
+ if ( tinymce.Env.ie ) {
+ editor.focus(); // Needed for IE
+ }
+
+ window.wpLink.open( editor.id, url, text, linkNode );
+
+ editToolbar.tempHide = true;
+ inputInstance.reset();
+ }
+ }
+ } );
+
+ editor.addButton( 'wp_link_apply', {
+ tooltip: 'Apply',
+ icon: 'dashicon dashicons-editor-break',
+ cmd: 'wp_link_apply',
+ classes: 'widget btn primary'
+ } );
+
+ return {
+ close: function() {
+ editToolbar.tempHide = false;
+ editor.execCommand( 'wp_link_cancel' );
+ }
+ };