diff --git a/wp-admin/admin-ajax.php b/wp-admin/admin-ajax.php index 27ca2a1246..c2aa989e12 100644 --- a/wp-admin/admin-ajax.php +++ b/wp-admin/admin-ajax.php @@ -1100,6 +1100,13 @@ case 'menu-quick-search': _wp_ajax_menu_quick_search( $_REQUEST ); + exit; + break; +case 'wp-link-ajax': + require_once ABSPATH . WPINC . '/js/tinymce/wp-mce-link-includes.php'; + + wp_link_ajax( $_POST ); + exit; break; case 'menu-locations-save': diff --git a/wp-admin/css/colors-classic.dev.css b/wp-admin/css/colors-classic.dev.css index 76fd6a4dfd..63355d242b 100644 --- a/wp-admin/css/colors-classic.dev.css +++ b/wp-admin/css/colors-classic.dev.css @@ -110,13 +110,16 @@ div.dashboard-widget-submit { } div.tabs-panel, +.wp-tabs-panel, ul.category-tabs li.tabs, -ul.add-menu-item-tabs li.tabs { +ul.add-menu-item-tabs li.tabs, +.wp-tab-active { border-color: #dfdfdf; } ul.category-tabs li.tabs, -ul.add-menu-item-tabs li.tabs { +ul.add-menu-item-tabs li.tabs, +.wp-tab-active { background-color: #f1f1f1; } @@ -383,7 +386,8 @@ div.dashboard-widget-submit input:hover, } #side-sortables .category-tabs .tabs a, -#side-sortables .add-menu-item-tabs .tabs a { +#side-sortables .add-menu-item-tabs .tabs a, +.wp-tab-bar .wp-tab-active a { color: #333; } diff --git a/wp-admin/css/colors-fresh.dev.css b/wp-admin/css/colors-fresh.dev.css index 2278e1db98..4b75c3bdd5 100644 --- a/wp-admin/css/colors-fresh.dev.css +++ b/wp-admin/css/colors-fresh.dev.css @@ -110,13 +110,16 @@ div.dashboard-widget-submit { } div.tabs-panel, +.wp-tab-panel, ul.category-tabs li.tabs, -ul.add-menu-item-tabs li.tabs { +ul.add-menu-item-tabs li.tabs, +.wp-tab-active { border-color: #dfdfdf; } ul.category-tabs li.tabs, -ul.add-menu-item-tabs li.tabs { +ul.add-menu-item-tabs li.tabs, +.wp-tab-active { background-color: #f1f1f1; } @@ -382,7 +385,8 @@ div.dashboard-widget-submit input:hover, } #side-sortables .category-tabs .tabs a, -#side-sortables .add-menu-item-tabs .tabs a { +#side-sortables .add-menu-item-tabs .tabs a, +.wp-tab-bar .wp-tab-active a { color: #333; } diff --git a/wp-admin/css/wp-admin.dev.css b/wp-admin/css/wp-admin.dev.css index 85877db553..5a11efad90 100644 --- a/wp-admin/css/wp-admin.dev.css +++ b/wp-admin/css/wp-admin.dev.css @@ -2029,6 +2029,7 @@ input#link_url { text-decoration: none; } +.wp-tab-panel, .categorydiv div.tabs-panel, .customlinkdiv div.tabs-panel, .posttypediv div.tabs-panel, @@ -2063,17 +2064,20 @@ div.tabs-panel-inactive { } #side-sortables .category-tabs li, -#side-sortables .add-menu-item-tabs li { +#side-sortables .add-menu-item-tabs li, +.wp-tab-bar li { display: inline; } #side-sortables .category-tabs a, -#side-sortables .add-menu-item-tabs a { +#side-sortables .add-menu-item-tabs a, +.wp-tab-bar a { text-decoration: none; } #side-sortables .category-tabs, -#side-sortables .add-menu-item-tabs { +#side-sortables .add-menu-item-tabs, +.wp-tab-bar { margin-bottom: 3px; } @@ -2110,21 +2114,15 @@ ul.categorychecklist li { margin-bottom: 0px; } -.categorydiv .tabs-panel, -.customlinkdiv .tabs-panel, -.posttypediv .tabs-panel, -.taxonomydiv .tabs-panel { - border-width: 3px; - border-style: solid; -} - ul.category-tabs, -ul.add-menu-item-tabs { +ul.add-menu-item-tabs, +ul.wp-tab-bar { margin-top: 12px; } ul.category-tabs li.tabs, -ul.add-menu-item-tabs li.tabs { +ul.add-menu-item-tabs li.tabs, +.wp-tab-active { border-style: solid solid none; border-width: 1px 1px 0; } @@ -2137,7 +2135,8 @@ ul.add-menu-item-tabs li.tabs { } ul.category-tabs li, -ul.add-menu-item-tabs li { +ul.add-menu-item-tabs li, +ul.wp-tab-bar li { padding: 5px; -moz-border-radius: 3px 3px 0 0; -webkit-border-top-left-radius: 3px; diff --git a/wp-admin/includes/post.php b/wp-admin/includes/post.php index 0a61f97997..f6884e623e 100644 --- a/wp-admin/includes/post.php +++ b/wp-admin/includes/post.php @@ -1311,10 +1311,10 @@ function wp_tiny_mce( $teeny = false, $settings = false ) { $mce_spellchecker_languages = apply_filters('mce_spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv'); if ( $teeny ) { - $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'media', 'fullscreen', 'wordpress') ); + $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'media', 'fullscreen', 'wordpress', 'wplink') ); $ext_plugins = ''; } else { - $plugins = array( 'inlinepopups', 'spellchecker', 'paste', 'wordpress', 'media', 'fullscreen', 'wpeditimage', 'wpgallery', 'tabfocus' ); + $plugins = array( 'inlinepopups', 'spellchecker', 'paste', 'wordpress', 'media', 'fullscreen', 'wpeditimage', 'wpgallery', 'tabfocus', 'wplink' ); /* The following filter takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'. diff --git a/wp-includes/js/tinymce/plugins/wplink/editor_plugin.dev.js b/wp-includes/js/tinymce/plugins/wplink/editor_plugin.dev.js new file mode 100644 index 0000000000..c420eb3ffd --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wplink/editor_plugin.dev.js @@ -0,0 +1,56 @@ +(function() { + tinymce.create('tinymce.plugins.wpLink', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init : function(ed, url) { + // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); + ed.addCommand('WP_Link', function() { + ed.windowManager.open({ + file : tinymce.baseURL + '/wp-mce-link.php', + width : 320, + height : 340, + inline : 1 + }, { + plugin_url : url // Plugin absolute URL + }); + }); + + // Register example button + ed.addButton('link', { + title : ed.getLang('advanced.link_desc'), + cmd : 'WP_Link' + }); + + ed.addShortcut('alt+shift+a', ed.getLang('advanced.link_desc'), 'WP_Link'); + + // Add a node change handler, selects the button in the UI when a link is selected + ed.onNodeChange.add(function(ed, cm, n) { + cm.setActive('wplink', n.nodeName == 'A'); + }); + }, + /** + * Returns information about the plugin as a name/value array. + * The current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the plugin. + */ + getInfo : function() { + return { + longname : 'WordPress Link Dialog', + author : 'WordPress', + authorurl : 'http://wordpress.org', + infourl : '', + version : "1.0" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('wplink', tinymce.plugins.wpLink); +})(); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wplink/editor_plugin.js b/wp-includes/js/tinymce/plugins/wplink/editor_plugin.js new file mode 100644 index 0000000000..96291e75c4 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wplink/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.wpLink",{init:function(a,b){a.addCommand("WP_Link",function(){a.windowManager.open({file:tinymce.baseURL+"/wp-mce-link.php",width:320,height:340,inline:1},{plugin_url:b})});a.addButton("link",{title:a.getLang("advanced.link_desc"),cmd:"WP_Link"});a.addShortcut("alt+shift+a",a.getLang("advanced.link_desc"),"WP_Link");a.onNodeChange.add(function(d,c,e){c.setActive("wplink",e.nodeName=="A")})},getInfo:function(){return{longname:"WordPress Link Dialog",author:"WordPress",authorurl:"http://wordpress.org",infourl:"",version:"1.0"}}});tinymce.PluginManager.add("wplink",tinymce.plugins.wpLink)})(); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wplink/js/wplink.js b/wp-includes/js/tinymce/plugins/wplink/js/wplink.js new file mode 100644 index 0000000000..fab3eb5bcc --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wplink/js/wplink.js @@ -0,0 +1,296 @@ +(function($){ + $.widget('wp.wpTabs', { + options: {}, + _create: function() { + var self = this, + ul = this.element, + lis = ul.children(); + + this.active = lis.filter('.wp-tab-active'); + // Calculate panel IDs + lis.each(function() { + var panel = self._getPanel( $(this) ); + if ( self.active[0] == this ) + panel.show(); + else + panel.hide(); + }); + + ul.delegate('li', 'click.wpTabs', function(e) { + var li = $(this); + + // Prevent any child link from redirecting the page. + e.preventDefault(); + // Deactivate previous tab. + self._getPanel( self.active ).hide(); + self.active.removeClass('wp-tab-active'); + self._trigger("hide", e, self.widget() ); + + // Activate current tab. + self.active = li.addClass('wp-tab-active'); + self._getPanel( self.active ).show(); + self._trigger("show", e, self.widget() ); + }); + }, + widget: function() { + return { + ul: this.element, + tab: this.active, + panel: this._getPanel( this.active ) + }; + }, + _setPanel: function( $el ) { + var panel = $( '#' + $el.children('.wp-tab-for-id').val() ); + $el.data( 'wp-tab-panel', panel ); + return panel; + }, + _getPanel: function( $el ) { + var panel = $el.data('wp-tab-panel'); + return ( !panel || !panel.length ) ? this._setPanel( $el ) : panel; + } + }); + // Create tab bars by default. + $(function(){ + $('.wp-tab-bar').wpTabs(); + }); +})(jQuery); + +(function($){ + var inputs = {}, panels, active, ed, + wpLink = { + init : function() { + var e, etarget, eclass; + // Init shared vars + ed = tinyMCEPopup.editor; + // Secondary options + inputs.title = $('#link-title-field'); + // Advanced Options + inputs.openInNewTab = $('#link-target-checkbox'); + // Types + inputs.typeDropdown = $('#link-type'); + inputs.typeOptions = inputs.typeDropdown.find('option'); + + panels = $('.link-panel'); + active = $('.link-panel-active'); + + // Extract type names + inputs.typeOptions.each( function(){ + var linkType = this.id.replace(/^link-option-id-/,''), + parts = linkType.split('-'); + $(this).data( 'link-type', { + full : linkType, + type : parts[0], + name : parts[1] || '' + }); + }); + panels.each( function(){ + var linkType = this.id.replace(/^link-panel-id-/,''), + parts = linkType.split('-'); + $(this).data( 'link-type', { + full : linkType, + type : parts[0], + name : parts[1] || '' + }); + }); + + // Bind event handlers + inputs.typeDropdown.change( wpLink.selectPanel ); + $('#wp-update').click( wpLink.update ); + $('#wp-cancel').click( function() { tinyMCEPopup.close(); } ); + $('.link-panel .wp-tab-bar').wpTabs('option', 'show', wpLink.maybeLoadPanel ); + $('.link-panel .wp-tab-panel').delegate('li', 'click', wpLink.selectInternalLink ); + $('.wp-tab-panel-pagelinks').delegate('a', 'click', wpLink.selectPageLink ); + $('.link-panel .link-search-field').keyup( wpLink.searchInternalLinks ); + + // If link exists, select proper values. + e = ed.dom.getParent(ed.selection.getNode(), 'A'); + if ( ! e ) + return; + + // @TODO: select proper panel/fill values when a link is edited + active.find('input.url-field').val( e.href ); + inputs.title.val( ed.dom.getAttrib(e, 'title') ); + // Advanced Options + + if ( "_blank" == ed.dom.getAttrib(e, 'target') ) + inputs.openInNewTab.attr('checked','checked'); + }, + + update : function() { + var el, + ed = tinyMCEPopup.editor, + attrs = { + title : inputs.title.val(), + target : inputs.openInNewTab.attr('checked') ? '_blank' : '' + }, defaultContent, e, b; + + if ( active.hasClass('link-panel-custom') ) { + attrs.href = active.find('input.url-field').val(); + defaultContent = attrs.href; + } else { + el = active.find('li.selected:visible'); + if ( !el.length ) + return; + + attrs.href = el.children('input').val(); + defaultContent = el.text(); + } + + tinyMCEPopup.restoreSelection(); + e = ed.dom.getParent(ed.selection.getNode(), 'A'); + + // If the values are empty... + if ( ! attrs.href ) { + // ...and nothing is selected, we should return + if ( ed.selection.isCollapsed() ) { + tinyMCEPopup.close(); + return; + // ...and a link exists, we should unlink and return + } else if ( e ) { + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + b = ed.selection.getBookmark(); + ed.dom.remove(e, 1); + ed.selection.moveToBookmark(b); + tinyMCEPopup.execCommand("mceEndUndoLevel"); + tinyMCEPopup.close(); + return; + } + } + + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + + if (e == null) { + ed.getDoc().execCommand("unlink", false, null); + + // If no selection exists, create a new link from scratch. + if ( ed.selection.isCollapsed() ) { + var el = ed.dom.create('a', { href: "#mce_temp_url#" }, defaultContent); + ed.selection.setNode(el); + // If a selection exists, wrap it in a link. + } else { + tinyMCEPopup.execCommand("CreateLink", false, "#mce_temp_url#", {skip_undo : 1}); + } + + tinymce.each(ed.dom.select("a"), function(n) { + if (ed.dom.getAttrib(n, 'href') == '#mce_temp_url#') { + e = n; + ed.dom.setAttribs(e, attrs); + } + }); + } else { + ed.dom.setAttribs(e, attrs); + } + + // Don't move caret if selection was image + if (e.childNodes.length != 1 || e.firstChild.nodeName != 'IMG') { + ed.focus(); + ed.selection.select(e); + ed.selection.collapse(0); + tinyMCEPopup.storeSelection(); + } + + tinyMCEPopup.execCommand("mceEndUndoLevel"); + tinyMCEPopup.close(); + }, + + selectPanel : function( option ) { + var sel = inputs.typeOptions.filter(':selected'); + + if ( option.jquery ) { + sel.removeAttr('selected'); + sel = option.attr('selected', 'selected'); + } + + active.removeClass('link-panel-active'); + active = $('#link-panel-id-' + sel.data('link-type').full ).addClass('link-panel-active'); + wpLink.maybeLoadPanel(); + }, + + maybeLoadPanel : function() { + var panel = active.find('.wp-tab-panel:visible'); + if ( panel.length && panel.find('.wp-tab-panel-loading').length ) + wpLink.linkPanelAJAX( panel ); + }, + + linkPanelAJAX : function( $panel, params, callback ) { + if ( ! $panel.hasClass('wp-tab-panel') ) + $panel = $panel.parents('.wp-tab-panel'); + + if ( ! $panel.length ) + return; + + var query = $panel.children('.wp-tab-panel-query').val(); + + wpLink.linkAJAX( $panel, $.extend({ + preset : query, + page : 'all' == query ? 1 : 0 + }, params), function(r, lt) { + var pagelinks = $panel.children('.wp-tab-panel-pagelinks'); + + // Set results + $panel.children('ul').html( wpLink.generateListMarkup( r['results'], lt ) ); + + // Handle page links + if ( r['page_links'] ) + pagelinks.html( r['page_links'] ).show(); + else + pagelinks.hide(); + // Run callback + if ( callback ) + callback(r, lt); + }) + }, + + selectInternalLink : function() { + var t = $(this); + if ( t.hasClass('unselectable') ) + return; + t.siblings('.selected').removeClass('selected'); + t.addClass('selected'); + }, + + selectPageLink : function(e) { + var page = e.target.href.match(/page=(\d+)/); + + page = page ? page[1] : 1; // If there's no match, it's the first page. + e.preventDefault(); // Prevent the link from redirecting. + + wpLink.linkPanelAJAX( $(this), { page : page }); + }, + + searchInternalLinks : function() { + var t = $(this), + waiting = t.siblings('img.waiting').show(); + + wpLink.linkPanelAJAX( t, { title : t.val() }, function(){ waiting.hide(); }); + }, + + linkAJAX : function( el, params, callback ) { + var linkType = el.parents('.link-panel').data('link-type'); + $.post( ajaxurl, $.extend({ + action : 'wp-link-ajax', + type : linkType.type, + name : linkType.name + }, params ), function(r) { + return callback(r, linkType); + }, "json" ); + }, + + generateListMarkup : function( results, linkType ) { + var s = ''; + + if ( ! results ) + return '
  • ' + wpLinkL10n.noMatchesFound + '
  • '; + + $.each( results, function() { + s+= ''; + }); + return s; + } + } + + $(document).ready( wpLink.init ); +})(jQuery); \ No newline at end of file diff --git a/wp-includes/js/tinymce/wp-mce-link-includes.php b/wp-includes/js/tinymce/wp-mce-link-includes.php new file mode 100644 index 0000000000..017b35ab79 --- /dev/null +++ b/wp-includes/js/tinymce/wp-mce-link-includes.php @@ -0,0 +1,214 @@ +tabs, array( + 'label' => $label, + 'for' => $id, + 'url' => $url + )); + } + + function select( $id ) { + $this->selected = $id; + } + + function render( $echo=true ) { + if ( empty( $this->selected ) ) + $this->selected = $this->tabs[0]['for']; + + $out = "