4 * Released under LGPL License.
5 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
7 * License: http://www.tinymce.com/license
8 * Contributing: http://www.tinymce.com/contributing
11 /*global tinymce:true */
13 tinymce.PluginManager.add('charmap', function(editor) {
14 var isArray = tinymce.util.Tools.isArray;
16 function getDefaultCharMap() {
18 ['160', 'no-break space'],
19 ['173', 'soft hyphen'],
20 ['34', 'quotation mark'],
23 ['8364', 'euro sign'],
24 ['163', 'pound sign'],
27 ['169', 'copyright sign'],
28 ['174', 'registered sign'],
29 ['8482', 'trade mark sign'],
30 ['8240', 'per mille sign'],
31 ['181', 'micro sign'],
32 ['183', 'middle dot'],
34 ['8230', 'three dot leader'],
35 ['8242', 'minutes / feet'],
36 ['8243', 'seconds / inches'],
37 ['167', 'section sign'],
38 ['182', 'paragraph sign'],
39 ['223', 'sharp s / ess-zed'],
41 ['8249', 'single left-pointing angle quotation mark'],
42 ['8250', 'single right-pointing angle quotation mark'],
43 ['171', 'left pointing guillemet'],
44 ['187', 'right pointing guillemet'],
45 ['8216', 'left single quotation mark'],
46 ['8217', 'right single quotation mark'],
47 ['8220', 'left double quotation mark'],
48 ['8221', 'right double quotation mark'],
49 ['8218', 'single low-9 quotation mark'],
50 ['8222', 'double low-9 quotation mark'],
51 ['60', 'less-than sign'],
52 ['62', 'greater-than sign'],
53 ['8804', 'less-than or equal to'],
54 ['8805', 'greater-than or equal to'],
59 ['164', 'currency sign'],
60 ['166', 'broken bar'],
62 ['161', 'inverted exclamation mark'],
63 ['191', 'turned question mark'],
64 ['710', 'circumflex accent'],
65 ['732', 'small tilde'],
66 ['176', 'degree sign'],
67 ['8722', 'minus sign'],
68 ['177', 'plus-minus sign'],
69 ['247', 'division sign'],
70 ['8260', 'fraction slash'],
71 ['215', 'multiplication sign'],
72 ['185', 'superscript one'],
73 ['178', 'superscript two'],
74 ['179', 'superscript three'],
75 ['188', 'fraction one quarter'],
76 ['189', 'fraction one half'],
77 ['190', 'fraction three quarters'],
79 ['402', 'function / florin'],
81 ['8721', 'n-ary sumation'],
83 ['8730', 'square root'],
84 ['8764', 'similar to'],
85 ['8773', 'approximately equal to'],
86 ['8776', 'almost equal to'],
87 ['8800', 'not equal to'],
88 ['8801', 'identical to'],
89 ['8712', 'element of'],
90 ['8713', 'not an element of'],
91 ['8715', 'contains as member'],
92 ['8719', 'n-ary product'],
93 ['8743', 'logical and'],
94 ['8744', 'logical or'],
96 ['8745', 'intersection'],
98 ['8706', 'partial differential'],
100 ['8707', 'there exists'],
101 ['8709', 'diameter'],
102 ['8711', 'backward difference'],
103 ['8727', 'asterisk operator'],
104 ['8733', 'proportional to'],
107 ['180', 'acute accent'],
109 ['170', 'feminine ordinal indicator'],
110 ['186', 'masculine ordinal indicator'],
112 ['8225', 'double dagger'],
113 // alphabetical special chars
114 ['192', 'A - grave'],
115 ['193', 'A - acute'],
116 ['194', 'A - circumflex'],
117 ['195', 'A - tilde'],
118 ['196', 'A - diaeresis'],
119 ['197', 'A - ring above'],
120 ['198', 'ligature AE'],
121 ['199', 'C - cedilla'],
122 ['200', 'E - grave'],
123 ['201', 'E - acute'],
124 ['202', 'E - circumflex'],
125 ['203', 'E - diaeresis'],
126 ['204', 'I - grave'],
127 ['205', 'I - acute'],
128 ['206', 'I - circumflex'],
129 ['207', 'I - diaeresis'],
131 ['209', 'N - tilde'],
132 ['210', 'O - grave'],
133 ['211', 'O - acute'],
134 ['212', 'O - circumflex'],
135 ['213', 'O - tilde'],
136 ['214', 'O - diaeresis'],
137 ['216', 'O - slash'],
138 ['338', 'ligature OE'],
139 ['352', 'S - caron'],
140 ['217', 'U - grave'],
141 ['218', 'U - acute'],
142 ['219', 'U - circumflex'],
143 ['220', 'U - diaeresis'],
144 ['221', 'Y - acute'],
145 ['376', 'Y - diaeresis'],
147 ['224', 'a - grave'],
148 ['225', 'a - acute'],
149 ['226', 'a - circumflex'],
150 ['227', 'a - tilde'],
151 ['228', 'a - diaeresis'],
152 ['229', 'a - ring above'],
153 ['230', 'ligature ae'],
154 ['231', 'c - cedilla'],
155 ['232', 'e - grave'],
156 ['233', 'e - acute'],
157 ['234', 'e - circumflex'],
158 ['235', 'e - diaeresis'],
159 ['236', 'i - grave'],
160 ['237', 'i - acute'],
161 ['238', 'i - circumflex'],
162 ['239', 'i - diaeresis'],
164 ['241', 'n - tilde'],
165 ['242', 'o - grave'],
166 ['243', 'o - acute'],
167 ['244', 'o - circumflex'],
168 ['245', 'o - tilde'],
169 ['246', 'o - diaeresis'],
171 ['339', 'ligature oe'],
172 ['353', 's - caron'],
173 ['249', 'u - grave'],
174 ['250', 'u - acute'],
175 ['251', 'u - circumflex'],
176 ['252', 'u - diaeresis'],
177 ['253', 'y - acute'],
179 ['255', 'y - diaeresis'],
221 ['962', 'final sigma'],
230 ['8501', 'alef symbol'],
231 ['982', 'pi symbol'],
232 ['8476', 'real part symbol'],
233 ['978', 'upsilon - hook symbol'],
234 ['8472', 'Weierstrass p'],
235 ['8465', 'imaginary part'],
237 ['8592', 'leftwards arrow'],
238 ['8593', 'upwards arrow'],
239 ['8594', 'rightwards arrow'],
240 ['8595', 'downwards arrow'],
241 ['8596', 'left right arrow'],
242 ['8629', 'carriage return'],
243 ['8656', 'leftwards double arrow'],
244 ['8657', 'upwards double arrow'],
245 ['8658', 'rightwards double arrow'],
246 ['8659', 'downwards double arrow'],
247 ['8660', 'left right double arrow'],
248 ['8756', 'therefore'],
249 ['8834', 'subset of'],
250 ['8835', 'superset of'],
251 ['8836', 'not a subset of'],
252 ['8838', 'subset of or equal to'],
253 ['8839', 'superset of or equal to'],
254 ['8853', 'circled plus'],
255 ['8855', 'circled times'],
256 ['8869', 'perpendicular'],
257 ['8901', 'dot operator'],
258 ['8968', 'left ceiling'],
259 ['8969', 'right ceiling'],
260 ['8970', 'left floor'],
261 ['8971', 'right floor'],
262 ['9001', 'left-pointing angle bracket'],
263 ['9002', 'right-pointing angle bracket'],
265 ['9824', 'black spade suit'],
266 ['9827', 'black club suit'],
267 ['9829', 'black heart suit'],
268 ['9830', 'black diamond suit'],
269 ['8194', 'en space'],
270 ['8195', 'em space'],
271 ['8201', 'thin space'],
272 ['8204', 'zero width non-joiner'],
273 ['8205', 'zero width joiner'],
274 ['8206', 'left-to-right mark'],
275 ['8207', 'right-to-left mark']
279 function charmapFilter(charmap) {
280 return tinymce.util.Tools.grep(charmap, function(item) {
281 return isArray(item) && item.length == 2;
285 function getCharsFromSetting(settingValue) {
286 if (isArray(settingValue)) {
287 return [].concat(charmapFilter(settingValue));
290 if (typeof settingValue == "function") {
291 return settingValue();
297 function extendCharMap(charmap) {
298 var settings = editor.settings;
300 if (settings.charmap) {
301 charmap = getCharsFromSetting(settings.charmap);
304 if (settings.charmap_append) {
305 return [].concat(charmap).concat(getCharsFromSetting(settings.charmap_append));
311 function getCharMap() {
312 return extendCharMap(getDefaultCharMap());
315 function insertChar(chr) {
316 editor.fire('insertCustomChar', {chr: chr}).chr;
317 editor.execCommand('mceInsertContent', false, chr);
320 function showDialog() {
321 var gridHtml, x, y, win;
323 function getParentTd(elm) {
325 if (elm.nodeName == 'TD') {
329 elm = elm.parentNode;
333 gridHtml = '<table role="presentation" cellspacing="0" class="mce-charmap"><tbody>';
335 var charmap = getCharMap();
336 var width = Math.min(charmap.length, 25);
337 var height = Math.ceil(charmap.length / width);
338 for (y = 0; y < height; y++) {
341 for (x = 0; x < width; x++) {
342 var index = y * width + x;
343 if (index < charmap.length) {
344 var chr = charmap[index];
346 gridHtml += '<td title="' + chr[1] + '"><div tabindex="-1" title="' + chr[1] + '" role="button">' +
347 (chr ? String.fromCharCode(parseInt(chr[0], 10)) : ' ') + '</div></td>';
349 gridHtml += '<td />';
356 gridHtml += '</tbody></table>';
361 onclick: function(e) {
362 var target = e.target;
364 if (/^(TD|DIV)$/.test(target.nodeName)) {
365 if (getParentTd(target).firstChild) {
366 insertChar(tinymce.trim(target.innerText || target.textContent));
374 onmouseover: function(e) {
375 var td = getParentTd(e.target);
377 if (td && td.firstChild) {
378 win.find('#preview').text(td.firstChild.firstChild.data);
379 win.find('#previewTitle').text(td.title);
381 win.find('#preview').text(' ');
382 win.find('#previewTitle').text(' ');
387 win = editor.windowManager.open({
388 title: "Special character",
406 style: 'font-size: 40px; text-align: center',
413 name: 'previewTitle',
415 style: 'text-align: center',
424 {text: "Close", onclick: function() {
431 editor.addCommand('mceShowCharmap', showDialog);
433 editor.addButton('charmap', {
435 tooltip: 'Special character',
436 cmd: 'mceShowCharmap'
439 editor.addMenuItem('charmap', {
441 text: 'Special character',
442 cmd: 'mceShowCharmap',
447 getCharMap: getCharMap,
448 insertChar: insertChar