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