TinyMCE - custom link button - "add link" is fine, but can't find any documentation for "edit link" and accessing link attributes

LiverpoolsNumber9 picture LiverpoolsNumber9 · Feb 21, 2011 · Viewed 11.2k times · Source

I've added a custom button to TinyMCE which brings up a bespoke link-picker. When the user selects some text and clicks the button the dialog appears and when they've picked the url I'm using execCommand('insertHTML', false, "<a href... etc">) on the selection.

This works fine - now, when the link has already been created, and the user wants to edit it, they click the link button again (when the cursor is inside the linked text as normal), but then here is the situation - I don't know how to access the already created link and it's attributes to then load up and populate the dialogue again!

Have search TinyMCE site, Stack, Google in general. Hoping for (and also slightly dreading) a simple answer - but if not, a complex one will be fine!!

If anybody knows the answer or can point me to it, I'd be extremely grateful. Thanks in advance, Rob

EDIT - bits of my code to explain need

In the TinyMCE init:

setup: function (ed) {
    ed.addButton("link", {
        title: "Link",
        onclick: function (evt) {
            Intranet.TextEditor._loadUrlDialog(jQueryTextAreaObject, evt);
        }
    });
}

The function which is called above:

_loadUrlDialog: function (jQueryTextAreaObject, clickEvent) {

    var mce = $(jQueryTextAreaObject).tinymce();

    var isSelected = mce.selection.getContent().length != 0 ? true : false;

    if (isSelected) {
        Intranet.UrlDialog.Fn.LoadDialog("", true, "", function (url, target, title) {

            var theTarget = target == false ? "_self" : "_blank";

            var link = "<a href=\"" + url + "\" target=\"" + theTarget + "\" title=\"" + title + "\">" + mce.selection.getContent() + "</a>";

            mce.execCommand('insertHTML', false, link); // creates new link

        });
    }
    else {
        /// THIS IS THE MISSING BIT!
    };
}

Answer

Thariama picture Thariama · Feb 21, 2011

You have two ways of achieving this:

When pushing the button you check for the selection parent node. If the node is a link then you can get the link information from the html a-element. To populate your dialogue you will know what to do.

The other option is to add a contextmenu on rightclick, which will provide the necessary functionalities.

Here is the plugin code for this (keep in mind that you will have to add "customcontextmenu" to the plugin-setting of your tinymce).

/**
 * editor_plugin_src.js
 *
 * Plugin for contextmenus.
 */

(function() {
    var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM;

    tinymce.PluginManager.requireLangPack('customcontextmenu');

    /**
     * This plugin a context menu to TinyMCE editor instances.
     *
     * @class tinymce.plugins.customcontextmenu
     */
    tinymce.create('tinymce.plugins.customcontextmenu', {
        /**
         * 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.
         *
         * @method init
         * @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) {
            var t = this, lastRng;

            t.editor = ed;

            // Registiere commands
            ed.addCommand('edit_inline_element', function() {
                //edit_inline_element(ed, ed.right_clicked_node);  //ed.right_clicked_node is the actually clicked node
                //call or do whatever you need here
            });

            // delete link
            ed.addCommand('delete_inline_element', function() {
                $(ed.right_clicked_node).replaceWith($(ed.right_clicked_node).html());
            });

                    // assign the right clicked node (it is the evt.target)
        ed.onClick.add(function(ed, evt) {
            if (evt.button == 2) ed.right_clicked_node = evt.target;
        });

            /**
             * This event gets fired when the context menu is shown.
             *
             * @event onContextMenu
             * @param {tinymce.plugins.ContextMenu} sender Plugin instance sending the event.
             * @param {tinymce.ui.DropMenu} menu Drop down menu to fill with more items if needed.
             */
            t.onContextMenu = new tinymce.util.Dispatcher(this);

            ed.onContextMenu.add(function(ed, e) {

                if (!e.ctrlKey) {

                    // Restore the last selection since it was removed
                    if (lastRng)
                        ed.selection.setRng(lastRng);

                    var menu = t._getMenu(ed);

                    if ((typeof menu).toLowerCase() == 'object')
                    {
                        menu.showMenu(e.clientX, e.clientY);

                        Event.add(ed.getDoc(), 'click', function(e) {
                            hide(ed, e);
                        });
                        Event.cancel(e);
                    }
                    // sonst Standardmenu anzeigen
                }

            });

            ed.onRemove.add(function() {
                if (t._menu)
                    t._menu.removeAll();
            });

            function hide(ed, e) {
                lastRng = null;

                // Since the contextmenu event moves
                // the selection we need to store it away
                if (e && e.button == 2) {
                    lastRng = ed.selection.getRng();
                    return;
                }

                if (t._menu) {
                    t._menu.removeAll();
                    t._menu.destroy();
                    Event.remove(ed.getDoc(), 'click', hide);
                }
            };

            ed.onMouseDown.add(hide);
            ed.onKeyDown.add(hide);
        },

        _getMenu: function(ed){
            var t = this, m = t._menu, se = ed.selection, col = se.isCollapsed(), el = se.getNode() || ed.getBody(), am, p1, p2;
            if (m) {
                m.removeAll();
                m.destroy();
            }

            p1 = DOM.getPos(ed.getContentAreaContainer());
            p2 = DOM.getPos(ed.getContainer());

            m = ed.controlManager.createDropMenu('contextmenu', {
                offset_x : p1.x + ed.getParam('contextmenu_offset_x', 0),
                offset_y : p1.y + ed.getParam('contextmenu_offset_y', 0),
                constrain : 1
            });

            t._menu = m;

                    if ((typeof ed.right_clicked_node) !== "undefined" && ed.right_clicked_node.nodeName.toLowerCase() == 'a' )
                    {
                        m.add({
                            title: $(ed.right_clicked_node).attr('title'),
                        });

                        m.addSeparator();

                        m.add({
                            title: 'Edit link',
                            icon: 'edit_inline_element',
                            cmd: 'edit_link'
                        });

                        m.add({
                            title: 'Delete link',
                            icon: 'delete_inline_element',
                            cmd: 'delete_link'
                        });

                        t.onContextMenu.dispatch(t, m, el, col);

                        return m;
                    }
                    else {
                        // kein Menu anzeigen
                        return 0;
                    }
        }
    });

    // Register plugin
    tinymce.PluginManager.add('customcontextmenu', tinymce.plugins.customcontextmenu);
})();