// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/*
 * @package    atto_charmap
 * @copyright  2014 Frédéric Massart
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

/**
 * Atto text editor character map plugin
 *
 * @module moodle-atto_charmap-button
 */

var COMPONENTNAME = 'atto_charmap',
    CSS = {
        BUTTON: 'atto_charmap_character',
        CHARMAP: 'atto_charmap_selector'
    },
    /*
     * Map of special characters, kindly borrowed from TinyMCE.
     *
     * Each entries contains in order:
     * - {String} HTML code
     * - {String} HTML numerical code
     * - {Boolean} Whether or not to include it in the list
     * - {String} The language string key
     *
     * @property CHARMAP
     * @type {Array}
     */
    CHARMAP = [
        ['&nbsp;', '&#160;', true, 'nobreakspace'],
        ['&amp;', '&#38;', true, 'ampersand'],
        ['&quot;', '&#34;', true, 'quotationmark'],
        ['&#8253;', '&#8253;', true, 'interrobang'],
        // Finance.
        ['&cent;', '&#162;', true, 'centsign'],
        ['&euro;', '&#8364;', true, 'eurosign'],
        ['&pound;', '&#163;', true, 'poundsign'],
        ['&yen;', '&#165;', true, 'yensign'],
        // Signs.
        ['&copy;', '&#169;', true, 'copyrightsign'],
        ['&reg;', '&#174;', true, 'registeredsign'],
        ['&trade;', '&#8482;', true, 'trademarksign'],
        ['&permil;', '&#8240;', true, 'permillesign'],
        ['&micro;', '&#181;', true, 'microsign'],
        ['&middot;', '&#183;', true, 'middledot'],
        ['&bull;', '&#8226;', true, 'bullet'],
        ['&hellip;', '&#8230;', true, 'threedotleader'],
        ['&prime;', '&#8242;', true, 'minutesfeet'],
        ['&Prime;', '&#8243;', true, 'secondsinches'],
        ['&sect;', '&#167;', true, 'sectionsign'],
        ['&para;', '&#182;', true, 'paragraphsign'],
        ['&szlig;', '&#223;', true, 'sharpsesszed'],
        // Quotations.
        ['&lsaquo;', '&#8249;', true, 'singleleftpointinganglequotationmark'],
        ['&rsaquo;', '&#8250;', true, 'singlerightpointinganglequotationmark'],
        ['&laquo;', '&#171;', true, 'leftpointingguillemet'],
        ['&raquo;', '&#187;', true, 'rightpointingguillemet'],
        ['&lsquo;', '&#8216;', true, 'leftsinglequotationmark'],
        ['&rsquo;', '&#8217;', true, 'rightsinglequotationmark'],
        ['&ldquo;', '&#8220;', true, 'leftdoublequotationmark'],
        ['&rdquo;', '&#8221;', true, 'rightdoublequotationmark'],
        ['&sbquo;', '&#8218;', true, 'singlelow9quotationmark'],
        ['&bdquo;', '&#8222;', true, 'doublelow9quotationmark'],
        ['&lt;', '&#60;', true, 'lessthansign'],
        ['&gt;', '&#62;', true, 'greaterthansign'],
        ['&le;', '&#8804;', true, 'lessthanorequalto'],
        ['&ge;', '&#8805;', true, 'greaterthanorequalto'],
        ['&ndash;', '&#8211;', true, 'endash'],
        ['&mdash;', '&#8212;', true, 'emdash'],
        ['&macr;', '&#175;', true, 'macron'],
        ['&oline;', '&#8254;', true, 'overline'],
        ['&curren;', '&#164;', true, 'currencysign'],
        ['&brvbar;', '&#166;', true, 'brokenbar'],
        ['&uml;', '&#168;', true, 'diaeresis'],
        ['&iexcl;', '&#161;', true, 'invertedexclamationmark'],
        ['&iquest;', '&#191;', true, 'turnedquestionmark'],
        ['&circ;', '&#710;', true, 'circumflexaccent'],
        ['&tilde;', '&#732;', true, 'smalltilde'],
        ['&deg;', '&#176;', true, 'degreesign'],
        ['&minus;', '&#8722;', true, 'minussign'],
        ['&plusmn;', '&#177;', true, 'plusminussign'],
        ['&divide;', '&#247;', true, 'divisionsign'],
        ['&frasl;', '&#8260;', true, 'fractionslash'],
        ['&times;', '&#215;', true, 'multiplicationsign'],
        ['&sup1;', '&#185;', true, 'superscriptone'],
        ['&sup2;', '&#178;', true, 'superscripttwo'],
        ['&sup3;', '&#179;', true, 'superscriptthree'],
        ['&frac14;', '&#188;', true, 'fractiononequarter'],
        ['&frac12;', '&#189;', true, 'fractiononehalf'],
        ['&frac34;', '&#190;', true, 'fractionthreequarters'],
        // Math / logical.
        ['&fnof;', '&#402;', true, 'functionflorin'],
        ['&int;', '&#8747;', true, 'integral'],
        ['&sum;', '&#8721;', true, 'narysumation'],
        ['&infin;', '&#8734;', true, 'infinity'],
        ['&radic;', '&#8730;', true, 'squareroot'],
        ['&sim;', '&#8764;', false, 'similarto'],
        ['&cong;', '&#8773;', false, 'approximatelyequalto'],
        ['&asymp;', '&#8776;', true, 'almostequalto'],
        ['&ne;', '&#8800;', true, 'notequalto'],
        ['&equiv;', '&#8801;', true, 'identicalto'],
        ['&isin;', '&#8712;', false, 'elementof'],
        ['&notin;', '&#8713;', false, 'notanelementof'],
        ['&ni;', '&#8715;', false, 'containsasmember'],
        ['&prod;', '&#8719;', true, 'naryproduct'],
        ['&and;', '&#8743;', false, 'logicaland'],
        ['&or;', '&#8744;', false, 'logicalor'],
        ['&not;', '&#172;', true, 'notsign'],
        ['&cap;', '&#8745;', true, 'intersection'],
        ['&cup;', '&#8746;', false, 'union'],
        ['&part;', '&#8706;', true, 'partialdifferential'],
        ['&forall;', '&#8704;', false, 'forall'],
        ['&exist;', '&#8707;', false, 'thereexists'],
        ['&empty;', '&#8709;', false, 'diameter'],
        ['&nabla;', '&#8711;', false, 'backwarddifference'],
        ['&lowast;', '&#8727;', false, 'asteriskoperator'],
        ['&prop;', '&#8733;', false, 'proportionalto'],
        ['&ang;', '&#8736;', false, 'angle'],
        // Undefined.
        ['&acute;', '&#180;', true, 'acuteaccent'],
        ['&cedil;', '&#184;', true, 'cedilla'],
        ['&ordf;', '&#170;', true, 'feminineordinalindicator'],
        ['&ordm;', '&#186;', true, 'masculineordinalindicator'],
        ['&dagger;', '&#8224;', true, 'dagger'],
        ['&Dagger;', '&#8225;', true, 'doubledagger'],
        // Alphabetical special chars.
        ['&Agrave;', '&#192;', true, 'agrave_caps'],
        ['&Aacute;', '&#193;', true, 'aacute_caps'],
        ['&Acirc;', '&#194;', true, 'acircumflex_caps'],
        ['&Atilde;', '&#195;', true, 'atilde_caps'],
        ['&Auml;', '&#196;', true, 'adiaeresis_caps'],
        ['&Aring;', '&#197;', true, 'aringabove_caps'],
        ['&#256;', '&#256;', true, 'amacron_caps'],
        ['&AElig;', '&#198;', true, 'ligatureae_caps'],
        ['&Ccedil;', '&#199;', true, 'ccedilla_caps'],
        ['&Egrave;', '&#200;', true, 'egrave_caps'],
        ['&Eacute;', '&#201;', true, 'eacute_caps'],
        ['&Ecirc;', '&#202;', true, 'ecircumflex_caps'],
        ['&Euml;', '&#203;', true, 'ediaeresis_caps'],
        ['&#274;', '&#274;', true, 'emacron_caps'],
        ['&Igrave;', '&#204;', true, 'igrave_caps'],
        ['&Iacute;', '&#205;', true, 'iacute_caps'],
        ['&Icirc;', '&#206;', true, 'icircumflex_caps'],
        ['&Iuml;', '&#207;', true, 'idiaeresis_caps'],
        ['&#298;', '&#298;', true, 'imacron_caps'],
        ['&ETH;', '&#208;', true, 'eth_caps'],
        ['&Ntilde;', '&#209;', true, 'ntilde_caps'],
        ['&Ograve;', '&#210;', true, 'ograve_caps'],
        ['&Oacute;', '&#211;', true, 'oacute_caps'],
        ['&Ocirc;', '&#212;', true, 'ocircumflex_caps'],
        ['&Otilde;', '&#213;', true, 'otilde_caps'],
        ['&Ouml;', '&#214;', true, 'odiaeresis_caps'],
        ['&Oslash;', '&#216;', true, 'oslash_caps'],
        ['&#332;', '&#332;', true, 'omacron_caps'],
        ['&OElig;', '&#338;', true, 'ligatureoe_caps'],
        ['&Scaron;', '&#352;', true, 'scaron_caps'],
        ['&Ugrave;', '&#217;', true, 'ugrave_caps'],
        ['&Uacute;', '&#218;', true, 'uacute_caps'],
        ['&Ucirc;', '&#219;', true, 'ucircumflex_caps'],
        ['&Uuml;', '&#220;', true, 'udiaeresis_caps'],
        ['&#362;', '&#362;', true, 'umacron_caps'],
        ['&Yacute;', '&#221;', true, 'yacute_caps'],
        ['&Yuml;', '&#376;', true, 'ydiaeresis_caps'],
        ['&THORN;', '&#222;', true, 'thorn_caps'],
        ['&agrave;', '&#224;', true, 'agrave'],
        ['&aacute;', '&#225;', true, 'aacute'],
        ['&acirc;', '&#226;', true, 'acircumflex'],
        ['&atilde;', '&#227;', true, 'atilde'],
        ['&auml;', '&#228;', true, 'adiaeresis'],
        ['&aring;', '&#229;', true, 'aringabove'],
        ['&#257;', '&#257;', true, 'amacron'],
        ['&aelig;', '&#230;', true, 'ligatureae'],
        ['&ccedil;', '&#231;', true, 'ccedilla'],
        ['&egrave;', '&#232;', true, 'egrave'],
        ['&eacute;', '&#233;', true, 'eacute'],
        ['&ecirc;', '&#234;', true, 'ecircumflex'],
        ['&euml;', '&#235;', true, 'ediaeresis'],
        ['&#275;', '&#275;', true, 'emacron'],
        ['&igrave;', '&#236;', true, 'igrave'],
        ['&iacute;', '&#237;', true, 'iacute'],
        ['&icirc;', '&#238;', true, 'icircumflex'],
        ['&iuml;', '&#239;', true, 'idiaeresis'],
        ['&#299;', '&#299;', true, 'imacron'],
        ['&eth;', '&#240;', true, 'eth'],
        ['&ntilde;', '&#241;', true, 'ntilde'],
        ['&ograve;', '&#242;', true, 'ograve'],
        ['&oacute;', '&#243;', true, 'oacute'],
        ['&ocirc;', '&#244;', true, 'ocircumflex'],
        ['&otilde;', '&#245;', true, 'otilde'],
        ['&ouml;', '&#246;', true, 'odiaeresis'],
        ['&oslash;', '&#248;', true, 'oslash'],
        ['&#333;', '&#333;', true, 'omacron'],
        ['&oelig;', '&#339;', true, 'ligatureoe'],
        ['&scaron;', '&#353;', true, 'scaron'],
        ['&ugrave;', '&#249;', true, 'ugrave'],
        ['&uacute;', '&#250;', true, 'uacute'],
        ['&ucirc;', '&#251;', true, 'ucircumflex'],
        ['&uuml;', '&#252;', true, 'udiaeresis'],
        ['&#363;', '&#363;', true, 'umacron'],
        ['&yacute;', '&#253;', true, 'yacute'],
        ['&thorn;', '&#254;', true, 'thorn'],
        ['&yuml;', '&#255;', true, 'ydiaeresis'],
        ['&Alpha;', '&#913;', true, 'alpha_caps'],
        ['&Beta;', '&#914;', true, 'beta_caps'],
        ['&Gamma;', '&#915;', true, 'gamma_caps'],
        ['&Delta;', '&#916;', true, 'delta_caps'],
        ['&Epsilon;', '&#917;', true, 'epsilon_caps'],
        ['&Zeta;', '&#918;', true, 'zeta_caps'],
        ['&Eta;', '&#919;', true, 'eta_caps'],
        ['&Theta;', '&#920;', true, 'theta_caps'],
        ['&Iota;', '&#921;', true, 'iota_caps'],
        ['&Kappa;', '&#922;', true, 'kappa_caps'],
        ['&Lambda;', '&#923;', true, 'lambda_caps'],
        ['&Mu;', '&#924;', true, 'mu_caps'],
        ['&Nu;', '&#925;', true, 'nu_caps'],
        ['&Xi;', '&#926;', true, 'xi_caps'],
        ['&Omicron;', '&#927;', true, 'omicron_caps'],
        ['&Pi;', '&#928;', true, 'pi_caps'],
        ['&Rho;', '&#929;', true, 'rho_caps'],
        ['&Sigma;', '&#931;', true, 'sigma_caps'],
        ['&Tau;', '&#932;', true, 'tau_caps'],
        ['&Upsilon;', '&#933;', true, 'upsilon_caps'],
        ['&Phi;', '&#934;', true, 'phi_caps'],
        ['&Chi;', '&#935;', true, 'chi_caps'],
        ['&Psi;', '&#936;', true, 'psi_caps'],
        ['&Omega;', '&#937;', true, 'omega_caps'],
        ['&alpha;', '&#945;', true, 'alpha'],
        ['&beta;', '&#946;', true, 'beta'],
        ['&gamma;', '&#947;', true, 'gamma'],
        ['&delta;', '&#948;', true, 'delta'],
        ['&epsilon;', '&#949;', true, 'epsilon'],
        ['&zeta;', '&#950;', true, 'zeta'],
        ['&eta;', '&#951;', true, 'eta'],
        ['&theta;', '&#952;', true, 'theta'],
        ['&iota;', '&#953;', true, 'iota'],
        ['&kappa;', '&#954;', true, 'kappa'],
        ['&lambda;', '&#955;', true, 'lambda'],
        ['&mu;', '&#956;', true, 'mu'],
        ['&nu;', '&#957;', true, 'nu'],
        ['&xi;', '&#958;', true, 'xi'],
        ['&omicron;', '&#959;', true, 'omicron'],
        ['&pi;', '&#960;', true, 'pi'],
        ['&rho;', '&#961;', true, 'rho'],
        ['&sigmaf;', '&#962;', true, 'finalsigma'],
        ['&sigma;', '&#963;', true, 'sigma'],
        ['&tau;', '&#964;', true, 'tau'],
        ['&upsilon;', '&#965;', true, 'upsilon'],
        ['&phi;', '&#966;', true, 'phi'],
        ['&chi;', '&#967;', true, 'chi'],
        ['&psi;', '&#968;', true, 'psi'],
        ['&omega;', '&#969;', true, 'omega'],
        // Symbols.
        ['&alefsym;', '&#8501;', false, 'alefsymbol'],
        ['&piv;', '&#982;', false, 'pisymbol'],
        ['&real;', '&#8476;', false, 'realpartsymbol'],
        ['&thetasym;', '&#977;', false, 'thetasymbol'],
        ['&upsih;', '&#978;', false, 'upsilonhooksymbol'],
        ['&weierp;', '&#8472;', false, 'weierstrassp'],
        ['&image;', '&#8465;', false, 'imaginarypart'],
        // Arrows.
        ['&larr;', '&#8592;', true, 'leftwardsarrow'],
        ['&uarr;', '&#8593;', true, 'upwardsarrow'],
        ['&rarr;', '&#8594;', true, 'rightwardsarrow'],
        ['&darr;', '&#8595;', true, 'downwardsarrow'],
        ['&harr;', '&#8596;', true, 'leftrightarrow'],
        ['&crarr;', '&#8629;', false, 'carriagereturn'],
        ['&lArr;', '&#8656;', false, 'leftwardsdoublearrow'],
        ['&uArr;', '&#8657;', false, 'upwardsdoublearrow'],
        ['&rArr;', '&#8658;', false, 'rightwardsdoublearrow'],
        ['&dArr;', '&#8659;', false, 'downwardsdoublearrow'],
        ['&hArr;', '&#8660;', false, 'leftrightdoublearrow'],
        ['&there4;', '&#8756;', false, 'therefore'],
        ['&sub;', '&#8834;', false, 'subsetof'],
        ['&sup;', '&#8835;', false, 'supersetof'],
        ['&nsub;', '&#8836;', false, 'notasubsetof'],
        ['&sube;', '&#8838;', false, 'subsetoforequalto'],
        ['&supe;', '&#8839;', false, 'supersetoforequalto'],
        ['&oplus;', '&#8853;', false, 'circledplus'],
        ['&otimes;', '&#8855;', false, 'circledtimes'],
        ['&perp;', '&#8869;', false, 'perpendicular'],
        ['&sdot;', '&#8901;', false, 'dotoperator'],
        ['&lceil;', '&#8968;', false, 'leftceiling'],
        ['&rceil;', '&#8969;', false, 'rightceiling'],
        ['&lfloor;', '&#8970;', false, 'leftfloor'],
        ['&rfloor;', '&#8971;', false, 'rightfloor'],
        ['&lang;', '&#9001;', false, 'leftpointinganglebracket'],
        ['&rang;', '&#9002;', false, 'rightpointinganglebracket'],
        ['&loz;', '&#9674;', true, 'lozenge'],
        ['&spades;', '&#9824;', true, 'blackspadesuit'],
        ['&clubs;', '&#9827;', true, 'blackclubsuit'],
        ['&hearts;', '&#9829;', true, 'blackheartsuit'],
        ['&diams;', '&#9830;', true, 'blackdiamondsuit'],
        ['&ensp;', '&#8194;', false, 'enspace'],
        ['&emsp;', '&#8195;', false, 'emspace'],
        ['&thinsp;', '&#8201;', false, 'thinspace'],
        ['&zwnj;', '&#8204;', false, 'zerowidthnonjoiner'],
        ['&zwj;', '&#8205;', false, 'zerowidthjoiner'],
        ['&lrm;', '&#8206;', false, 'lefttorightmark'],
        ['&rlm;', '&#8207;', false, 'righttoleftmark'],
        ['&shy;', '&#173;', false, 'softhyphen']
    ];

