/* * Emoji plugin for summernote [https://github.com/summernote/summernote] * Canonical - https://github.com/JustinEldracher/summernote-plugins */ odoo.define('muk_web_utils.summernote_ext_specialchars', function (require) { 'use strict'; //template var tmpl = $.summernote.renderer.getTemplate(); // core functions: range, dom var range = $.summernote.core.range; var dom = $.summernote.core.dom; var KEY = { UP: 38, DOWN: 40, LEFT: 37, RIGHT: 39, ENTER: 13 }; var COLUMN_LENGTH = 15; var COLUMN_WIDTH = 35; var currentColumn, currentRow, totalColumn, totalRow = 0; // special characters data set var specialCharDataSet = [ "!", """, "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "×", "÷", "∀", "∂", "∃", "∅", "∇", "∈", "∉", "∋", "∏", "∑", "−", "∗", "√", "∝", "∞", "∠", "∧", "∨", "∩", "∪", "∫", "∴", "∼", "≅", "≈", "≠", "≡", "≤", "≥", "⊂", "⊃", "⊄", "⊆", "⊇", "⊕", "⊗", "⊥", "⋅", "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω", "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "ς", "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "ϑ", "ϒ", "ϖ", "Œ", "œ", "Š", "š", "Ÿ", "ƒ", "ˆ", "˜", " ", " ", " ", "‌", "‍", "‎", "‏", "–", "—", "‘", "’", "‚", "“", "”", "„", "†", "‡", "•", "…", "‰", "′", "″", "‹", "›", "‾", "€", "™", "←", "↑", "→", "↓", "↔", "↵", "⌈", "⌉", "⌊", "⌋", "◊", "♠", "♣", "♥", "♦", ]; /** * @member plugin.specialChar * @private * @param {jQuery} $editable * @return {String} */ var getTextOnRange = function ($editable) { $editable.focus(); var rng = range.create(); // if range on anchor, expand range with anchor if (rng.isOnAnchor()) { var anchor = dom.ancestor(rng.sc, dom.isAnchor); rng = range.createFromNode(anchor); } return rng.toString(); }; /** * Make Special Characters Table * * @member plugin.specialChar * @private * @return {jQuery} */ var makeSpecialCharSetTable = function () { var $table = $("
").attr("id", "specialCharTable"); $.each(specialCharDataSet, function (idx, text) { var $block = $("").attr("style", "border:1px solid black;display:inline-block;height:50px;width:35px;text-align:center;font-size:14pt;color:black;padding-top:10px;cursor:pointer;") .addClass("note-specialchar-node char-" + idx).attr("title", text).attr("id", "char-" + idx); $block.append(text); $table.append($block); }); return $table; }; /** * Show Special Characters and set event handlers on dialog controls. * * @member plugin.specialChar * @private * @param {jQuery} $dialog * @param {jQuery} $dialog * @param {Object} text * @return {Promise} */ var showSpecialCharDialog = function ($editable, $dialog, text) { return $.Deferred(function (deferred) { var $specialCharDialog = $dialog.find('.note-specialchar-dialog'); var $specialCharNode = $specialCharDialog.find('.note-specialchar-node'); var $selectedNode = null; var ARROW_KEYS = [KEY.UP, KEY.DOWN, KEY.LEFT, KEY.RIGHT]; var ENTER_KEY = KEY.ENTER; var pos = 0; var end = specialCharDataSet.length; function addActiveClass($target) { if (!$target) { return; } $target.find('span').addClass('active'); $selectedNode = $target; } function removeActiveClass($target) { $target.find('span').removeClass('active'); $selectedNode = null; } // find next node function findNextNode(row, column) { var findNode = null; $.each($specialCharNode, function (idx, $node) { var findRow = Math.ceil((idx + 1) / COLUMN_LENGTH); var findColumn = ((idx + 1) % COLUMN_LENGTH === 0) ? COLUMN_LENGTH : (idx + 1) % COLUMN_LENGTH; if (findRow === row && findColumn === column) { findNode = $node; return false; } }); return $(findNode); } function arrowKeyHandler(keyCode) { // left, right, up, down key var w = $("#specialCharTable").css("width") + ""; w = w.substr(0, w.length - 2); var cols = Math.floor(w / 35); pos = parseInt(pos); if (KEY.LEFT === keyCode) { if (pos > 0) { pos--; clear(); $(".char-" + pos).css("border", "1px solid blue").css("background-color", "aliceblue"); $selectedNode = $(".char-" + pos); } } else if (KEY.RIGHT === keyCode) { if (pos < end - 1) { pos++; clear(); $(".char-" + pos).css("border", "1px solid blue").css("background-color", "aliceblue"); $selectedNode = $(".char-" + pos); } } else if (KEY.UP === keyCode) { if (pos - cols >= 0) { clear(); pos = pos - cols; $(".char-" + pos).css("border", "1px solid blue").css("background-color", "aliceblue"); $selectedNode = $(".char-" + pos); } } else if (KEY.DOWN === keyCode) { if (pos + cols <= end) { clear(); pos = pos + cols; $(".char-" + pos).css("border", "1px solid blue").css("background-color", "aliceblue"); $selectedNode = $(".char-" + pos); } } } function enterKeyHandler() { if (!$selectedNode) { return; } pos = 0; deferred.resolve(decodeURIComponent($selectedNode.attr("title"))); $specialCharDialog.modal('hide'); } function keyDownEventHandler(event) { event.preventDefault(); var keyCode = event.keyCode; if (keyCode === undefined || keyCode === null) { return; } // check arrowKeys match if (ARROW_KEYS.indexOf(keyCode) > -1) { arrowKeyHandler(keyCode); } else if (keyCode === ENTER_KEY) { enterKeyHandler(); } return false; } // remove class removeActiveClass($specialCharNode); // find selected node if (text) { for (var i = 0; i < $specialCharNode.length; i++) { var $checkNode = $($specialCharNode[i]); if ($checkNode.text() === text) { addActiveClass($checkNode); currentRow = Math.ceil((i + 1) / COLUMN_LENGTH); currentColumn = (i + 1) % COLUMN_LENGTH; } } } $specialCharDialog.one('shown.bs.modal', function () { $(document).on('keydown', keyDownEventHandler); $specialCharNode.on('click', function (event) { event.preventDefault(); pos = 0; deferred.resolve(decodeURIComponent(event.currentTarget.title)); $specialCharDialog.modal('hide'); }); $specialCharNode.mouseenter(function() { clear(); $(this).css("border", "1px solid blue").css("background-color", "aliceblue"); $selectedNode = $(this); var thisid = $(this).attr("id") + ""; pos = thisid.substr(5); }); $specialCharNode.mouseleave(function() { clear(); }); }).one('hidden.bs.modal', function () { $specialCharNode.off('click'); $(document).off('keydown', keyDownEventHandler); if (deferred.state() === 'pending') { deferred.reject(); } }).modal('show'); // tooltip /*$dialog.find('span').tooltip({ container: $specialCharDialog.find('.form-group'), trigger: 'hover', placement: 'top' });*/ // $editable blur $editable.blur(); function clear() { $specialCharNode.css("border", "1px solid black").css("background-color", "white"); $selectedNode = null; } }); }; /** * @class plugin.specialChar * * Special Characters Plugin * * ### load script * * ``` * < script src="plugin/summernote-ext-specialchar.js"> * ``` * * ### use a plugin in toolbar * ``` * $("#editor").summernote({ * ... * toolbar : [ * ['group', [ 'specialChar' ]] * ] * ... * }); * ``` */ $.summernote.addPlugin({ /** @property {String} name name of plugin */ name: 'specialChar', /** * @property {Object} buttons * @property {function(object): string} buttons.specialChar */ buttons: { specialChar: function (lang, options) { return tmpl.iconButton(options.iconPrefix + 'circle-o ' + options.iconPrefix, { event: 'showSpecialCharDialog', title: lang.specialChar.specialChar, hide: true }); } }, /** * @property {Object} dialogs * @property {function(object, object): string} dialogs.specialChar */ dialogs: { specialChar: function (lang) { var body = '
' + makeSpecialCharSetTable()[0].outerHTML + '
'; return tmpl.dialog('note-specialchar-dialog', lang.specialChar.select, body); } }, /** * @property {Object} events * @property {Function} events.showSpecialCharDialog */ events: { showSpecialCharDialog: function (event, editor, layoutInfo) { var $dialog = layoutInfo.dialog(), $editable = layoutInfo.editable(), currentSpecialChar = getTextOnRange($editable); // save current range editor.saveRange($editable); showSpecialCharDialog($editable, $dialog, currentSpecialChar).then(function (selectChar) { // when ok button clicked // restore range editor.restoreRange($editable); // build node var $node = $('').html(selectChar)[0]; //var $node = $(selectChar)[0]; if ($node) { // insert character node editor.insertNode($editable, $node); } }).fail(function () { // when cancel button clicked editor.restoreRange($editable); }); } }, // define language langs: { 'en-US': { specialChar: { specialChar: 'Special Characters', select: 'Select Special characters' } }, 'ko-KR': { specialChar: { specialChar: '특수문자', select: '특수문자를 선택하세요' } } } }); });