From b780792d87534846166bec037aaeacac076a2600 Mon Sep 17 00:00:00 2001 From: Ella Iseulde Van Dorpe Date: Sat, 23 Jan 2016 00:07:29 +0000 Subject: [PATCH] TinyMCE: add inline link dialog First run. Links the advanced button to the "old" dialog for now. See #33301. git-svn-id: https://develop.svn.wordpress.org/trunk@36384 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/ajax-actions.php | 8 +- src/wp-includes/class-wp-editor.php | 1 + src/wp-includes/css/editor.css | 76 ++++++ .../js/tinymce/plugins/wordpress/plugin.js | 24 +- .../js/tinymce/plugins/wplink/plugin.js | 232 ++++++++++++++++-- 5 files changed, 311 insertions(+), 30 deletions(-) diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index ec3e1e1057..68f8d508d1 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -1477,8 +1477,14 @@ function wp_ajax_wp_link_ajax() { $args = array(); - if ( isset( $_POST['search'] ) ) + if ( isset( $_POST['search'] ) ) { $args['s'] = wp_unslash( $_POST['search'] ); + } + + if ( isset( $_POST['term'] ) ) { + $args['s'] = wp_unslash( $_POST['term'] ); + } + $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1; require(ABSPATH . WPINC . '/class-wp-editor.php'); diff --git a/src/wp-includes/class-wp-editor.php b/src/wp-includes/class-wp-editor.php index 4296722092..88c64a01bd 100644 --- a/src/wp-includes/class-wp-editor.php +++ b/src/wp-includes/class-wp-editor.php @@ -779,6 +779,7 @@ final class _WP_Editors { if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) { wp_enqueue_script('wplink'); + wp_enqueue_script( 'jquery-ui-autocomplete' ); } if ( self::$old_dfw_compat ) { diff --git a/src/wp-includes/css/editor.css b/src/wp-includes/css/editor.css index 6d99c9799b..af0f7ce991 100644 --- a/src/wp-includes/css/editor.css +++ b/src/wp-includes/css/editor.css @@ -451,6 +451,46 @@ div.mce-path { width: 20px; } +.mce-toolbar .mce-btn-group .mce-btn.mce-primary { + min-width: 0; + background: #0085ba; + border-color: #0073aa #006799 #006799; + -webkit-box-shadow: 0 1px 0 #006799; + box-shadow: 0 1px 0 #006799; + color: #fff; + text-decoration: none; + text-shadow: 0 -1px 1px #006799, + 1px 0 1px #006799, + 0 1px 1px #006799, + -1px 0 1px #006799; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-primary .mce-ico { + color: #fff; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-primary:hover, +.mce-toolbar .mce-btn-group .mce-btn.mce-primary:focus { + background: #008ec2; + border-color: #006799; + color: #fff; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-primary:focus { + -webkit-box-shadow: 0 1px 0 #0073aa, + 0 0 2px 1px #33b3db; + box-shadow: 0 1px 0 #0073aa, + 0 0 2px 1px #33b3db; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-primary:active { + background: #0073aa; + border-color: #006799; + -webkit-box-shadow: inset 0 2px 0 #006799; + box-shadow: inset 0 2px 0 #006799; + vertical-align: top; +} + /* mce listbox */ .mce-toolbar .mce-btn-group .mce-btn.mce-listbox { -webkit-border-radius: 0; @@ -1709,6 +1749,42 @@ div.wp-link-preview a { } } +div.wp-link-input { + float: left; + margin: 2px; + max-width: 694px; +} + +div.wp-link-input input { + width: 300px; + padding: 3px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +@media screen and ( max-width: 400px ) { + div.wp-link-input { + min-width: 0; + max-width: 70%; + max-width: -webkit-calc(100% - 80px); + max-width: calc(100% - 80px); + } + + div.wp-link-input input { + width: 100%; + font-size: 16px; + padding: 4px; + } +} + +.ui-autocomplete.mce-wp-autocomplete { + z-index: 100100; + margin-top: 10px; + max-height: 200px; + overflow-y: auto; +} + /* =Overlay Body -------------------------------------------------------------- */ diff --git a/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js b/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js index 8e4d5b6a74..13ef3af059 100644 --- a/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js +++ b/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js @@ -744,7 +744,10 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) { top, left; if ( spaceTop >= editorHeight || spaceBottom >= editorHeight ) { - return this.hide(); + this.scrolling = true; + this.hide(); + this.scrolling = false; + return this; } // Add offset in iOS to move the menu over the image, out of the way of the default iOS menu. @@ -850,13 +853,17 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) { currentSelection = args.selection || args.element; - if ( activeToolbar ) { + if ( activeToolbar && activeToolbar !== args.toolbar ) { activeToolbar.hide(); } if ( args.toolbar ) { - activeToolbar = args.toolbar; - activeToolbar.show(); + if ( activeToolbar !== args.toolbar ) { + activeToolbar = args.toolbar; + activeToolbar.show(); + } else { + activeToolbar.reposition(); + } } else { activeToolbar = false; } @@ -870,18 +877,21 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) { function hide( event ) { if ( activeToolbar ) { - activeToolbar.hide(); - if ( event.type === 'hide' ) { + activeToolbar.hide(); activeToolbar = false; - } else if ( event.type === 'resize' || event.type === 'scroll' ) { + } else if ( ( event.type === 'resize' || event.type === 'scroll' ) && ! activeToolbar.blockHide ) { clearTimeout( timeout ); timeout = setTimeout( function() { if ( activeToolbar && typeof activeToolbar.show === 'function' ) { + activeToolbar.scrolling = false; activeToolbar.show(); } }, 250 ); + + activeToolbar.scrolling = true; + activeToolbar.hide(); } } } diff --git a/src/wp-includes/js/tinymce/plugins/wplink/plugin.js b/src/wp-includes/js/tinymce/plugins/wplink/plugin.js index eef8940c63..2a8c4a14eb 100644 --- a/src/wp-includes/js/tinymce/plugins/wplink/plugin.js +++ b/src/wp-includes/js/tinymce/plugins/wplink/plugin.js @@ -47,12 +47,105 @@ } } ); + tinymce.ui.WPLinkInput = tinymce.ui.Control.extend( { + renderHtml: function() { + return ( + '' + ); + }, + setURL: function( url ) { + this.getEl().firstChild.value = url; + } + } ); + tinymce.PluginManager.add( 'wplink', function( editor ) { + var a; var toolbar; + var editToolbar; + var previewInstance; + var inputInstance; + var $ = window.jQuery; + + editor.on( 'preinit', function() { + if ( editor.wp && editor.wp._createToolbar ) { + toolbar = editor.wp._createToolbar( [ + 'wp_link_preview', + 'wp_link_edit', + 'wp_link_remove' + ], true ); + + editToolbar = editor.wp._createToolbar( [ + 'wp_link_input', + 'wp_link_apply', + 'wp_link_advanced' + ], true ); + + editToolbar.on( 'show', function() { + var node = editToolbar.find( 'toolbar' )[0]; + node && node.focus( true ); + a = editor.dom.getParent( editor.selection.getNode(), 'a' ); + } ); + + editToolbar.on( 'hide', function() { + editToolbar.scrolling || editor.execCommand( 'wp_link_cancel' ); + } ); + } + } ); editor.addCommand( 'WP_Link', function() { - window.wpLink && window.wpLink.open( editor.id ); - }); + var a = editor.dom.getParent( editor.selection.getNode(), 'a' ); + + if ( a ) { + editor.dom.setAttribs( a, { 'data-wp-edit': true } ); + } else { + editor.execCommand( 'mceInsertLink', false, { href: '_wp_link_placeholder' } ); + } + + editor.nodeChanged(); + } ); + + editor.addCommand( 'wp_link_apply', function() { + if ( editToolbar.scrolling ) { + return; + } + + var href = tinymce.trim( inputInstance.getEl().firstChild.value ); + + if ( href && ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( href ) ) { + href = 'http://' + href; + } + + if ( ! href ) { + editor.dom.remove( a, true ); + return; + } + + if ( a ) { + editor.dom.setAttribs( a, { href: href, 'data-wp-edit': null } ); + } + + a = false; + + editor.nodeChanged(); + editor.focus(); + } ); + + editor.addCommand( 'wp_link_cancel', function() { + if ( a ) { + if ( editor.$( a ).attr( 'href' ) === '_wp_link_placeholder' ) { + editor.dom.remove( a, true ); + } else { + editor.dom.setAttribs( a, { 'data-wp-edit': null } ); + } + } + + a = false; + + editor.nodeChanged(); + editor.focus(); + } ); // WP default shortcut editor.addShortcut( 'access+a', '', 'WP_Link' ); @@ -102,27 +195,115 @@ editor.addButton( 'wp_link_preview', { type: 'WPLinkPreview', onPostRender: function() { - var self = this; + previewInstance = this; + } + } ); - editor.on( 'wptoolbar', function( event ) { - var anchor = editor.dom.getParent( event.element, 'a' ), - $anchor, - href; + editor.addButton( 'wp_link_input', { + type: 'WPLinkInput', + onPostRender: function() { + var input = this.getEl().firstChild; + var cache; + var last; - if ( anchor ) { - $anchor = editor.$( anchor ); - href = $anchor.attr( 'href' ); + inputInstance = this; - if ( href && ! $anchor.find( 'img' ).length ) { - self.setURL( href ); - event.element = anchor; - event.toolbar = toolbar; + if ( $ ) { + $( 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 ); + }, + select: function( event, ui ) { + $( input ).val( ui.item.permalink ); + 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+5' } - } + } ).autocomplete( 'instance' )._renderItem = function( ul, item ) { + return $( '
  • ' ) + .append( '' + item.title + ' ' + item.info + '' ) + .appendTo( ul ); + }; + + $( input ) + .attr( { + 'role': 'combobox', + 'aria-autocomplete': 'list', + 'aria-expanded': 'false', + 'aria-owns': $( input ).autocomplete( 'widget' ).attr( 'id' ) + } ) + .on( 'focus', function() { + $( input ).autocomplete( 'search' ); + } ) + .autocomplete( 'widget' ) + .addClass( 'mce-wp-autocomplete' ) + .attr( 'role', 'listbox' ); + } + + tinymce.$( input ).on( 'keydown', function( event ) { + event.keyCode === 13 && editor.execCommand( 'wp_link_apply' ); } ); } } ); + editor.on( 'wptoolbar', function( event ) { + var anchor = editor.dom.getParent( event.element, 'a' ), + $anchor, + href, edit; + + if ( anchor ) { + $anchor = editor.$( anchor ); + href = $anchor.attr( 'href' ); + edit = $anchor.attr( 'data-wp-edit' ); + + if ( href === '_wp_link_placeholder' || edit ) { + inputInstance.setURL( edit ? href : '' ); + event.element = anchor; + event.toolbar = editToolbar; + } else if ( href && ! $anchor.find( 'img' ).length ) { + previewInstance.setURL( href ); + event.element = anchor; + event.toolbar = toolbar; + } + } + } ); + editor.addButton( 'wp_link_edit', { tooltip: 'Edit ', // trailing space is needed, used for context icon: 'dashicon dashicons-edit', @@ -135,14 +316,21 @@ cmd: 'unlink' } ); - editor.on( 'preinit', function() { - if ( editor.wp && editor.wp._createToolbar ) { - toolbar = editor.wp._createToolbar( [ - 'wp_link_preview', - 'wp_link_edit', - 'wp_link_remove' - ], true ); + // Advanced, more, options? + editor.addButton( 'wp_link_advanced', { + tooltip: 'Advanced', + icon: 'dashicon dashicons-admin-generic', + onclick: function() { + editor.execCommand( 'wp_link_apply' ); + window.wpLink && window.wpLink.open( editor.id ); } } ); + + editor.addButton( 'wp_link_apply', { + tooltip: 'Apply', + icon: 'dashicon dashicons-editor-break', + cmd: 'wp_link_apply', + classes: 'widget btn primary' + } ); } ); } )( window.tinymce );