/**
 * Atto text editor charmap plugin.
 *
 * @namespace M.atto_charmap
 * @class button
 * @extends M.editor_atto.EditorPlugin
 */

Y.namespace('M.atto_charmap').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
    /**
     * A reference to the current selection at the time that the dialogue
     * was opened.
     *
     * @property _currentSelection
     * @type Range
     * @private
     */
    _currentSelection: null,

    initializer: function() {
        this.addButton({
            icon: 'e/special_character',
            callback: this._displayDialogue
        });
    },

    /**
     * Display the Character Map selector.
     *
     * @method _displayDialogue
     * @private
     */
    _displayDialogue: function() {
        // Store the current selection.
        this._currentSelection = this.get('host').getSelection();
        if (this._currentSelection === false) {
            return;
        }

        var dialogue = this.getDialogue({
            headerContent: M.util.get_string('insertcharacter', COMPONENTNAME),
            focusAfterHide: true
        }, true);

        // Set the dialogue content, and then show the dialogue.
        dialogue.set('bodyContent', this._getDialogueContent())
                .show();
    },

    /**
     * Return the dialogue content for the tool.
     *
     * @method _getDialogueContent
     * @private
     * @return {Node} The content to place in the dialogue.
     */
    _getDialogueContent: function() {
        var template = Y.Handlebars.compile(
            '<div class="{{CSS.CHARMAP}}">' +
                '{{#each CHARMAP}}' +
                    '{{#if this.[2]}}' +
                    '<button class="btn btn-default btn-sm {{../../CSS.BUTTON}}" ' +
                        'aria-label="{{get_string this.[3] ../../component}}" ' +
                        'title="{{get_string this.[3] ../../component}}" ' +
                        'data-character="{{this.[0]}}" ' +
                    '>{{{this.[0]}}}</button>' +
                    '{{/if}}' +
                '{{/each}}' +
            '</div>'
        );

        var content = Y.Node.create(template({
            component: COMPONENTNAME,
            CSS: CSS,
            CHARMAP: CHARMAP
        }));

        content.delegate('click', this._insertChar, '.' + CSS.BUTTON, this);
        return content;
    },

    /**
     * Insert the picked character into the editor.
     *
     * @method _insertChar
     * @param {EventFacade} e
     * @private
     */
    _insertChar: function(e) {
        var character = e.target.getData('character');

        // Hide the dialogue.
        this.getDialogue({
            focusAfterHide: null
        }).hide();

        var host = this.get('host');

        // Focus on the last point.
        host.setSelection(this._currentSelection);

        // And add the character.
        host.insertContentAtFocusPoint(character);

        // And mark the text area as updated.
        this.markUpdated();
    }
});
