e4dfab5446691b041eda1735c7a2adac654ce438
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Resources / Public / JavaScript / Plugins / CharacterMap.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * Character Map Plugin for TYPO3 htmlArea RTE
16 */
17 define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
18 'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
19 'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
20 'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
21 function (Plugin, UserAgent, Event, Util) {
22
23 var CharacterMap = function (editor, pluginName) {
24 this.constructor.super.call(this, editor, pluginName);
25 };
26 Util.inherit(CharacterMap, Plugin);
27 Util.apply(CharacterMap.prototype, {
28
29 /**
30 * This function gets called by the class constructor
31 */
32 configurePlugin: function(editor) {
33 /**
34 * Registering plugin "About" information
35 */
36 var pluginInformation = {
37 version : '4.0',
38 developer : 'Holger Hees, Bernhard Pfeifer, Stanislas Rolland',
39 developerUrl : 'http://www.sjbr.ca/',
40 copyrightOwner : 'Holger Hees, Bernhard Pfeifer, Stanislas Rolland',
41 sponsor : 'System Concept GmbH, Bernhard Pfeifer, SJBR, BLE',
42 sponsorUrl : 'http://www.sjbr.ca/',
43 license : 'GPL'
44 };
45 this.registerPluginInformation(pluginInformation);
46
47 /**
48 * Registering the buttons
49 */
50 for (var i = 0, n = this.buttons.length; i < n; ++i) {
51 var button = this.buttons[i];
52 buttonId = button[0];
53 var buttonConfiguration = {
54 id: buttonId,
55 tooltip: this.localize(buttonId + '-Tooltip'),
56 action: 'onButtonPress',
57 context: button[1],
58 dialog: false,
59 iconCls: 'htmlarea-action-' + button[2]
60 };
61 this.registerButton(buttonConfiguration);
62 }
63
64 /**
65 * Localizing the maps
66 */
67 for (var key in this.maps) {
68 var map = this.maps[key];
69 for (var i = map.length; --i >= 0;) {
70 this.maps[key][i].push(this.localize(map[i][1]));
71 }
72 }
73 return true;
74 },
75
76 /**
77 * The list of buttons added by this plugin
78 */
79 buttons: [
80 ['InsertCharacter', null, 'character-insert-from-map'],
81 ['InsertSoftHyphen', null, 'soft-hyphen-insert']
82 ],
83
84 /**
85 * Character maps
86 */
87 maps: {
88 general: [
89 ['&nbsp;', 'nbsp'],
90 ['&Agrave;', 'Agrave'],
91 ['&agrave;', 'agrave'],
92 ['&Aacute;', 'Aacute'],
93 ['&aacute;', 'aacute'],
94 ['&Acirc;', 'Acirc'],
95 ['&acirc;', 'acirc'],
96 ['&Atilde;', 'Atilde'],
97 ['&atilde;', 'atilde'],
98 ['&Auml;', 'Auml'],
99 ['&auml;', 'auml'],
100 ['&Aring;', 'Aring'],
101 ['&aring;', 'aring'],
102 ['&AElig;', 'AElig'],
103 ['&aelig;', 'aelig'],
104 ['&ordf;', 'ordf'],
105 ['&Ccedil;', 'Ccedil'],
106 ['&ccedil;', 'ccedil'],
107 ['&ETH;', 'ETH'],
108 ['&eth;', 'eth'],
109 ['&Egrave;', 'Egrave'],
110 ['&egrave;', 'egrave'],
111 ['&Eacute;', 'Eacute'],
112 ['&eacute;', 'eacute'],
113 ['&Ecirc;', 'Ecirc'],
114 ['&ecirc;', 'ecirc'],
115 ['&Euml;', 'Euml'],
116 ['&euml;', 'euml'],
117 ['&Igrave;', 'Igrave'],
118 ['&igrave;', 'igrave'],
119 ['&Iacute;', 'Iacute'],
120 ['&iacute;', 'iacute'],
121 ['&Icirc;', 'Icirc'],
122 ['&icirc;', 'icirc'],
123 ['&Iuml;', 'Iuml'],
124 ['&iuml;', 'iuml'],
125 ['&Ntilde;', 'Ntilde'],
126 ['&ntilde;', 'ntilde'],
127 ['&Ograve;', 'Ograve'],
128 ['&ograve;', 'ograve'],
129 ['&Oacute;', 'Oacute'],
130 ['&oacute;', 'oacute'],
131 ['&Ocirc;', 'Ocirc'],
132 ['&ocirc;', 'ocirc'],
133 ['&Otilde;', 'Otilde'],
134 ['&otilde;', 'otilde'],
135 ['&Ouml;', 'Ouml'],
136 ['&ouml;', 'ouml'],
137 ['&Oslash;', 'Oslash'],
138 ['&oslash;', 'oslash'],
139 ['&OElig;', 'OElig'],
140 ['&oelig;', 'oelig'],
141 ['&ordm;', 'ordm'],
142 ['&Scaron;', 'Scaron'],
143 ['&scaron;', 'scaron'],
144 ['&szlig;', 'szlig'],
145 ['&THORN;', 'THORN'],
146 ['&thorn;', 'thorn'],
147 ['&Ugrave;', 'Ugrave'],
148 ['&ugrave;', 'ugrave'],
149 ['&Uacute;', 'Uacute'],
150 ['&uacute;', 'uacute'],
151 ['&Ucirc;', 'Ucirc'],
152 ['&ucirc;', 'ucirc'],
153 ['&Uuml;', 'Uuml'],
154 ['&uuml;', 'uuml'],
155 ['&Yacute;', 'Yacute'],
156 ['&yacute;', 'yacute'],
157 ['&Yuml;', 'Yuml'],
158 ['&yuml;', 'yuml'],
159 ['&acute;', 'acute'],
160 ['&circ;', 'circ'],
161 ['&tilde;', 'tilde'],
162 ['&uml;', 'uml'],
163 ['&cedil;', 'cedil'],
164 ['&shy;', 'shy'],
165 ['&ndash;', 'ndash'],
166 ['&mdash;', 'mdash'],
167 ['&lsquo;', 'lsquo'],
168 ['&rsquo;', 'rsquo'],
169 ['&sbquo;', 'sbquo'],
170 ['&ldquo;', 'ldquo'],
171 ['&rdquo;', 'rdquo'],
172 ['&bdquo;', 'bdquo'],
173 ['&lsaquo;', 'lsaquo'],
174 ['&rsaquo;', 'rsaquo'],
175 ['&laquo;', 'laquo'],
176 ['&raquo;', 'raquo'],
177 ['&quot;', 'quot'],
178 ['&hellip;', 'hellip'],
179 ['&iquest;', 'iquest'],
180 ['&iexcl;', 'iexcl'],
181 ['&bull;', 'bull'],
182 ['&dagger;', 'dagger'],
183 ['&Dagger;', 'Dagger'],
184 ['&brvbar;', 'brvbar'],
185 ['&para;', 'para'],
186 ['&sect;', 'sect'],
187 ['&loz;', 'loz'],
188 ['&#064;', '#064'],
189 ['&copy;', 'copy'],
190 ['&reg;', 'reg'],
191 ['&trade;', 'trade'],
192 ['&curren;', 'curren'],
193 ['&cent;', 'cent'],
194 ['&euro;', 'euro'],
195 ['&pound;', 'pound'],
196 ['&yen;', 'yen'],
197 ['&emsp;', 'emsp'],
198 ['&ensp;', 'ensp'],
199 ['&thinsp;', 'thinsp'],
200 ['&zwj;', 'zwj'],
201 ['&zwnj;', 'zwnj']
202 ],
203 mathematical: [
204 ['&minus;', 'minus'],
205 ['&plusmn;', 'plusmn'],
206 ['&times;', 'times'],
207 ['&divide;', 'divide'],
208 ['&radic;', 'radic'],
209 ['&sdot;', 'sdot'],
210 ['&otimes;', 'otimes'],
211 ['&lowast;', 'lowast'],
212 ['&ge;', 'ge'],
213 ['&le;', 'le'],
214 ['&ne;', 'ne'],
215 ['&asymp;', 'asymp'],
216 ['&sim;', 'sim'],
217 ['&prop;', 'prop'],
218 ['&deg;', 'deg'],
219 ['&prime;', 'prime'],
220 ['&Prime;', 'Prime'],
221 ['&micro;', 'micro'],
222 ['&ang;', 'ang'],
223 ['&perp;', 'perp'],
224 ['&permil;', 'permil'],
225 ['&frasl;', 'frasl'],
226 ['&frac14;', 'frac14'],
227 ['&frac12;', 'frac12'],
228 ['&frac34;', 'frac34'],
229 ['&sup1;', 'sup1'],
230 ['&sup2;', 'sup2'],
231 ['&sup3;', 'sup3'],
232 ['&not;', 'not'],
233 ['&and;', 'and'],
234 ['&or;', 'or'],
235 ['&there4;', 'there4'],
236 ['&cong;', 'cong'],
237 ['&isin;', 'isin'],
238 ['&ni;', 'ni'],
239 ['&notin;', 'notin'],
240 ['&sub;', 'sub'],
241 ['&sube;', 'sube'],
242 ['&nsub;', 'nsub'],
243 ['&sup;', 'sup'],
244 ['&supe;', 'supe'],
245 ['&cap;', 'cap'],
246 ['&cup;', 'cup'],
247 ['&oplus;', 'oplus'],
248 ['&nabla;', 'nabla'],
249 ['&empty;', 'empty'],
250 ['&equiv;', 'equiv'],
251 ['&sum;', 'sum'],
252 ['&prod;', 'prod'],
253 ['&weierp;', 'weierp'],
254 ['&exist;', 'exist'],
255 ['&forall;', 'forall'],
256 ['&infin;', 'infin'],
257 ['&alefsym;', 'alefsym'],
258 ['&real;', 'real'],
259 ['&image;', 'image'],
260 ['&fnof;', 'fnof'],
261 ['&int;', 'int'],
262 ['&part;', 'part'],
263 ['&Alpha;', 'Alpha'],
264 ['&alpha;', 'alpha'],
265 ['&Beta;', 'Beta'],
266 ['&beta;', 'beta'],
267 ['&Gamma;', 'Gamma'],
268 ['&gamma;', 'gamma'],
269 ['&Delta;', 'Delta'],
270 ['&delta;', 'delta'],
271 ['&Epsilon;', 'Epsilon'],
272 ['&epsilon;', 'epsilon'],
273 ['&Zeta;', 'Zeta'],
274 ['&zeta;', 'zeta'],
275 ['&Eta;', 'Eta'],
276 ['&eta;', 'eta'],
277 ['&Theta;', 'Theta'],
278 ['&theta;', 'theta'],
279 ['&thetasym;', 'thetasym'],
280 ['&Iota;', 'Iota'],
281 ['&iota;', 'iota'],
282 ['&Kappa;', 'Kappa'],
283 ['&kappa;', 'kappa'],
284 ['&Lambda;', 'Lambda'],
285 ['&lambda;', 'lambda'],
286 ['&Mu;', 'Mu'],
287 ['&mu;', 'mu'],
288 ['&Nu;', 'Nu'],
289 ['&nu;', 'nu'],
290 ['&Xi;', 'Xi'],
291 ['&xi;', 'xi'],
292 ['&Omicron;', 'Omicron'],
293 ['&omicron;', 'omicron'],
294 ['&Pi;', 'Pi'],
295 ['&pi;', 'pi'],
296 ['&piv;', 'piv'],
297 ['&Rho;', 'Rho'],
298 ['&rho;', 'rho'],
299 ['&Sigma;', 'Sigma'],
300 ['&sigma;', 'sigma'],
301 ['&sigmaf;', 'sigmaf'],
302 ['&Tau;', 'Tau'],
303 ['&tau;', 'tau'],
304 ['&Upsilon;', 'Upsilon'],
305 ['&upsih;', 'upsih'],
306 ['&upsilon;', 'upsilon'],
307 ['&Phi;', 'Phi'],
308 ['&phi;', 'phi'],
309 ['&Chi;', 'Chi'],
310 ['&chi;', 'chi'],
311 ['&Psi;', 'Psi'],
312 ['&psi;', 'psi'],
313 ['&Omega;', 'Omega'],
314 ['&omega;', 'omega']
315 ],
316 graphical: [
317 ['&crarr;', 'crarr'],
318 ['&uarr;', 'uarr'],
319 ['&darr;', 'darr'],
320 ['&larr;', 'larr'],
321 ['&rarr;', 'rarr'],
322 ['&harr;', 'harr'],
323 ['&uArr;', 'uArr'],
324 ['&dArr;', 'dArr'],
325 ['&lArr;', 'lArr'],
326 ['&rArr;', 'rArr'],
327 ['&hArr;', 'hArr'],
328 ['&nbsp;', 'nbsp'],
329 ['&nbsp;', 'nbsp'],
330 ['&nbsp;', 'nbsp'],
331 ['&nbsp;', 'nbsp'],
332 ['&clubs;', 'clubs'],
333 ['&diams;', 'diams'],
334 ['&hearts;', 'hearts'],
335 ['&spades;', 'spades']
336 ]
337 },
338 /*
339 * This function gets called when the button was pressed.
340 *
341 * @param object editor: the editor instance
342 * @param string id: the button id or the key
343 *
344 * @return boolean false if action is completed
345 */
346 onButtonPress: function (editor, id) {
347 // Could be a button or its hotkey
348 var buttonId = this.translateHotKey(id);
349 buttonId = buttonId ? buttonId : id;
350 switch (buttonId) {
351 case 'InsertCharacter':
352 this.openDialogue(
353 buttonId,
354 'Insert special character',
355 this.getWindowDimensions(
356 {
357 width: 434,
358 height: 360
359 },
360 buttonId
361 ),
362 this.buildTabItems()
363 );
364 break;
365 case 'InsertSoftHyphen':
366 this.insertEntity('\xAD');
367 break;
368 }
369 return false;
370 },
371 /*
372 * Open the dialogue window
373 *
374 * @param string buttonId: the button id
375 * @param string title: the window title
376 * @param integer dimensions: the opening width of the window
377 * @param object tabItems: the configuration of the tabbed panel
378 * @param function handler: handler when the OK button if clicked
379 *
380 * @return void
381 */
382 openDialogue: function (buttonId, title, dimensions, tabItems, handler) {
383 this.dialog = new Ext.Window({
384 title: this.localize(title),
385 cls: 'htmlarea-window',
386 border: false,
387 width: dimensions.width,
388 height: 'auto',
389 iconCls: this.getButton(buttonId).iconCls,
390 listeners: {
391 close: {
392 fn: this.onClose,
393 scope: this
394 }
395 },
396 items: {
397 xtype: 'tabpanel',
398 activeTab: 0,
399 listeners: {
400 activate: {
401 fn: this.resetFocus,
402 scope: this
403 },
404 tabchange: {
405 fn: this.syncHeight,
406 scope: this
407 }
408 },
409 items: tabItems
410 },
411 buttons: [
412 this.buildButtonConfig('Cancel', this.onCancel)
413 ]
414 });
415 this.show();
416 },
417 /*
418 * Build the configuration of the the tab items
419 *
420 * @return array the configuration array of tab items
421 */
422 buildTabItems: function () {
423 var tabItems = [];
424 for (var id in this.maps) {
425 tabItems.push({
426 xtype: 'box',
427 cls: 'character-map',
428 title: this.localize(id),
429 itemId: id,
430 tpl: new Ext.XTemplate(
431 '<tpl for="."><a href="#" class="character" hidefocus="on" ext:qtitle="<span>&</span>{1};" ext:qtip="{2}">{0}</a></tpl>'
432 ),
433 listeners: {
434 render: {
435 fn: this.renderMap,
436 scope: this
437 }
438 }
439 });
440 }
441 return tabItems;
442 },
443
444 /**
445 * Render an array of characters
446 *
447 * @param object component: the box containing the characters
448 * @return void
449 */
450 renderMap: function (component) {
451 component.tpl.overwrite(component.el, this.maps[component.itemId]);
452 var self = this;
453 Event.on(component.el.dom, 'click', function (event) { return self.insertCharacter(event); }, {delegate: 'a'});
454 },
455
456 /**
457 * Handle the click on an item of the map
458 *
459 * @param object event: the jQuery event
460 * @return boolean
461 */
462 insertCharacter: function (event) {
463 Event.stopEvent(event);
464 this.restoreSelection();
465 var entity = event.target.innerHTML;
466 this.insertEntity(entity);
467 this.saveSelection();
468 return false;
469 },
470
471 /**
472 * Insert the selected entity
473 *
474 * @param string entity: the entity to insert at the current selection
475 *
476 * @return void
477 */
478 insertEntity: function (entity) {
479 // Firefox, WebKit and IE convert '&nbsp;' to '&amp;nbsp;'
480 var node = this.editor.document.createTextNode(((UserAgent.isGecko || UserAgent.isWebKit || UserAgent.isIE) && entity == '&nbsp;') ? '\xA0' : entity);
481 this.editor.getSelection().insertNode(node);
482 this.editor.getSelection().selectNode(node, false);
483 },
484
485 /**
486 * Reset focus on the the current selection, if at all possible
487 *
488 */
489 resetFocus: function () {
490 this.restoreSelection();
491 },
492
493 /**
494 * Remove listeners before closing the window
495 */
496 removeListeners: function () {
497 var components = this.dialog.findByType('box');
498 for (var i = components.length; --i > 0;) {
499 if (components[i].el) {
500 Event.off(components[i].el.dom);
501 }
502 }
503 }
504 });
505
506 return CharacterMap;
507
508 });