/**
 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

"use strict";

(function () {
    CKEDITOR.plugins.add("link", {
        requires: "dialog,fakeobjects",
        // jscs:disable maximumLineLength
        lang: "af,ar,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn", // %REMOVE_LINE_CORE%
        // jscs:enable maximumLineLength
        icons: "anchor,anchor-rtl,link,unlink", // %REMOVE_LINE_CORE%
        hidpi: true, // %REMOVE_LINE_CORE%
        onLoad: function () {
            // Add the CSS styles for anchor placeholders.
            var iconPath = CKEDITOR.getUrl(
                    this.path +
                        "images" +
                        (CKEDITOR.env.hidpi ? "/hidpi" : "") +
                        "/anchor.png",
                ),
                baseStyle =
                    "background:url(" +
                    iconPath +
                    ") no-repeat %1 center;border:1px dotted #00f;background-size:16px;";

            var template =
                ".%2 a.cke_anchor," +
                ".%2 a.cke_anchor_empty" +
                ",.cke_editable.%2 a[name]" +
                ",.cke_editable.%2 a[data-cke-saved-name]" +
                "{" +
                baseStyle +
                "padding-%1:18px;" +
                // Show the arrow cursor for the anchor image (FF at least).
                "cursor:auto;" +
                "}" +
                ".%2 img.cke_anchor" +
                "{" +
                baseStyle +
                "width:16px;" +
                "min-height:15px;" +
                // The default line-height on IE.
                "height:1.15em;" +
                // Opera works better with "middle" (even if not perfect)
                "vertical-align:text-bottom;" +
                "}";

            // Styles with contents direction awareness.
            function cssWithDir(dir) {
                return template
                    .replace(/%1/g, dir == "rtl" ? "right" : "left")
                    .replace(/%2/g, "cke_contents_" + dir);
            }

            CKEDITOR.addCss(cssWithDir("ltr") + cssWithDir("rtl"));
        },

        init: function (editor) {
            var allowed = "a[!href]",
                required = "a[href]";

            if (CKEDITOR.dialog.isTabEnabled(editor, "link", "advanced"))
                allowed = allowed.replace(
                    "]",
                    ",accesskey,charset,dir,id,lang,name,rel,tabindex,title,type]{*}(*)",
                );
            if (CKEDITOR.dialog.isTabEnabled(editor, "link", "target"))
                allowed = allowed.replace("]", ",target,onclick]");

            // Add the link and unlink buttons.
            editor.addCommand(
                "link",
                new CKEDITOR.dialogCommand("link", {
                    allowedContent: allowed,
                    requiredContent: required,
                }),
            );
            editor.addCommand(
                "anchor",
                new CKEDITOR.dialogCommand("anchor", {
                    allowedContent: "a[!name,id]",
                    requiredContent: "a[name]",
                }),
            );
            editor.addCommand("unlink", new CKEDITOR.unlinkCommand());
            editor.addCommand("removeAnchor", new CKEDITOR.removeAnchorCommand());

            editor.setKeystroke(CKEDITOR.CTRL + 76 /*L*/, "link");

            if (editor.ui.addButton) {
                editor.ui.addButton("Link", {
                    label: editor.lang.link.toolbar,
                    command: "link",
                    toolbar: "links,10",
                });
                editor.ui.addButton("Unlink", {
                    label: editor.lang.link.unlink,
                    command: "unlink",
                    toolbar: "links,20",
                });
                editor.ui.addButton("Anchor", {
                    label: editor.lang.link.anchor.toolbar,
                    command: "anchor",
                    toolbar: "links,30",
                });
            }

            CKEDITOR.dialog.add("link", this.path + "dialogs/link.js");
            CKEDITOR.dialog.add("anchor", this.path + "dialogs/anchor.js");

            editor.on(
                "doubleclick",
                function (evt) {
                    var element =
                        CKEDITOR.plugins.link.getSelectedLink(editor) ||
                        evt.data.element;

                    if (!element.isReadOnly()) {
                        if (element.is("a")) {
                            evt.data.dialog =
                                element.getAttribute("name") &&
                                (!element.getAttribute("href") ||
                                    !element.getChildCount())
                                    ? "anchor"
                                    : "link";

                            // Pass the link to be selected along with event data.
                            evt.data.link = element;
                        } else if (
                            CKEDITOR.plugins.link.tryRestoreFakeAnchor(editor, element)
                        ) {
                            evt.data.dialog = "anchor";
                        }
                    }
                },
                null,
                null,
                0,
            );

            // If event was cancelled, link passed in event data will not be selected.
            editor.on(
                "doubleclick",
                function (evt) {
                    // Make sure both links and anchors are selected (#11822).
                    if (evt.data.dialog in { link: 1, anchor: 1 } && evt.data.link)
                        editor.getSelection().selectElement(evt.data.link);
                },
                null,
                null,
                20,
            );

            // If the "menu" plugin is loaded, register the menu items.
            if (editor.addMenuItems) {
                editor.addMenuItems({
                    anchor: {
                        label: editor.lang.link.anchor.menu,
                        command: "anchor",
                        group: "anchor",
                        order: 1,
                    },

                    removeAnchor: {
                        label: editor.lang.link.anchor.remove,
                        command: "removeAnchor",
                        group: "anchor",
                        order: 5,
                    },

                    link: {
                        label: editor.lang.link.menu,
                        command: "link",
                        group: "link",
                        order: 1,
                    },

                    unlink: {
                        label: editor.lang.link.unlink,
                        command: "unlink",
                        group: "link",
                        order: 5,
                    },
                });
            }

            // If the "contextmenu" plugin is loaded, register the listeners.
            if (editor.contextMenu) {
                editor.contextMenu.addListener(function (element) {
                    if (!element || element.isReadOnly()) return null;

                    var anchor = CKEDITOR.plugins.link.tryRestoreFakeAnchor(
                        editor,
                        element,
                    );

                    if (
                        !anchor &&
                        !(anchor = CKEDITOR.plugins.link.getSelectedLink(editor))
                    )
                        return null;

                    var menu = {};

                    if (anchor.getAttribute("href") && anchor.getChildCount())
                        menu = {
                            link: CKEDITOR.TRISTATE_OFF,
                            unlink: CKEDITOR.TRISTATE_OFF,
                        };

                    if (anchor && anchor.hasAttribute("name"))
                        menu.anchor = menu.removeAnchor = CKEDITOR.TRISTATE_OFF;

                    return menu;
                });
            }

            this.compiledProtectionFunction = getCompiledProtectionFunction(editor);
        },

        afterInit: function (editor) {
            // Empty anchors upcasting to fake objects.
            editor.dataProcessor.dataFilter.addRules({
                elements: {
                    a: function (element) {
                        if (!element.attributes.name) return null;

                        if (!element.children.length)
                            return editor.createFakeParserElement(
                                element,
                                "cke_anchor",
                                "anchor",
                            );

                        return null;
                    },
                },
            });

            var pathFilters = editor._.elementsPath && editor._.elementsPath.filters;
            if (pathFilters) {
                pathFilters.push(function (element, name) {
                    if (name == "a") {
                        if (
                            CKEDITOR.plugins.link.tryRestoreFakeAnchor(
                                editor,
                                element,
                            ) ||
                            (element.getAttribute("name") &&
                                (!element.getAttribute("href") ||
                                    !element.getChildCount()))
                        )
                            return "anchor";
                    }
                });
            }
        },
    });

    // Loads the parameters in a selected link to the link dialog fields.
    var javascriptProtocolRegex = /^javascript:/,
        emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,
        emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/i,
        emailBodyRegex = /body=([^;?:@&=$,\/]*)/i,
        anchorRegex = /^#(.*)$/,
        urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,
        selectableTargets = /^(_(?:self|top|parent|blank))$/,
        encodedEmailLinkRegex =
            /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,
        functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/,
        popupRegex =
            /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/,
        popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi;

    var advAttrNames = {
        id: "advId",
        dir: "advLangDir",
        accessKey: "advAccessKey",
        // 'data-cke-saved-name': 'advName',
        name: "advName",
        lang: "advLangCode",
        tabindex: "advTabIndex",
        title: "advTitle",
        type: "advContentType",
        class: "advCSSClasses",
        charset: "advCharset",
        style: "advStyles",
        rel: "advRel",
    };

    function unescapeSingleQuote(str) {
        return str.replace(/\\'/g, "'");
    }

    function escapeSingleQuote(str) {
        return str.replace(/'/g, "\\$&");
    }

    function protectEmailAddressAsEncodedString(address) {
        var charCode,
            length = address.length,
            encodedChars = [];

        for (var i = 0; i < length; i++) {
            charCode = address.charCodeAt(i);
            encodedChars.push(charCode);
        }

        return "String.fromCharCode(" + encodedChars.join(",") + ")";
    }

    function protectEmailLinkAsFunction(editor, email) {
        var plugin = editor.plugins.link,
            name = plugin.compiledProtectionFunction.name,
            params = plugin.compiledProtectionFunction.params,
            paramName,
            paramValue,
            retval;

        retval = [name, "("];
        for (var i = 0; i < params.length; i++) {
            paramName = params[i].toLowerCase();
            paramValue = email[paramName];

            i > 0 && retval.push(",");
            retval.push(
                "'",
                paramValue
                    ? escapeSingleQuote(encodeURIComponent(email[paramName]))
                    : "",
                "'",
            );
        }
        retval.push(")");
        return retval.join("");
    }

    function getCompiledProtectionFunction(editor) {
        var emailProtection = editor.config.emailProtection || "",
            compiledProtectionFunction;

        // Compile the protection function pattern.
        if (emailProtection && emailProtection != "encode") {
            compiledProtectionFunction = {};

            emailProtection.replace(
                /^([^(]+)\(([^)]+)\)$/,
                function (match, funcName, params) {
                    compiledProtectionFunction.name = funcName;
                    compiledProtectionFunction.params = [];
                    params.replace(/[^,\s]+/g, function (param) {
                        compiledProtectionFunction.params.push(param);
                    });
                },
            );
        }

        return compiledProtectionFunction;
    }

    /**
     * Set of Link plugin helpers.
     *
     * @class
     * @singleton
     */
    CKEDITOR.plugins.link = {
        /**
         * Get the surrounding link element of the current selection.
         *
         *		CKEDITOR.plugins.link.getSelectedLink( editor );
         *
         *		// The following selections will all return the link element.
         *
         *		<a href="#">li^nk</a>
         *		<a href="#">[link]</a>
         *		text[<a href="#">link]</a>
         *		<a href="#">li[nk</a>]
         *		[<b><a href="#">li]nk</a></b>]
         *		[<a href="#"><b>li]nk</b></a>
         *
         * @since 3.2.1
         * @param {CKEDITOR.editor} editor
         */
        getSelectedLink: function (editor) {
            var selection = editor.getSelection();
            var selectedElement = selection.getSelectedElement();
            if (selectedElement && selectedElement.is("a")) return selectedElement;

            var range = selection.getRanges()[0];

            if (range) {
                range.shrink(CKEDITOR.SHRINK_TEXT);
                return editor.elementPath(range.getCommonAncestor()).contains("a", 1);
            }
            return null;
        },

        /**
         * Collects anchors available in the editor (i.e. used by the Link plugin).
         * Note that the scope of search is different for inline (the "global" document) and
         * classic (`iframe`-based) editors (the "inner" document).
         *
         * @since 4.3.3
         * @param {CKEDITOR.editor} editor
         * @returns {CKEDITOR.dom.element[]} An array of anchor elements.
         */
        getEditorAnchors: function (editor) {
            var editable = editor.editable(),
                // The scope of search for anchors is the entire document for inline editors
                // and editor's editable for classic editor/divarea (#11359).
                scope =
                    editable.isInline() && !editor.plugins.divarea
                        ? editor.document
                        : editable,
                links = scope.getElementsByTag("a"),
                imgs = scope.getElementsByTag("img"),
                anchors = [],
                i = 0,
                item;

            // Retrieve all anchors within the scope.
            while ((item = links.getItem(i++))) {
                if (item.data("cke-saved-name") || item.hasAttribute("name")) {
                    anchors.push({
                        name: item.data("cke-saved-name") || item.getAttribute("name"),
                        id: item.getAttribute("id"),
                    });
                }
            }
            // Retrieve all "fake anchors" within the scope.
            i = 0;

            while ((item = imgs.getItem(i++))) {
                if ((item = this.tryRestoreFakeAnchor(editor, item))) {
                    anchors.push({
                        name: item.getAttribute("name"),
                        id: item.getAttribute("id"),
                    });
                }
            }

            return anchors;
        },

        /**
         * Opera and WebKit do not make it possible to select empty anchors. Fake
         * elements must be used for them.
         *
         * @readonly
         * @deprecated 4.3.3 It is set to `true` in every browser.
         * @property {Boolean}
         */
        fakeAnchor: true,

        /**
         * For browsers that do not support CSS3 `a[name]:empty()`. Note that IE9 is included because of #7783.
         *
         * @readonly
         * @deprecated 4.3.3 It is set to `false` in every browser.
         * @property {Boolean} synAnchorSelector
         */

        /**
         * For browsers that have editing issues with an empty anchor.
         *
         * @readonly
         * @deprecated 4.3.3 It is set to `false` in every browser.
         * @property {Boolean} emptyAnchorFix
         */

        /**
         * Returns an element representing a real anchor restored from a fake anchor.
         *
         * @param {CKEDITOR.editor} editor
         * @param {CKEDITOR.dom.element} element
         * @returns {CKEDITOR.dom.element} Restored anchor element or nothing if the
         * passed element was not a fake anchor.
         */
        tryRestoreFakeAnchor: function (editor, element) {
            if (
                element &&
                element.data("cke-real-element-type") &&
                element.data("cke-real-element-type") == "anchor"
            ) {
                var link = editor.restoreRealElement(element);
                if (link.data("cke-saved-name")) return link;
            }
        },

        /**
         * Parses attributes of the link element and returns an object representing
         * the current state (data) of the link. This data format is a plain object accepted
         * e.g. by the Link dialog window and {@link #getLinkAttributes}.
         *
         * **Note:** Data model format produced by the parser must be compatible with the Link
         * plugin dialog because it is passed directly to {@link CKEDITOR.dialog#setupContent}.
         *
         * @since 4.4
         * @param {CKEDITOR.editor} editor
         * @param {CKEDITOR.dom.element} element
         * @returns {Object} An object of link data.
         */
        parseLinkAttributes: function (editor, element) {
            var href =
                    (element &&
                        (element.data("cke-saved-href") ||
                            element.getAttribute("href"))) ||
                    "",
                compiledProtectionFunction =
                    editor.plugins.link.compiledProtectionFunction,
                emailProtection = editor.config.emailProtection,
                javascriptMatch,
                emailMatch,
                anchorMatch,
                urlMatch,
                retval = {};

            if ((javascriptMatch = href.match(javascriptProtocolRegex))) {
                if (emailProtection == "encode") {
                    href = href.replace(
                        encodedEmailLinkRegex,
                        function (match, protectedAddress, rest) {
                            // Without it 'undefined' is appended to e-mails without subject and body (#9192).
                            rest = rest || "";

                            return (
                                "mailto:" +
                                String.fromCharCode.apply(
                                    String,
                                    protectedAddress.split(","),
                                ) +
                                unescapeSingleQuote(rest)
                            );
                        },
                    );
                }
                // Protected email link as function call.
                else if (emailProtection) {
                    href.replace(
                        functionCallProtectedEmailLinkRegex,
                        function (match, funcName, funcArgs) {
                            if (funcName == compiledProtectionFunction.name) {
                                retval.type = "email";
                                var email = (retval.email = {});

                                var paramRegex = /[^,\s]+/g,
                                    paramQuoteRegex = /(^')|('$)/g,
                                    paramsMatch = funcArgs.match(paramRegex),
                                    paramsMatchLength = paramsMatch.length,
                                    paramName,
                                    paramVal;

                                for (var i = 0; i < paramsMatchLength; i++) {
                                    paramVal = decodeURIComponent(
                                        unescapeSingleQuote(
                                            paramsMatch[i].replace(paramQuoteRegex, ""),
                                        ),
                                    );
                                    paramName =
                                        compiledProtectionFunction.params[
                                            i
                                        ].toLowerCase();
                                    email[paramName] = paramVal;
                                }
                                email.address = [email.name, email.domain].join("@");
                            }
                        },
                    );
                }
            }

            if (!retval.type) {
                if ((anchorMatch = href.match(anchorRegex))) {
                    retval.type = "anchor";
                    retval.anchor = {};
                    retval.anchor.name = retval.anchor.id = anchorMatch[1];
                }
                // Protected email link as encoded string.
                else if ((emailMatch = href.match(emailRegex))) {
                    var subjectMatch = href.match(emailSubjectRegex),
                        bodyMatch = href.match(emailBodyRegex);

                    retval.type = "email";
                    var email = (retval.email = {});
                    email.address = emailMatch[1];
                    subjectMatch &&
                        (email.subject = decodeURIComponent(subjectMatch[1]));
                    bodyMatch && (email.body = decodeURIComponent(bodyMatch[1]));
                }
                // urlRegex matches empty strings, so need to check for href as well.
                else if (href && (urlMatch = href.match(urlRegex))) {
                    retval.type = "url";
                    retval.url = {};
                    retval.url.protocol = urlMatch[1];
                    retval.url.url = urlMatch[2];
                }
            }

            // Load target and popup settings.
            if (element) {
                var target = element.getAttribute("target");

                // IE BUG: target attribute is an empty string instead of null in IE if it's not set.
                if (!target) {
                    var onclick =
                            element.data("cke-pa-onclick") ||
                            element.getAttribute("onclick"),
                        onclickMatch = onclick && onclick.match(popupRegex);

                    if (onclickMatch) {
                        retval.target = {
                            type: "popup",
                            name: onclickMatch[1],
                        };

                        var featureMatch;
                        while (
                            (featureMatch = popupFeaturesRegex.exec(onclickMatch[2]))
                        ) {
                            // Some values should remain numbers (#7300)
                            if (
                                (featureMatch[2] == "yes" || featureMatch[2] == "1") &&
                                !(
                                    featureMatch[1] in
                                    { height: 1, width: 1, top: 1, left: 1 }
                                )
                            )
                                retval.target[featureMatch[1]] = true;
                            else if (isFinite(featureMatch[2]))
                                retval.target[featureMatch[1]] = featureMatch[2];
                        }
                    }
                } else {
                    retval.target = {
                        type: target.match(selectableTargets) ? target : "frame",
                        name: target,
                    };
                }

                var advanced = {};

                for (var a in advAttrNames) {
                    var val = element.getAttribute(a);

                    if (val) advanced[advAttrNames[a]] = val;
                }

                var advName = element.data("cke-saved-name") || advanced.advName;

                if (advName) advanced.advName = advName;

                if (!CKEDITOR.tools.isEmpty(advanced)) retval.advanced = advanced;
            }

            return retval;
        },

        /**
         * Converts link data produced by {@link #parseLinkAttributes} into an object which consists
         * of attributes to be set (with their values) and an array of attributes to be removed.
         * This method can be used to compose or to update any link element with the given data.
         *
         * @since 4.4
         * @param {CKEDITOR.editor} editor
         * @param {Object} data Data in {@link #parseLinkAttributes} format.
         * @returns {Object} An object consisting of two keys, i.e.:
         *
         *		{
         *			// Attributes to be set.
         *			set: {
         *				href: 'http://foo.bar',
         *				target: 'bang'
         *			},
         *			// Attributes to be removed.
         *			removed: [
         *				'id', 'style'
         *			]
         *		}
         *
         */
        getLinkAttributes: function (editor, data) {
            var emailProtection = editor.config.emailProtection || "",
                set = {};

            // Compose the URL.
            switch (data.type) {
                case "url":
                    var protocol =
                            data.url && data.url.protocol !== undefined
                                ? data.url.protocol
                                : "http://",
                        url = (data.url && CKEDITOR.tools.trim(data.url.url)) || "";

                    set["data-cke-saved-href"] =
                        url.indexOf("/") === 0 ? url : protocol + url;

                    break;
                case "anchor":
                    var name = data.anchor && data.anchor.name,
                        id = data.anchor && data.anchor.id;

                    set["data-cke-saved-href"] = "#" + (name || id || "");

                    break;
                case "email":
                    var email = data.email,
                        address = email.address,
                        linkHref;

                    switch (emailProtection) {
                        case "":
                        case "encode":
                            var subject = encodeURIComponent(email.subject || ""),
                                body = encodeURIComponent(email.body || ""),
                                argList = [];

                            // Build the e-mail parameters first.
                            subject && argList.push("subject=" + subject);
                            body && argList.push("body=" + body);
                            argList = argList.length ? "?" + argList.join("&") : "";

                            if (emailProtection == "encode") {
                                linkHref = [
                                    "javascript:void(location.href='mailto:'+", // jshint ignore:line
                                    protectEmailAddressAsEncodedString(address),
                                ];
                                // parameters are optional.
                                argList &&
                                    linkHref.push(
                                        "+'",
                                        escapeSingleQuote(argList),
                                        "'",
                                    );

                                linkHref.push(")");
                            } else {
                                linkHref = ["mailto:", address, argList];
                            }

                            break;
                        default:
                            // Separating name and domain.
                            var nameAndDomain = address.split("@", 2);
                            email.name = nameAndDomain[0];
                            email.domain = nameAndDomain[1];

                            linkHref = [
                                "javascript:",
                                protectEmailLinkAsFunction(editor, email),
                            ]; // jshint ignore:line
                    }

                    set["data-cke-saved-href"] = linkHref.join("");
                    break;
            }

            // Popups and target.
            if (data.target) {
                if (data.target.type == "popup") {
                    var onclickList = [
                            "window.open(this.href, '",
                            data.target.name || "",
                            "', '",
                        ],
                        featureList = [
                            "resizable",
                            "status",
                            "location",
                            "toolbar",
                            "menubar",
                            "fullscreen",
                            "scrollbars",
                            "dependent",
                        ],
                        featureLength = featureList.length,
                        addFeature = function (featureName) {
                            if (data.target[featureName])
                                featureList.push(
                                    featureName + "=" + data.target[featureName],
                                );
                        };

                    for (var i = 0; i < featureLength; i++)
                        featureList[i] =
                            featureList[i] +
                            (data.target[featureList[i]] ? "=yes" : "=no");

                    addFeature("width");
                    addFeature("left");
                    addFeature("height");
                    addFeature("top");

                    onclickList.push(featureList.join(","), "'); return false;");
                    set["data-cke-pa-onclick"] = onclickList.join("");
                } else if (data.target.type != "notSet" && data.target.name) {
                    set.target = data.target.name;
                }
            }

            // Advanced attributes.
            if (data.advanced) {
                for (var a in advAttrNames) {
                    var val = data.advanced[advAttrNames[a]];

                    if (val) set[a] = val;
                }

                if (set.name) set["data-cke-saved-name"] = set.name;
            }

            // Browser need the "href" fro copy/paste link to work. (#6641)
            if (set["data-cke-saved-href"]) set.href = set["data-cke-saved-href"];

            var removed = {
                target: 1,
                onclick: 1,
                "data-cke-pa-onclick": 1,
                "data-cke-saved-name": 1,
            };

            if (data.advanced) CKEDITOR.tools.extend(removed, advAttrNames);

            // Remove all attributes which are not currently set.
            for (var s in set) delete removed[s];

            return {
                set: set,
                removed: CKEDITOR.tools.objectKeys(removed),
            };
        },
    };

    // TODO Much probably there's no need to expose these as public objects.

    CKEDITOR.unlinkCommand = function () {};
    CKEDITOR.unlinkCommand.prototype = {
        exec: function (editor) {
            var style = new CKEDITOR.style({
                element: "a",
                type: CKEDITOR.STYLE_INLINE,
                alwaysRemoveElement: 1,
            });
            editor.removeStyle(style);
        },

        refresh: function (editor, path) {
            // Despite our initial hope, document.queryCommandEnabled() does not work
            // for this in Firefox. So we must detect the state by element paths.

            var element = path.lastElement && path.lastElement.getAscendant("a", true);

            if (
                element &&
                element.getName() == "a" &&
                element.getAttribute("href") &&
                element.getChildCount()
            )
                this.setState(CKEDITOR.TRISTATE_OFF);
            else this.setState(CKEDITOR.TRISTATE_DISABLED);
        },

        contextSensitive: 1,
        startDisabled: 1,
        requiredContent: "a[href]",
    };

    CKEDITOR.removeAnchorCommand = function () {};
    CKEDITOR.removeAnchorCommand.prototype = {
        exec: function (editor) {
            var sel = editor.getSelection(),
                bms = sel.createBookmarks(),
                anchor;
            if (
                sel &&
                (anchor = sel.getSelectedElement()) &&
                (!anchor.getChildCount()
                    ? CKEDITOR.plugins.link.tryRestoreFakeAnchor(editor, anchor)
                    : anchor.is("a"))
            )
                anchor.remove(1);
            else {
                if ((anchor = CKEDITOR.plugins.link.getSelectedLink(editor))) {
                    if (anchor.hasAttribute("href")) {
                        anchor.removeAttributes({ name: 1, "data-cke-saved-name": 1 });
                        anchor.removeClass("cke_anchor");
                    } else {
                        anchor.remove(1);
                    }
                }
            }
            sel.selectBookmarks(bms);
        },
        requiredContent: "a[name]",
    };

    CKEDITOR.tools.extend(CKEDITOR.config, {
        /**
         * Whether to show the Advanced tab in the Link dialog window.
         *
         * @cfg {Boolean} [linkShowAdvancedTab=true]
         * @member CKEDITOR.config
         */
        linkShowAdvancedTab: true,

        /**
         * Whether to show the Target tab in the Link dialog window.
         *
         * @cfg {Boolean} [linkShowTargetTab=true]
         * @member CKEDITOR.config
         */
        linkShowTargetTab: true,

        /**
         * Whether JavaScript code is allowed as a `href` attribute in an anchor tag.
         * With this option enabled it is possible to create links like:
         *
         *		<a href="javascript:alert('Hello world!')">hello world</a>
         *
         * By default JavaScript links are not allowed and will not pass
         * the Link dialog window validation.
         *
         * @since 4.4.1
         * @cfg {Boolean} [linkJavaScriptLinksAllowed=false]
         * @member CKEDITOR.config
         */
    });
})();
