]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/Editor.js
Release 6.2.3
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / Editor.js
1 /**
2  * Editor.js
3  *
4  * Copyright 2009, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         // Shorten these names
13         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,
14                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,
15                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,
16                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
17                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
18
19         /**
20          * This class contains the core logic for a TinyMCE editor.
21          *
22          * @class tinymce.Editor
23          * @example
24          * // Add a class to all paragraphs in the editor.
25          * tinyMCE.activeEditor.dom.addClass(tinyMCE.activeEditor.dom.select('p'), 'someclass');
26          * 
27          * // Gets the current editors selection as text
28          * tinyMCE.activeEditor.selection.getContent({format : 'text'});
29          * 
30          * // Creates a new editor instance
31          * var ed = new tinymce.Editor('textareaid', {
32          *     some_setting : 1
33          * });
34          * 
35          * // Select each item the user clicks on
36          * ed.onClick.add(function(ed, e) {
37          *     ed.selection.select(e.target);
38          * });
39          * 
40          * ed.render();
41          */
42         tinymce.create('tinymce.Editor', {
43                 /**
44                  * Constructs a editor instance by id.
45                  *
46                  * @constructor
47                  * @method Editor
48                  * @param {String} id Unique id for the editor.
49                  * @param {Object} s Optional settings string for the editor.
50                  * @author Moxiecode
51                  */
52                 Editor : function(id, s) {
53                         var t = this;
54
55                         /**
56                          * Editor instance id, normally the same as the div/textarea that was replaced. 
57                          *
58                          * @property id
59                          * @type String
60                          */
61                         t.id = t.editorId = id;
62
63                         t.execCommands = {};
64                         t.queryStateCommands = {};
65                         t.queryValueCommands = {};
66
67                         /**
68                          * State to force the editor to return false on a isDirty call. 
69                          *
70                          * @property isNotDirty
71                          * @type Boolean
72                          * @example
73                          * function ajaxSave() {
74                          *     var ed = tinyMCE.get('elm1');
75                          *
76                          *     // Save contents using some XHR call
77                          *     alert(ed.getContent());
78                          *
79                          *     ed.isNotDirty = 1; // Force not dirty state
80                          * }
81                          */
82                         t.isNotDirty = false;
83
84                         /**
85                          * Name/Value object containting plugin instances.
86                          *
87                          * @property plugins
88                          * @type Object
89                          * @example
90                          * // Execute a method inside a plugin directly
91                          * tinyMCE.activeEditor.plugins.someplugin.someMethod();
92                          */
93                         t.plugins = {};
94
95                         // Add events to the editor
96                         each([
97                                 /**
98                                  * Fires before the initialization of the editor.
99                                  *
100                                  * @event onPreInit
101                                  * @param {tinymce.Editor} sender Editor instance.
102                                  * @see #onInit
103                                  * @example
104                                  * // Adds an observer to the onPreInit event using tinyMCE.init
105                                  * tinyMCE.init({
106                                  *    ...
107                                  *    setup : function(ed) {
108                                  *       ed.onPreInit.add(function(ed) {
109                                  *           console.debug('PreInit: ' + ed.id);
110                                  *       });
111                                  *    }
112                                  * });
113                                  */
114                                 'onPreInit',
115
116                                 /**
117                                  * Fires before the initialization of the editor.
118                                  *
119                                  * @event onBeforeRenderUI
120                                  * @param {tinymce.Editor} sender Editor instance.
121                                  * @example
122                                  * // Adds an observer to the onBeforeRenderUI event using tinyMCE.init
123                                  * tinyMCE.init({
124                                  *    ...
125                                  *    setup : function(ed) {
126                                  *      ed.onBeforeRenderUI.add(function(ed, cm) {
127                                  *          console.debug('Before render: ' + ed.id);
128                                  *      });
129                                  *    }
130                                  * });
131                                  */
132                                 'onBeforeRenderUI',
133
134                                 /**
135                                  * Fires after the rendering has completed.
136                                  *
137                                  * @event onPostRender
138                                  * @param {tinymce.Editor} sender Editor instance.
139                                  * @example
140                                  * // Adds an observer to the onPostRender event using tinyMCE.init
141                                  * tinyMCE.init({
142                                  *    ...
143                                  *    setup : function(ed) {
144                                  *       ed.onPostRender.add(function(ed, cm) {
145                                  *           console.debug('After render: ' + ed.id);
146                                  *       });
147                                  *    }
148                                  * });
149                                  */
150                                 'onPostRender',
151
152                                 /**
153                                  * Fires after the initialization of the editor is done.
154                                  *
155                                  * @event onInit
156                                  * @param {tinymce.Editor} sender Editor instance.
157                                  * @see #onPreInit
158                                  * @example
159                                  * // Adds an observer to the onInit event using tinyMCE.init
160                                  * tinyMCE.init({
161                                  *    ...
162                                  *    setup : function(ed) {
163                                  *       ed.onInit.add(function(ed) {
164                                  *           console.debug('Editor is done: ' + ed.id);
165                                  *       });
166                                  *    }
167                                  * });
168                                  */
169                                 'onInit',
170
171                                 /**
172                                  * Fires when the editor instance is removed from page.
173                                  *
174                                  * @event onRemove
175                                  * @param {tinymce.Editor} sender Editor instance.
176                                  * @example
177                                  * // Adds an observer to the onRemove event using tinyMCE.init
178                                  * tinyMCE.init({
179                                  *    ...
180                                  *    setup : function(ed) {
181                                  *       ed.onRemove.add(function(ed) {
182                                  *           console.debug('Editor was removed: ' + ed.id);
183                                  *       });
184                                  *    }
185                                  * });
186                                  */
187                                 'onRemove',
188
189                                 /**
190                                  * Fires when the editor is activated.
191                                  *
192                                  * @event onActivate
193                                  * @param {tinymce.Editor} sender Editor instance.
194                                  * @example
195                                  * // Adds an observer to the onActivate event using tinyMCE.init
196                                  * tinyMCE.init({
197                                  *    ...
198                                  *    setup : function(ed) {
199                                  *       ed.onActivate.add(function(ed) {
200                                  *           console.debug('Editor was activated: ' + ed.id);
201                                  *       });
202                                  *    }
203                                  * });
204                                  */
205                                 'onActivate',
206
207                                 /**
208                                  * Fires when the editor is deactivated.
209                                  *
210                                  * @event onDeactivate
211                                  * @param {tinymce.Editor} sender Editor instance.
212                                  * @example
213                                  * // Adds an observer to the onDeactivate event using tinyMCE.init
214                                  * tinyMCE.init({
215                                  *    ...
216                                  *    setup : function(ed) {
217                                  *       ed.onDeactivate.add(function(ed) {
218                                  *           console.debug('Editor was deactivated: ' + ed.id);
219                                  *       });
220                                  *    }
221                                  * });
222                                  */
223                                 'onDeactivate',
224
225                                 /**
226                                  * Fires when something in the body of the editor is clicked.
227                                  *
228                                  * @event onClick
229                                  * @param {tinymce.Editor} sender Editor instance.
230                                  * @param {Event} evt W3C DOM Event instance.
231                                  * @example
232                                  * // Adds an observer to the onClick event using tinyMCE.init
233                                  * tinyMCE.init({
234                                  *    ...
235                                  *    setup : function(ed) {
236                                  *       ed.onClick.add(function(ed, e) {
237                                  *           console.debug('Editor was clicked: ' + e.target.nodeName);
238                                  *       });
239                                  *    }
240                                  * });
241                                  */
242                                 'onClick',
243
244                                 /**
245                                  * Fires when a registered event is intercepted.
246                                  *
247                                  * @event onEvent
248                                  * @param {tinymce.Editor} sender Editor instance.
249                                  * @param {Event} evt W3C DOM Event instance.
250                                  * @example
251                                  * // Adds an observer to the onEvent event using tinyMCE.init
252                                  * tinyMCE.init({
253                                  *    ...
254                                  *    setup : function(ed) {
255                                  *       ed.onEvent.add(function(ed, e) {
256                                  *          console.debug('Editor event occured: ' + e.target.nodeName);
257                                  *       });
258                                  *    }
259                                  * });
260                                  */
261                                 'onEvent',
262
263                                 /**
264                                  * Fires when a mouseup event is intercepted inside the editor.
265                                  *
266                                  * @event onMouseUp
267                                  * @param {tinymce.Editor} sender Editor instance.
268                                  * @param {Event} evt W3C DOM Event instance.
269                                  * @example
270                                  * // Adds an observer to the onMouseUp event using tinyMCE.init
271                                  * tinyMCE.init({
272                                  *    ...
273                                  *    setup : function(ed) {
274                                  *       ed.onMouseUp.add(function(ed, e) {
275                                  *           console.debug('Mouse up event: ' + e.target.nodeName);
276                                  *       });
277                                  *    }
278                                  * });
279                                  */
280                                 'onMouseUp',
281
282                                 /**
283                                  * Fires when a mousedown event is intercepted inside the editor.
284                                  *
285                                  * @event onMouseDown
286                                  * @param {tinymce.Editor} sender Editor instance.
287                                  * @param {Event} evt W3C DOM Event instance.
288                                  * @example
289                                  * // Adds an observer to the onMouseDown event using tinyMCE.init
290                                  * tinyMCE.init({
291                                  *    ...
292                                  *    setup : function(ed) {
293                                  *       ed.onMouseDown.add(function(ed, e) {
294                                  *           console.debug('Mouse down event: ' + e.target.nodeName);
295                                  *       });
296                                  *    }
297                                  * });
298                                  */
299                                 'onMouseDown',
300
301                                 /**
302                                  * Fires when a dblclick event is intercepted inside the editor.
303                                  *
304                                  * @event onDblClick
305                                  * @param {tinymce.Editor} sender Editor instance.
306                                  * @param {Event} evt W3C DOM Event instance.
307                                  * @example
308                                  * // Adds an observer to the onDblClick event using tinyMCE.init
309                                  * tinyMCE.init({
310                                  *    ...
311                                  *    setup : function(ed) {
312                                  *       ed.onDblClick.add(function(ed, e) {
313                                  *          console.debug('Double click event: ' + e.target.nodeName);
314                                  *       });
315                                  *    }
316                                  * });
317                                  */
318                                 'onDblClick',
319
320                                 /**
321                                  * Fires when a keydown event is intercepted inside the editor.
322                                  *
323                                  * @event onKeyDown
324                                  * @param {tinymce.Editor} sender Editor instance.
325                                  * @param {Event} evt W3C DOM Event instance.
326                                  * @example
327                                  * // Adds an observer to the onKeyDown event using tinyMCE.init
328                                  * tinyMCE.init({
329                                  *    ...
330                                  *    setup : function(ed) {
331                                  *       ed.onKeyDown.add(function(ed, e) {
332                                  *           console.debug('Key down event: ' + e.keyCode);
333                                  *       });
334                                  *    }
335                                  * });
336                                  */
337                                 'onKeyDown',
338
339                                 /**
340                                  * Fires when a keydown event is intercepted inside the editor.
341                                  *
342                                  * @event onKeyUp
343                                  * @param {tinymce.Editor} sender Editor instance.
344                                  * @param {Event} evt W3C DOM Event instance.
345                                  * @example
346                                  * // Adds an observer to the onKeyUp event using tinyMCE.init
347                                  * tinyMCE.init({
348                                  *    ...
349                                  *    setup : function(ed) {
350                                  *       ed.onKeyUp.add(function(ed, e) {
351                                  *           console.debug('Key up event: ' + e.keyCode);
352                                  *       });
353                                  *    }
354                                  * });
355                                  */
356                                 'onKeyUp',
357
358                                 /**
359                                  * Fires when a keypress event is intercepted inside the editor.
360                                  *
361                                  * @event onKeyPress
362                                  * @param {tinymce.Editor} sender Editor instance.
363                                  * @param {Event} evt W3C DOM Event instance.
364                                  * @example
365                                  * // Adds an observer to the onKeyPress event using tinyMCE.init
366                                  * tinyMCE.init({
367                                  *    ...
368                                  *    setup : function(ed) {
369                                  *       ed.onKeyPress.add(function(ed, e) {
370                                  *           console.debug('Key press event: ' + e.keyCode);
371                                  *       });
372                                  *    }
373                                  * });
374                                  */
375                                 'onKeyPress',
376
377                                 /**
378                                  * Fires when a contextmenu event is intercepted inside the editor.
379                                  *
380                                  * @event onContextMenu
381                                  * @param {tinymce.Editor} sender Editor instance.
382                                  * @param {Event} evt W3C DOM Event instance.
383                                  * @example
384                                  * // Adds an observer to the onContextMenu event using tinyMCE.init
385                                  * tinyMCE.init({
386                                  *    ...
387                                  *    setup : function(ed) {
388                                  *       ed.onContextMenu.add(function(ed, e) {
389                                  *            console.debug('Context menu event:' + e.target);
390                                  *       });
391                                  *    }
392                                  * });
393                                  */
394                                 'onContextMenu',
395
396                                 /**
397                                  * Fires when a form submit event is intercepted.
398                                  *
399                                  * @event onSubmit
400                                  * @param {tinymce.Editor} sender Editor instance.
401                                  * @param {Event} evt W3C DOM Event instance.
402                                  * @example
403                                  * // Adds an observer to the onSubmit event using tinyMCE.init
404                                  * tinyMCE.init({
405                                  *    ...
406                                  *    setup : function(ed) {
407                                  *       ed.onSubmit.add(function(ed, e) {
408                                  *            console.debug('Form submit:' + e.target);
409                                  *       });
410                                  *    }
411                                  * });
412                                  */
413                                 'onSubmit',
414
415                                 /**
416                                  * Fires when a form reset event is intercepted.
417                                  *
418                                  * @event onReset
419                                  * @param {tinymce.Editor} sender Editor instance.
420                                  * @param {Event} evt W3C DOM Event instance.
421                                  * @example
422                                  * // Adds an observer to the onReset event using tinyMCE.init
423                                  * tinyMCE.init({
424                                  *    ...
425                                  *    setup : function(ed) {
426                                  *       ed.onReset.add(function(ed, e) {
427                                  *            console.debug('Form reset:' + e.target);
428                                  *       });
429                                  *    }
430                                  * });
431                                  */
432                                 'onReset',
433
434                                 /**
435                                  * Fires when a paste event is intercepted inside the editor.
436                                  *
437                                  * @event onPaste
438                                  * @param {tinymce.Editor} sender Editor instance.
439                                  * @param {Event} evt W3C DOM Event instance.
440                                  * @example
441                                  * // Adds an observer to the onPaste event using tinyMCE.init
442                                  * tinyMCE.init({
443                                  *    ...
444                                  *    setup : function(ed) {
445                                  *       ed.onPaste.add(function(ed, e) {
446                                  *            console.debug('Pasted plain text');
447                                  *       });
448                                  *    }
449                                  * });
450                                  */
451                                 'onPaste',
452
453                                 /**
454                                  * Fires when the Serializer does a preProcess on the contents.
455                                  *
456                                  * @event onPreProcess
457                                  * @param {tinymce.Editor} sender Editor instance.
458                                  * @param {Object} obj PreProcess object.
459                                  * @option {Node} node DOM node for the item being serialized.
460                                  * @option {String} format The specified output format normally "html".
461                                  * @option {Boolean} get Is true if the process is on a getContent operation.
462                                  * @option {Boolean} set Is true if the process is on a setContent operation.
463                                  * @option {Boolean} cleanup Is true if the process is on a cleanup operation.
464                                  * @example
465                                  * // Adds an observer to the onPreProcess event using tinyMCE.init
466                                  * tinyMCE.init({
467                                  *    ...
468                                  *    setup : function(ed) {
469                                  *       ed.onPreProcess.add(function(ed, o) {
470                                  *            // Add a class to each paragraph in the editor
471                                  *            ed.dom.addClass(ed.dom.select('p', o.node), 'myclass');
472                                  *       });
473                                  *    }
474                                  * });
475                                  */
476                                 'onPreProcess',
477
478                                 /**
479                                  * Fires when the Serializer does a postProcess on the contents.
480                                  *
481                                  * @event onPostProcess
482                                  * @param {tinymce.Editor} sender Editor instance.
483                                  * @param {Object} obj PreProcess object.
484                                  * @example
485                                  * // Adds an observer to the onPostProcess event using tinyMCE.init
486                                  * tinyMCE.init({
487                                  *    ...
488                                  *    setup : function(ed) {
489                                  *       ed.onPostProcess.add(function(ed, o) {
490                                  *            // Remove all paragraphs and replace with BR
491                                  *            o.content = o.content.replace(/<p[^>]+>|<p>/g, '');
492                                  *            o.content = o.content.replace(/<\/p>/g, '<br />');
493                                  *       });
494                                  *    }
495                                  * });
496                                  */
497                                 'onPostProcess',
498
499                                 /**
500                                  * Fires before new contents is added to the editor. Using for example setContent.
501                                  *
502                                  * @event onBeforeSetContent
503                                  * @param {tinymce.Editor} sender Editor instance.
504                                  * @example
505                                  * // Adds an observer to the onBeforeSetContent event using tinyMCE.init
506                                  * tinyMCE.init({
507                                  *    ...
508                                  *    setup : function(ed) {
509                                  *       ed.onBeforeSetContent.add(function(ed, o) {
510                                  *            // Replaces all a characters with b characters
511                                  *            o.content = o.content.replace(/a/g, 'b');
512                                  *       });
513                                  *    }
514                                  * });
515                                  */
516                                 'onBeforeSetContent',
517
518                                 /**
519                                  * Fires before contents is extracted from the editor using for example getContent.
520                                  *
521                                  * @event onBeforeGetContent
522                                  * @param {tinymce.Editor} sender Editor instance.
523                                  * @param {Event} evt W3C DOM Event instance.
524                                  * @example
525                                  * // Adds an observer to the onBeforeGetContent event using tinyMCE.init
526                                  * tinyMCE.init({
527                                  *    ...
528                                  *    setup : function(ed) {
529                                  *       ed.onBeforeGetContent.add(function(ed, o) {
530                                  *            console.debug('Before get content.');
531                                  *       });
532                                  *    }
533                                  * });
534                                  */
535                                 'onBeforeGetContent',
536
537                                 /**
538                                  * Fires after the contents has been added to the editor using for example onSetContent.
539                                  *
540                                  * @event onSetContent
541                                  * @param {tinymce.Editor} sender Editor instance.
542                                  * @example
543                                  * // Adds an observer to the onSetContent event using tinyMCE.init
544                                  * tinyMCE.init({
545                                  *    ...
546                                  *    setup : function(ed) {
547                                  *       ed.onSetContent.add(function(ed, o) {
548                                  *            // Replaces all a characters with b characters
549                                  *            o.content = o.content.replace(/a/g, 'b');
550                                  *       });
551                                  *    }
552                                  * });
553                                  */
554                                 'onSetContent',
555
556                                 /**
557                                  * Fires after the contents has been extracted from the editor using for example getContent.
558                                  *
559                                  * @event onGetContent
560                                  * @param {tinymce.Editor} sender Editor instance.
561                                  * @example
562                                  * // Adds an observer to the onGetContent event using tinyMCE.init
563                                  * tinyMCE.init({
564                                  *    ...
565                                  *    setup : function(ed) {
566                                  *       ed.onGetContent.add(function(ed, o) {
567                                  *           // Replace all a characters with b
568                                  *           o.content = o.content.replace(/a/g, 'b');
569                                  *       });
570                                  *    }
571                                  * });
572                                  */
573                                 'onGetContent',
574
575                                 /**
576                                  * Fires when the editor gets loaded with contents for example when the load method is executed.
577                                  *
578                                  * @event onLoadContent
579                                  * @param {tinymce.Editor} sender Editor instance.
580                                  * @example
581                                  * // Adds an observer to the onLoadContent event using tinyMCE.init
582                                  * tinyMCE.init({
583                                  *    ...
584                                  *    setup : function(ed) {
585                                  *       ed.onLoadContent.add(function(ed, o) {
586                                  *           // Output the element name
587                                  *           console.debug(o.element.nodeName);
588                                  *       });
589                                  *    }
590                                  * });
591                                  */
592                                 'onLoadContent',
593
594                                 /**
595                                  * Fires when the editor contents gets saved for example when the save method is executed.
596                                  *
597                                  * @event onSaveContent
598                                  * @param {tinymce.Editor} sender Editor instance.
599                                  * @example
600                                  * // Adds an observer to the onSaveContent event using tinyMCE.init
601                                  * tinyMCE.init({
602                                  *    ...
603                                  *    setup : function(ed) {
604                                  *       ed.onSaveContent.add(function(ed, o) {
605                                  *           // Output the element name
606                                  *           console.debug(o.element.nodeName);
607                                  *       });
608                                  *    }
609                                  * });
610                                  */
611                                 'onSaveContent',
612
613                                 /**
614                                  * Fires when the user changes node location using the mouse or keyboard.
615                                  *
616                                  * @event onNodeChange
617                                  * @param {tinymce.Editor} sender Editor instance.
618                                  * @example
619                                  * // Adds an observer to the onNodeChange event using tinyMCE.init
620                                  * tinyMCE.init({
621                                  *    ...
622                                  *    setup : function(ed) {
623                                  *       ed.onNodeChange.add(function(ed, cm, e) {
624                                  *           // Activates the link button when the caret is placed in a anchor element
625                                  *           if (e.nodeName == 'A')
626                                  *              cm.setActive('link', true);
627                                  *       });
628                                  *    }
629                                  * });
630                                  */
631                                 'onNodeChange',
632
633                                 /**
634                                  * Fires when a new undo level is added to the editor.
635                                  *
636                                  * @event onChange
637                                  * @param {tinymce.Editor} sender Editor instance.
638                                  * @example
639                                  * // Adds an observer to the onChange event using tinyMCE.init
640                                  * tinyMCE.init({
641                                  *    ...
642                                  *    setup : function(ed) {
643                                  *        ed.onChange.add(function(ed, l) {
644                                  *                console.debug('Editor contents was modified. Contents: ' + l.content);
645                                  *        });
646                                  *    }
647                                  * });
648                                  */
649                                 'onChange',
650
651                                 /**
652                                  * Fires before a command gets executed for example "Bold".
653                                  *
654                                  * @event onBeforeExecCommand
655                                  * @param {tinymce.Editor} sender Editor instance.
656                                  * @example
657                                  * // Adds an observer to the onBeforeExecCommand event using tinyMCE.init
658                                  * tinyMCE.init({
659                                  *    ...
660                                  *    setup : function(ed) {
661                                  *       ed.onBeforeExecCommand.add(function(ed, cmd, ui, val) {
662                                  *           console.debug('Command is to be executed: ' + cmd);
663                                  *       });
664                                  *    }
665                                  * });
666                                  */
667                                 'onBeforeExecCommand',
668
669                                 /**
670                                  * Fires after a command is executed for example "Bold".
671                                  *
672                                  * @event onExecCommand
673                                  * @param {tinymce.Editor} sender Editor instance.
674                                  * @example
675                                  * // Adds an observer to the onExecCommand event using tinyMCE.init
676                                  * tinyMCE.init({
677                                  *    ...
678                                  *    setup : function(ed) {
679                                  *       ed.onExecCommand.add(function(ed, cmd, ui, val) {
680                                  *           console.debug('Command was executed: ' + cmd);
681                                  *       });
682                                  *    }
683                                  * });
684                                  */
685                                 'onExecCommand',
686
687                                 /**
688                                  * Fires when the contents is undo:ed.
689                                  *
690                                  * @event onUndo
691                                  * @param {tinymce.Editor} sender Editor instance.
692                                  * @param {Object} level Undo level object.
693                                  * @ example
694                                  * // Adds an observer to the onUndo event using tinyMCE.init
695                                  * tinyMCE.init({
696                                  *    ...
697                                  *    setup : function(ed) {
698                                  *       ed.onUndo.add(function(ed, level) {
699                                  *           console.debug('Undo was performed: ' + level.content);
700                                  *       });
701                                  *    }
702                                  * });
703                                  */
704                                 'onUndo',
705
706                                 /**
707                                  * Fires when the contents is redo:ed.
708                                  *
709                                  * @event onRedo
710                                  * @param {tinymce.Editor} sender Editor instance.
711                                  * @param {Object} level Undo level object.
712                                  * @example
713                                  * // Adds an observer to the onRedo event using tinyMCE.init
714                                  * tinyMCE.init({
715                                  *    ...
716                                  *    setup : function(ed) {
717                                  *       ed.onRedo.add(function(ed, level) {
718                                  *           console.debug('Redo was performed: ' +level.content);
719                                  *       });
720                                  *    }
721                                  * });
722                                  */
723                                 'onRedo',
724
725                                 /**
726                                  * Fires when visual aids is enabled/disabled.
727                                  *
728                                  * @event onVisualAid
729                                  * @param {tinymce.Editor} sender Editor instance.
730                                  * @example
731                                  * // Adds an observer to the onVisualAid event using tinyMCE.init
732                                  * tinyMCE.init({
733                                  *    ...
734                                  *    setup : function(ed) {
735                                  *       ed.onVisualAid.add(function(ed, e, s) {
736                                  *           console.debug('onVisualAid event: ' + ed.id + ", State: " + s);
737                                  *       });
738                                  *    }
739                                  * });
740                                  */
741                                 'onVisualAid',
742
743                                 /**
744                                  * Fires when the progress throbber is shown above the editor.
745                                  *
746                                  * @event onSetProgressState
747                                  * @param {tinymce.Editor} sender Editor instance.
748                                  * @example
749                                  * // Adds an observer to the onSetProgressState event using tinyMCE.init
750                                  * tinyMCE.init({
751                                  *    ...
752                                  *    setup : function(ed) {
753                                  *       ed.onSetProgressState.add(function(ed, b) {
754                                  *            if (b)
755                                  *                 console.debug('SHOW!');
756                                  *            else
757                                  *                 console.debug('HIDE!');
758                                  *       });
759                                  *    }
760                                  * });
761                                  */
762                                 'onSetProgressState'
763                         ], function(e) {
764                                 t[e] = new Dispatcher(t);
765                         });
766
767                         /**
768                          * Name/value collection with editor settings.
769                          *
770                          * @property settings
771                          * @type Object
772                          * @example
773                          * // Get the value of the theme setting
774                          * tinyMCE.activeEditor.windowManager.alert("You are using the " + tinyMCE.activeEditor.settings.theme + " theme");
775                          */
776                         t.settings = s = extend({
777                                 id : id,
778                                 language : 'en',
779                                 docs_language : 'en',
780                                 theme : 'simple',
781                                 skin : 'default',
782                                 delta_width : 0,
783                                 delta_height : 0,
784                                 popup_css : '',
785                                 plugins : '',
786                                 document_base_url : tinymce.documentBaseURL,
787                                 add_form_submit_trigger : 1,
788                                 submit_patch : 1,
789                                 add_unload_trigger : 1,
790                                 convert_urls : 1,
791                                 relative_urls : 1,
792                                 remove_script_host : 1,
793                                 table_inline_editing : 0,
794                                 object_resizing : 1,
795                                 cleanup : 1,
796                                 accessibility_focus : 1,
797                                 custom_shortcuts : 1,
798                                 custom_undo_redo_keyboard_shortcuts : 1,
799                                 custom_undo_redo_restore_selection : 1,
800                                 custom_undo_redo : 1,
801                                 doctype : tinymce.isIE6 ? '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' : '<!DOCTYPE>', // Use old doctype on IE 6 to avoid horizontal scroll
802                                 visual_table_class : 'mceItemTable',
803                                 visual : 1,
804                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
805                                 apply_source_formatting : 1,
806                                 directionality : 'ltr',
807                                 forced_root_block : 'p',
808                                 hidden_input : 1,
809                                 padd_empty_editor : 1,
810                                 render_ui : 1,
811                                 init_theme : 1,
812                                 force_p_newlines : 1,
813                                 indentation : '30px',
814                                 keep_styles : 1,
815                                 fix_table_elements : 1,
816                                 inline_styles : 1,
817                                 convert_fonts_to_spans : true,
818                                 indent : 'simple',
819                                 indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',
820                                 indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',
821                                 validate : true,
822                                 entity_encoding : 'named',
823                                 url_converter : t.convertURL,
824                                 url_converter_scope : t,
825                                 ie7_compat : true
826                         }, s);
827
828                         /**
829                          * URI object to document configured for the TinyMCE instance.
830                          *
831                          * @property documentBaseURI
832                          * @type tinymce.util.URI
833                          * @example
834                          * // Get relative URL from the location of document_base_url
835                          * tinyMCE.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
836                          * 
837                          * // Get absolute URL from the location of document_base_url
838                          * tinyMCE.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
839                          */
840                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
841                                 base_uri : tinyMCE.baseURI
842                         });
843
844                         /**
845                          * URI object to current document that holds the TinyMCE editor instance.
846                          *
847                          * @property baseURI
848                          * @type tinymce.util.URI
849                          * @example
850                          * // Get relative URL from the location of the API
851                          * tinyMCE.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
852                          * 
853                          * // Get absolute URL from the location of the API
854                          * tinyMCE.activeEditor.baseURI.toAbsolute('somefile.htm');
855                          */
856                         t.baseURI = tinymce.baseURI;
857
858                         /**
859                          * Array with CSS files to load into the iframe.
860                          *
861                          * @property contentCSS
862                          * @type Array
863                          */                     
864                         t.contentCSS = [];
865
866                         // Call setup
867                         t.execCallback('setup', t);
868                 },
869
870                 /**
871                  * Renderes the editor/adds it to the page.
872                  *
873                  * @method render
874                  */
875                 render : function(nst) {
876                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
877
878                         // Page is not loaded yet, wait for it
879                         if (!Event.domLoaded) {
880                                 Event.add(document, 'init', function() {
881                                         t.render();
882                                 });
883                                 return;
884                         }
885
886                         tinyMCE.settings = s;
887
888                         // Element not found, then skip initialization
889                         if (!t.getElement())
890                                 return;
891
892                         // Is a iPad/iPhone and not on iOS5, then skip initialization. We need to sniff 
893                         // here since the browser says it has contentEditable support but there is no visible
894                         // caret We will remove this check ones Apple implements full contentEditable support
895                         if (tinymce.isIDevice && !tinymce.isIOS5)
896                                 return;
897
898                         // Add hidden input for non input elements inside form elements
899                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
900                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
901
902                         /**
903                          * Window manager reference, use this to open new windows and dialogs.
904                          *
905                          * @property windowManager
906                          * @type tinymce.WindowManager
907                          * @example
908                          * // Shows an alert message
909                          * tinyMCE.activeEditor.windowManager.alert('Hello world!');
910                          * 
911                          * // Opens a new dialog with the file.htm file and the size 320x240
912                          * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
913                          * tinyMCE.activeEditor.windowManager.open({
914                          *    url : 'file.htm',
915                          *    width : 320,
916                          *    height : 240
917                          * }, {
918                          *    custom_param : 1
919                          * });
920                          */
921                         if (tinymce.WindowManager)
922                                 t.windowManager = new tinymce.WindowManager(t);
923
924                         if (s.encoding == 'xml') {
925                                 t.onGetContent.add(function(ed, o) {
926                                         if (o.save)
927                                                 o.content = DOM.encode(o.content);
928                                 });
929                         }
930
931                         if (s.add_form_submit_trigger) {
932                                 t.onSubmit.addToTop(function() {
933                                         if (t.initialized) {
934                                                 t.save();
935                                                 t.isNotDirty = 1;
936                                         }
937                                 });
938                         }
939
940                         if (s.add_unload_trigger) {
941                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
942                                         if (t.initialized && !t.destroyed && !t.isHidden())
943                                                 t.save({format : 'raw', no_events : true});
944                                 });
945                         }
946
947                         tinymce.addUnload(t.destroy, t);
948
949                         if (s.submit_patch) {
950                                 t.onBeforeRenderUI.add(function() {
951                                         var n = t.getElement().form;
952
953                                         if (!n)
954                                                 return;
955
956                                         // Already patched
957                                         if (n._mceOldSubmit)
958                                                 return;
959
960                                         // Check page uses id="submit" or name="submit" for it's submit button
961                                         if (!n.submit.nodeType && !n.submit.length) {
962                                                 t.formElement = n;
963                                                 n._mceOldSubmit = n.submit;
964                                                 n.submit = function() {
965                                                         // Save all instances
966                                                         tinymce.triggerSave();
967                                                         t.isNotDirty = 1;
968
969                                                         return t.formElement._mceOldSubmit(t.formElement);
970                                                 };
971                                         }
972
973                                         n = null;
974                                 });
975                         }
976
977                         // Load scripts
978                         function loadScripts() {
979                                 if (s.language && s.language_load !== false)
980                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
981
982                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
983                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
984
985                                 each(explode(s.plugins), function(p) {
986                                         if (p &&!PluginManager.urls[p]) {
987                                                 if (p.charAt(0) == '-') {
988                                                         p = p.substr(1, p.length);
989                                                         var dependencies = PluginManager.dependencies(p);
990                                                         each(dependencies, function(dep) {
991                                                                 var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};
992                                                                 var dep = PluginManager.createUrl(defaultSettings, dep);
993                                                                 PluginManager.load(dep.resource, dep);
994                                                                 
995                                                         });
996                                                 } else {
997                                                         // Skip safari plugin, since it is removed as of 3.3b1
998                                                         if (p == 'safari') {
999                                                                 return;
1000                                                         }
1001                                                         PluginManager.load(p, {prefix:'plugins/', resource: p, suffix:'/editor_plugin' + tinymce.suffix + '.js'});
1002                                                 }
1003                                         }
1004                                 });
1005
1006                                 // Init when que is loaded
1007                                 sl.loadQueue(function() {
1008                                         if (!t.removed)
1009                                                 t.init();
1010                                 });
1011                         };
1012
1013                         loadScripts();
1014                 },
1015
1016                 /**
1017                  * Initializes the editor this will be called automatically when
1018                  * all plugins/themes and language packs are loaded by the rendered method.
1019                  * This method will setup the iframe and create the theme and plugin instances.
1020                  *
1021                  * @method init
1022                  */
1023                 init : function() {
1024                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
1025
1026                         tinymce.add(t);
1027
1028                         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));
1029
1030                         /**
1031                          * Reference to the theme instance that was used to generate the UI. 
1032                          *
1033                          * @property theme
1034                          * @type tinymce.Theme
1035                          * @example
1036                          * // Executes a method on the theme directly
1037                          * tinyMCE.activeEditor.theme.someMethod();
1038                          */
1039                         if (s.theme) {
1040                                 s.theme = s.theme.replace(/-/, '');
1041                                 o = ThemeManager.get(s.theme);
1042                                 t.theme = new o();
1043
1044                                 if (t.theme.init && s.init_theme)
1045                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
1046                         }
1047                         function initPlugin(p) {
1048                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
1049                                 if (c && tinymce.inArray(initializedPlugins,p) === -1) {
1050                                         each(PluginManager.dependencies(p), function(dep){
1051                                                 initPlugin(dep);
1052                                         });
1053                                         po = new c(t, u);
1054
1055                                         t.plugins[p] = po;
1056
1057                                         if (po.init) {
1058                                                 po.init(t, u);
1059                                                 initializedPlugins.push(p);
1060                                         }
1061                                 }
1062                         }
1063                         
1064                         // Create all plugins
1065                         each(explode(s.plugins.replace(/\-/g, '')), initPlugin);
1066
1067                         // Setup popup CSS path(s)
1068                         if (s.popup_css !== false) {
1069                                 if (s.popup_css)
1070                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
1071                                 else
1072                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
1073                         }
1074
1075                         if (s.popup_css_add)
1076                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
1077
1078                         /**
1079                          * Control manager instance for the editor. Will enables you to create new UI elements and change their states etc.
1080                          *
1081                          * @property controlManager
1082                          * @type tinymce.ControlManager
1083                          * @example
1084                          * // Disables the bold button
1085                          * tinyMCE.activeEditor.controlManager.setDisabled('bold', true);
1086                          */
1087                         t.controlManager = new tinymce.ControlManager(t);
1088
1089                         if (s.custom_undo_redo) {
1090                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {
1091                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
1092                                                 t.undoManager.beforeChange();
1093                                 });
1094
1095                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {
1096                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
1097                                                 t.undoManager.add();
1098                                 });
1099                         }
1100
1101                         t.onExecCommand.add(function(ed, c) {
1102                                 // Don't refresh the select lists until caret move
1103                                 if (!/^(FontName|FontSize)$/.test(c))
1104                                         t.nodeChanged();
1105                         });
1106
1107                         // Remove ghost selections on images and tables in Gecko
1108                         if (isGecko) {
1109                                 function repaint(a, o) {
1110                                         if (!o || !o.initial)
1111                                                 t.execCommand('mceRepaint');
1112                                 };
1113
1114                                 t.onUndo.add(repaint);
1115                                 t.onRedo.add(repaint);
1116                                 t.onSetContent.add(repaint);
1117                         }
1118
1119                         // Enables users to override the control factory
1120                         t.onBeforeRenderUI.dispatch(t, t.controlManager);
1121
1122                         // Measure box
1123                         if (s.render_ui) {
1124                                 w = s.width || e.style.width || e.offsetWidth;
1125                                 h = s.height || e.style.height || e.offsetHeight;
1126                                 t.orgDisplay = e.style.display;
1127                                 re = /^[0-9\.]+(|px)$/i;
1128
1129                                 if (re.test('' + w))
1130                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
1131
1132                                 if (re.test('' + h))
1133                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
1134
1135                                 // Render UI
1136                                 o = t.theme.renderUI({
1137                                         targetNode : e,
1138                                         width : w,
1139                                         height : h,
1140                                         deltaWidth : s.delta_width,
1141                                         deltaHeight : s.delta_height
1142                                 });
1143
1144                                 t.editorContainer = o.editorContainer;
1145                         }
1146
1147                         // #ifdef contentEditable
1148
1149                         // Content editable mode ends here
1150                         if (s.content_editable) {
1151                                 e = n = o = null; // Fix IE leak
1152                                 return t.setupContentEditable();
1153                         }
1154
1155                         // #endif
1156
1157                         // User specified a document.domain value
1158                         if (document.domain && location.hostname != document.domain)
1159                                 tinymce.relaxedDomain = document.domain;
1160
1161                         // Resize editor
1162                         DOM.setStyles(o.sizeContainer || o.editorContainer, {
1163                                 width : w,
1164                                 height : h
1165                         });
1166
1167                         // Load specified content CSS last
1168                         if (s.content_css) {
1169                                 tinymce.each(explode(s.content_css), function(u) {
1170                                         t.contentCSS.push(t.documentBaseURI.toAbsolute(u));
1171                                 });
1172                         }
1173
1174                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
1175                         if (h < 100)
1176                                 h = 100;
1177
1178                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
1179
1180                         // We only need to override paths if we have to
1181                         // IE has a bug where it remove site absolute urls to relative ones if this is specified
1182                         if (s.document_base_url != tinymce.documentBaseURL)
1183                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';
1184
1185                         // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
1186                         if (s.ie7_compat)
1187                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
1188                         else
1189                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';
1190
1191                         t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
1192
1193                         // Firefox 2 doesn't load stylesheets correctly this way
1194                         if (!isGecko || !/Firefox\/2/.test(navigator.userAgent)) {
1195                                 for (i = 0; i < t.contentCSS.length; i++)
1196                                         t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';
1197
1198                                 t.contentCSS = [];
1199                         }
1200
1201                         bi = s.body_id || 'tinymce';
1202                         if (bi.indexOf('=') != -1) {
1203                                 bi = t.getParam('body_id', '', 'hash');
1204                                 bi = bi[t.id] || bi;
1205                         }
1206
1207                         bc = s.body_class || '';
1208                         if (bc.indexOf('=') != -1) {
1209                                 bc = t.getParam('body_class', '', 'hash');
1210                                 bc = bc[t.id] || '';
1211                         }
1212
1213                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
1214
1215                         // Domain relaxing enabled, then set document domain
1216                         if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
1217                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button
1218                                 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';                         
1219                         }
1220
1221                         // Create iframe
1222                         // TODO: ACC add the appropriate description on this.
1223                         n = DOM.add(o.iframeContainer, 'iframe', { 
1224                                 id : t.id + "_ifr",
1225                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
1226                                 frameBorder : '0',
1227                                 allowTransparency : "true",
1228                                 title : s.aria_label,
1229                                 style : {
1230                                         width : '100%',
1231                                         height : h
1232                                 }
1233                         });
1234
1235                         t.contentAreaContainer = o.iframeContainer;
1236                         DOM.get(o.editorContainer).style.display = t.orgDisplay;
1237                         DOM.get(t.id).style.display = 'none';
1238                         DOM.setAttrib(t.id, 'aria-hidden', true);
1239
1240                         if (!tinymce.relaxedDomain || !u)
1241                                 t.setupIframe();
1242
1243                         e = n = o = null; // Cleanup
1244                 },
1245
1246                 /**
1247                  * This method get called by the init method ones the iframe is loaded.
1248                  * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
1249                  * This method should not be called directly.
1250                  *
1251                  * @method setupIframe
1252                  */
1253                 setupIframe : function(filled) {
1254                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
1255
1256                         // Setup iframe body
1257                         if ((!isIE || !tinymce.relaxedDomain) && !filled) {
1258                                 // We need to wait for the load event on Gecko
1259                                 if (isGecko && !s.readonly) {
1260                                         t.getWin().addEventListener("DOMContentLoaded", function() {
1261                                                 window.setTimeout(function() {
1262                                                         var b = t.getBody(), undef;
1263
1264                                                         // Editable element needs to have some contents or backspace/delete won't work properly for some odd reason on FF 3.6 or older
1265                                                         b.innerHTML = '<br>';
1266
1267                                                         // Check if Gecko supports contentEditable mode FF2 doesn't
1268                                                         if (b.contentEditable !== undef) {
1269                                                                 // Setting the contentEditable off/on seems to force caret mode in the editor and enabled auto focus
1270                                                                 b.contentEditable = false;
1271                                                                 b.contentEditable = true;
1272
1273                                                                 // Caret doesn't get rendered when you mousedown on the HTML element on FF 3.x
1274                                                                 t.onMouseDown.add(function(ed, e) {
1275                                                                         if (e.target.nodeName === "HTML") {
1276                                                                                 // Setting the contentEditable off/on seems to force caret mode in the editor and enabled auto focus
1277                                                                                 b.contentEditable = false;
1278                                                                                 b.contentEditable = true;
1279
1280                                                                                 d.designMode = 'on'; // Render the caret
1281
1282                                                                                 // Remove design mode again after a while so it has some time to execute
1283                                                                                 window.setTimeout(function() {
1284                                                                                         d.designMode = 'off';
1285                                                                                         t.getBody().focus();
1286                                                                                 }, 1);
1287                                                                         }
1288                                                                 });
1289                                                         } else
1290                                                                 d.designMode = 'on';
1291
1292                                                         // Call setup frame once the contentEditable/designMode has been initialized
1293                                                         // since the caret won't be rendered some times otherwise.
1294                                                         t.setupIframe(true);
1295                                                 }, 1);
1296                                         }, false);
1297                                 }
1298
1299                                 d.open();
1300                                 d.write(t.iframeHTML);
1301                                 d.close();
1302
1303                                 if (tinymce.relaxedDomain)
1304                                         d.domain = tinymce.relaxedDomain;
1305
1306                                 // Wait for iframe onload event on Gecko
1307                                 if (isGecko && !s.readonly)
1308                                         return;
1309                         }
1310
1311                         // It will not steal focus while setting contentEditable
1312                         b = t.getBody();
1313                         b.disabled = true;
1314
1315                         if (!isGecko && !s.readonly)
1316                                 b.contentEditable = true;
1317
1318                         b.disabled = false;
1319
1320                         /**
1321                          * Schema instance, enables you to validate elements and it's children.
1322                          *
1323                          * @property schema
1324                          * @type tinymce.html.Schema
1325                          */
1326                         t.schema = new tinymce.html.Schema(s);
1327
1328                         /**
1329                          * DOM instance for the editor.
1330                          *
1331                          * @property dom
1332                          * @type tinymce.dom.DOMUtils
1333                          * @example
1334                          * // Adds a class to all paragraphs within the editor
1335                          * tinyMCE.activeEditor.dom.addClass(tinyMCE.activeEditor.dom.select('p'), 'someclass');
1336                          */
1337                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
1338                                 keep_values : true,
1339                                 url_converter : t.convertURL,
1340                                 url_converter_scope : t,
1341                                 hex_colors : s.force_hex_style_colors,
1342                                 class_filter : s.class_filter,
1343                                 update_styles : 1,
1344                                 fix_ie_paragraphs : 1,
1345                                 schema : t.schema
1346                         });
1347
1348                         /**
1349                          * HTML parser will be used when contents is inserted into the editor.
1350                          *
1351                          * @property parser
1352                          * @type tinymce.html.DomParser
1353                          */
1354                         t.parser = new tinymce.html.DomParser(s, t.schema);
1355
1356                         // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
1357                         if (!t.settings.allow_html_in_named_anchor) {
1358                                 t.parser.addAttributeFilter('name', function(nodes, name) {
1359                                         var i = nodes.length, sibling, prevSibling, parent, node;
1360         
1361                                         while (i--) {
1362                                                 node = nodes[i];
1363                                                 if (node.name === 'a' && node.firstChild) {
1364                                                         parent = node.parent;
1365         
1366                                                         // Move children after current node
1367                                                         sibling = node.lastChild;
1368                                                         do {
1369                                                                 prevSibling = sibling.prev;
1370                                                                 parent.insert(sibling, node);
1371                                                                 sibling = prevSibling;
1372                                                         } while (sibling);
1373                                                 }
1374                                         }
1375                                 });
1376                         }
1377
1378                         // Convert src and href into data-mce-src, data-mce-href and data-mce-style
1379                         t.parser.addAttributeFilter('src,href,style', function(nodes, name) {
1380                                 var i = nodes.length, node, dom = t.dom, value, internalName;
1381
1382                                 while (i--) {
1383                                         node = nodes[i];
1384                                         value = node.attr(name);
1385                                         internalName = 'data-mce-' + name;
1386
1387                                         // Add internal attribute if we need to we don't on a refresh of the document
1388                                         if (!node.attributes.map[internalName]) {       
1389                                                 if (name === "style")
1390                                                         node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
1391                                                 else
1392                                                         node.attr(internalName, t.convertURL(value, name, node.name));
1393                                         }
1394                                 }
1395                         });
1396
1397                         // Keep scripts from executing
1398                         t.parser.addNodeFilter('script', function(nodes, name) {
1399                                 var i = nodes.length;
1400
1401                                 while (i--)
1402                                         nodes[i].attr('type', 'mce-text/javascript');
1403                         });
1404
1405                         t.parser.addNodeFilter('#cdata', function(nodes, name) {
1406                                 var i = nodes.length, node;
1407
1408                                 while (i--) {
1409                                         node = nodes[i];
1410                                         node.type = 8;
1411                                         node.name = '#comment';
1412                                         node.value = '[CDATA[' + node.value + ']]';
1413                                 }
1414                         });
1415
1416                         t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
1417                                 var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();
1418
1419                                 while (i--) {
1420                                         node = nodes[i];
1421
1422                                         if (node.isEmpty(nonEmptyElements))
1423                                                 node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;
1424                                 }
1425                         });
1426
1427                         /**
1428                          * DOM serializer for the editor. Will be used when contents is extracted from the editor.
1429                          *
1430                          * @property serializer
1431                          * @type tinymce.dom.Serializer
1432                          * @example
1433                          * // Serializes the first paragraph in the editor into a string
1434                          * tinyMCE.activeEditor.serializer.serialize(tinyMCE.activeEditor.dom.select('p')[0]);
1435                          */
1436                         t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);
1437
1438                         /**
1439                          * Selection instance for the editor.
1440                          *
1441                          * @property selection
1442                          * @type tinymce.dom.Selection
1443                          * @example
1444                          * // Sets some contents to the current selection in the editor
1445                          * tinyMCE.activeEditor.selection.setContent('Some contents');
1446                          *
1447                          * // Gets the current selection
1448                          * alert(tinyMCE.activeEditor.selection.getContent());
1449                          *
1450                          * // Selects the first paragraph found
1451                          * tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
1452                          */
1453                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
1454
1455                         /**
1456                          * Formatter instance.
1457                          *
1458                          * @property formatter
1459                          * @type tinymce.Formatter
1460                          */
1461                         t.formatter = new tinymce.Formatter(this);
1462
1463                         // Register default formats
1464                         t.formatter.register({
1465                                 alignleft : [
1466                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
1467                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
1468                                 ],
1469
1470                                 aligncenter : [
1471                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
1472                                         {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
1473                                         {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
1474                                 ],
1475
1476                                 alignright : [
1477                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
1478                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
1479                                 ],
1480
1481                                 alignfull : [
1482                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}
1483                                 ],
1484
1485                                 bold : [
1486                                         {inline : 'strong', remove : 'all'},
1487                                         {inline : 'span', styles : {fontWeight : 'bold'}},
1488                                         {inline : 'b', remove : 'all'}
1489                                 ],
1490
1491                                 italic : [
1492                                         {inline : 'em', remove : 'all'},
1493                                         {inline : 'span', styles : {fontStyle : 'italic'}},
1494                                         {inline : 'i', remove : 'all'}
1495                                 ],
1496
1497                                 underline : [
1498                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
1499                                         {inline : 'u', remove : 'all'}
1500                                 ],
1501
1502                                 strikethrough : [
1503                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
1504                                         {inline : 'strike', remove : 'all'}
1505                                 ],
1506
1507                                 forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
1508                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
1509                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},
1510                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},
1511                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
1512                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
1513                                 subscript : {inline : 'sub'},
1514                                 superscript : {inline : 'sup'},
1515
1516                                 removeformat : [
1517                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
1518                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
1519                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
1520                                 ]
1521                         });
1522
1523                         // Register default block formats
1524                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
1525                                 t.formatter.register(name, {block : name, remove : 'all'});
1526                         });
1527
1528                         // Register user defined formats
1529                         t.formatter.register(t.settings.formats);
1530
1531                         /**
1532                          * Undo manager instance, responsible for handling undo levels. 
1533                          *
1534                          * @property undoManager
1535                          * @type tinymce.UndoManager
1536                          * @example
1537                          * // Undoes the last modification to the editor
1538                          * tinyMCE.activeEditor.undoManager.undo();
1539                          */
1540                         t.undoManager = new tinymce.UndoManager(t);
1541
1542                         // Pass through
1543                         t.undoManager.onAdd.add(function(um, l) {
1544                                 if (um.hasUndo())
1545                                         return t.onChange.dispatch(t, l, um);
1546                         });
1547
1548                         t.undoManager.onUndo.add(function(um, l) {
1549                                 return t.onUndo.dispatch(t, l, um);
1550                         });
1551
1552                         t.undoManager.onRedo.add(function(um, l) {
1553                                 return t.onRedo.dispatch(t, l, um);
1554                         });
1555
1556                         t.forceBlocks = new tinymce.ForceBlocks(t, {
1557                                 forced_root_block : s.forced_root_block
1558                         });
1559
1560                         t.editorCommands = new tinymce.EditorCommands(t);
1561
1562                         // Pass through
1563                         t.serializer.onPreProcess.add(function(se, o) {
1564                                 return t.onPreProcess.dispatch(t, o, se);
1565                         });
1566
1567                         t.serializer.onPostProcess.add(function(se, o) {
1568                                 return t.onPostProcess.dispatch(t, o, se);
1569                         });
1570
1571                         t.onPreInit.dispatch(t);
1572
1573                         if (!s.gecko_spellcheck)
1574                                 t.getBody().spellcheck = 0;
1575
1576                         if (!s.readonly)
1577                                 t._addEvents();
1578
1579                         t.controlManager.onPostRender.dispatch(t, t.controlManager);
1580                         t.onPostRender.dispatch(t);
1581
1582                         t.quirks = new tinymce.util.Quirks(this);
1583
1584                         if (s.directionality)
1585                                 t.getBody().dir = s.directionality;
1586
1587                         if (s.nowrap)
1588                                 t.getBody().style.whiteSpace = "nowrap";
1589
1590                         if (s.handle_node_change_callback) {
1591                                 t.onNodeChange.add(function(ed, cm, n) {
1592                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
1593                                 });
1594                         }
1595
1596                         if (s.save_callback) {
1597                                 t.onSaveContent.add(function(ed, o) {
1598                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
1599
1600                                         if (h)
1601                                                 o.content = h;
1602                                 });
1603                         }
1604
1605                         if (s.onchange_callback) {
1606                                 t.onChange.add(function(ed, l) {
1607                                         t.execCallback('onchange_callback', t, l);
1608                                 });
1609                         }
1610
1611                         if (s.protect) {
1612                                 t.onBeforeSetContent.add(function(ed, o) {
1613                                         if (s.protect) {
1614                                                 each(s.protect, function(pattern) {
1615                                                         o.content = o.content.replace(pattern, function(str) {
1616                                                                 return '<!--mce:protected ' + escape(str) + '-->';
1617                                                         });
1618                                                 });
1619                                         }
1620                                 });
1621                         }
1622
1623                         if (s.convert_newlines_to_brs) {
1624                                 t.onBeforeSetContent.add(function(ed, o) {
1625                                         if (o.initial)
1626                                                 o.content = o.content.replace(/\r?\n/g, '<br />');
1627                                 });
1628                         }
1629
1630                         if (s.preformatted) {
1631                                 t.onPostProcess.add(function(ed, o) {
1632                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');
1633                                         o.content = o.content.replace(/<\/pre>\s*$/, '');
1634
1635                                         if (o.set)
1636                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
1637                                 });
1638                         }
1639
1640                         if (s.verify_css_classes) {
1641                                 t.serializer.attribValueFilter = function(n, v) {
1642                                         var s, cl;
1643
1644                                         if (n == 'class') {
1645                                                 // Build regexp for classes
1646                                                 if (!t.classesRE) {
1647                                                         cl = t.dom.getClasses();
1648
1649                                                         if (cl.length > 0) {
1650                                                                 s = '';
1651
1652                                                                 each (cl, function(o) {
1653                                                                         s += (s ? '|' : '') + o['class'];
1654                                                                 });
1655
1656                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');
1657                                                         }
1658                                                 }
1659
1660                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
1661                                         }
1662
1663                                         return v;
1664                                 };
1665                         }
1666
1667                         if (s.cleanup_callback) {
1668                                 t.onBeforeSetContent.add(function(ed, o) {
1669                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
1670                                 });
1671
1672                                 t.onPreProcess.add(function(ed, o) {
1673                                         if (o.set)
1674                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
1675
1676                                         if (o.get)
1677                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
1678                                 });
1679
1680                                 t.onPostProcess.add(function(ed, o) {
1681                                         if (o.set)
1682                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
1683
1684                                         if (o.get)                                              
1685                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
1686                                 });
1687                         }
1688
1689                         if (s.save_callback) {
1690                                 t.onGetContent.add(function(ed, o) {
1691                                         if (o.save)
1692                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
1693                                 });
1694                         }
1695
1696                         if (s.handle_event_callback) {
1697                                 t.onEvent.add(function(ed, e, o) {
1698                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)
1699                                                 Event.cancel(e);
1700                                 });
1701                         }
1702
1703                         // Add visual aids when new contents is added
1704                         t.onSetContent.add(function() {
1705                                 t.addVisual(t.getBody());
1706                         });
1707
1708                         // Remove empty contents
1709                         if (s.padd_empty_editor) {
1710                                 t.onPostProcess.add(function(ed, o) {
1711                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
1712                                 });
1713                         }
1714
1715                         if (isGecko) {
1716                                 // Fix gecko link bug, when a link is placed at the end of block elements there is
1717                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link
1718                                 function fixLinks(ed, o) {
1719                                         each(ed.dom.select('a'), function(n) {
1720                                                 var pn = n.parentNode;
1721
1722                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)
1723                                                         ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});
1724                                         });
1725                                 };
1726
1727                                 t.onExecCommand.add(function(ed, cmd) {
1728                                         if (cmd === 'CreateLink')
1729                                                 fixLinks(ed);
1730                                 });
1731
1732                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
1733                         }
1734
1735                         t.load({initial : true, format : 'html'});
1736                         t.startContent = t.getContent({format : 'raw'});
1737                         t.undoManager.add();
1738                         t.initialized = true;
1739
1740                         t.onInit.dispatch(t);
1741                         t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
1742                         t.execCallback('init_instance_callback', t);
1743                         t.focus(true);
1744                         t.nodeChanged({initial : 1});
1745
1746                         // Load specified content CSS last
1747                         each(t.contentCSS, function(u) {
1748                                 t.dom.loadCSS(u);
1749                         });
1750
1751                         // Handle auto focus
1752                         if (s.auto_focus) {
1753                                 setTimeout(function () {
1754                                         var ed = tinymce.get(s.auto_focus);
1755
1756                                         ed.selection.select(ed.getBody(), 1);
1757                                         ed.selection.collapse(1);
1758                                         ed.getBody().focus();
1759                                         ed.getWin().focus();
1760                                 }, 100);
1761                         }
1762
1763                         e = null;
1764                 },
1765
1766                 // #ifdef contentEditable
1767
1768                 /**
1769                  * Sets up the contentEditable mode.
1770                  *
1771                  * @method setupContentEditable
1772                  */
1773                 setupContentEditable : function() {
1774                         var t = this, s = t.settings, e = t.getElement();
1775
1776                         t.contentDocument = s.content_document || document;
1777                         t.contentWindow = s.content_window || window;
1778                         t.bodyElement = e;
1779
1780                         // Prevent leak in IE
1781                         s.content_document = s.content_window = null;
1782
1783                         DOM.hide(e);
1784                         e.contentEditable = t.getParam('content_editable_state', true);
1785                         DOM.show(e);
1786
1787                         if (!s.gecko_spellcheck)
1788                                 t.getDoc().body.spellcheck = 0;
1789
1790                         // Setup objects
1791                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
1792                                 keep_values : true,
1793                                 url_converter : t.convertURL,
1794                                 url_converter_scope : t,
1795                                 hex_colors : s.force_hex_style_colors,
1796                                 class_filter : s.class_filter,
1797                                 root_element : t.id,
1798                                 fix_ie_paragraphs : 1,
1799                                 update_styles : 1
1800                         });
1801
1802                         t.serializer = new tinymce.dom.Serializer(s, t.dom, schema);
1803
1804                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
1805                         t.forceBlocks = new tinymce.ForceBlocks(t, {
1806                                 forced_root_block : s.forced_root_block
1807                         });
1808
1809                         t.editorCommands = new tinymce.EditorCommands(t);
1810
1811                         // Pass through
1812                         t.serializer.onPreProcess.add(function(se, o) {
1813                                 return t.onPreProcess.dispatch(t, o, se);
1814                         });
1815
1816                         t.serializer.onPostProcess.add(function(se, o) {
1817                                 return t.onPostProcess.dispatch(t, o, se);
1818                         });
1819
1820                         t.onPreInit.dispatch(t);
1821                         t._addEvents();
1822
1823                         t.controlManager.onPostRender.dispatch(t, t.controlManager);
1824                         t.onPostRender.dispatch(t);
1825
1826                         t.onSetContent.add(function() {
1827                                 t.addVisual(t.getBody());
1828                         });
1829
1830                         //t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
1831                         t.startContent = t.getContent({format : 'raw'});
1832                         t.undoManager.add({initial : true});
1833                         t.initialized = true;
1834
1835                         t.onInit.dispatch(t);
1836                         t.focus(true);
1837                         t.nodeChanged({initial : 1});
1838
1839                         // Load specified content CSS last
1840                         if (s.content_css) {
1841                                 each(explode(s.content_css), function(u) {
1842                                         t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
1843                                 });
1844                         }
1845
1846                         if (isIE) {
1847                                 // Store away selection
1848                                 t.dom.bind(t.getElement(), 'beforedeactivate', function() {
1849                                         t.lastSelectionBookmark = t.selection.getBookmark(1);
1850                                 });
1851
1852                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, o) {
1853                                         if (!DOM.getParent(ed.selection.getStart(), function(n) {return n == ed.getBody();}))
1854                                                 o.terminate = 1;
1855
1856                                         if (!DOM.getParent(ed.selection.getEnd(), function(n) {return n == ed.getBody();}))
1857                                                 o.terminate = 1;
1858                                 });
1859                         }
1860
1861                         e = null; // Cleanup
1862                 },
1863
1864                 // #endif
1865
1866                 /**
1867                  * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
1868                  * it will also place DOM focus inside the editor.
1869                  *
1870                  * @method focus
1871                  * @param {Boolean} sf Skip DOM focus. Just set is as the active editor.
1872                  */
1873                 focus : function(sf) {
1874                         var oed, t = this, selection = t.selection, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();
1875
1876                         if (!sf) {
1877                                 // Get selected control element
1878                                 ieRng = selection.getRng();
1879                                 if (ieRng.item) {
1880                                         controlElm = ieRng.item(0);
1881                                 }
1882
1883                                 selection.normalize();
1884
1885                                 // Is not content editable
1886                                 if (!ce)
1887                                         t.getWin().focus();
1888
1889                                 // Focus the body as well since it's contentEditable
1890                                 if (tinymce.isGecko) {
1891                                         t.getBody().focus();
1892                                 }
1893
1894                                 // Restore selected control element
1895                                 // This is needed when for example an image is selected within a
1896                                 // layer a call to focus will then remove the control selection
1897                                 if (controlElm && controlElm.ownerDocument == doc) {
1898                                         ieRng = doc.body.createControlRange();
1899                                         ieRng.addElement(controlElm);
1900                                         ieRng.select();
1901                                 }
1902
1903                                 // #ifdef contentEditable
1904
1905                                 // Content editable mode ends here
1906                                 if (ce) {
1907                                         if (tinymce.isWebKit)
1908                                                 t.getWin().focus();
1909                                         else {
1910                                                 if (tinymce.isIE)
1911                                                         t.getElement().setActive();
1912                                                 else
1913                                                         t.getElement().focus();
1914                                         }
1915                                 }
1916
1917                                 // #endif
1918                         }
1919
1920                         if (tinymce.activeEditor != t) {
1921                                 if ((oed = tinymce.activeEditor) != null)
1922                                         oed.onDeactivate.dispatch(oed, t);
1923
1924                                 t.onActivate.dispatch(t, oed);
1925                         }
1926
1927                         tinymce._setActive(t);
1928                 },
1929
1930                 /**
1931                  * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
1932                  * There new event model is a better way to add callback so this method might be removed in the future.
1933                  *
1934                  * @method execCallback
1935                  * @param {String} n Name of the callback to execute.
1936                  * @return {Object} Return value passed from callback function.
1937                  */
1938                 execCallback : function(n) {
1939                         var t = this, f = t.settings[n], s;
1940
1941                         if (!f)
1942                                 return;
1943
1944                         // Look through lookup
1945                         if (t.callbackLookup && (s = t.callbackLookup[n])) {
1946                                 f = s.func;
1947                                 s = s.scope;
1948                         }
1949
1950                         if (is(f, 'string')) {
1951                                 s = f.replace(/\.\w+$/, '');
1952                                 s = s ? tinymce.resolve(s) : 0;
1953                                 f = tinymce.resolve(f);
1954                                 t.callbackLookup = t.callbackLookup || {};
1955                                 t.callbackLookup[n] = {func : f, scope : s};
1956                         }
1957
1958                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
1959                 },
1960
1961                 /**
1962                  * Translates the specified string by replacing variables with language pack items it will also check if there is
1963                  * a key mathcin the input.
1964                  *
1965                  * @method translate
1966                  * @param {String} s String to translate by the language pack data.
1967                  * @return {String} Translated string.
1968                  */
1969                 translate : function(s) {
1970                         var c = this.settings.language || 'en', i18n = tinymce.i18n;
1971
1972                         if (!s)
1973                                 return '';
1974
1975                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
1976                                 return i18n[c + '.' + b] || '{#' + b + '}';
1977                         });
1978                 },
1979
1980                 /**
1981                  * Returns a language pack item by name/key.
1982                  *
1983                  * @method getLang
1984                  * @param {String} n Name/key to get from the language pack.
1985                  * @param {String} dv Optional default value to retrive.
1986                  */
1987                 getLang : function(n, dv) {
1988                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
1989                 },
1990
1991                 /**
1992                  * Returns a configuration parameter by name.
1993                  *
1994                  * @method getParam
1995                  * @param {String} n Configruation parameter to retrive.
1996                  * @param {String} dv Optional default value to return.
1997                  * @param {String} ty Optional type parameter.
1998                  * @return {String} Configuration parameter value or default value.
1999                  * @example
2000                  * // Returns a specific config value from the currently active editor
2001                  * var someval = tinyMCE.activeEditor.getParam('myvalue');
2002                  * 
2003                  * // Returns a specific config value from a specific editor instance by id
2004                  * var someval2 = tinyMCE.get('my_editor').getParam('myvalue');
2005                  */
2006                 getParam : function(n, dv, ty) {
2007                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
2008
2009                         if (ty === 'hash') {
2010                                 o = {};
2011
2012                                 if (is(v, 'string')) {
2013                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
2014                                                 v = v.split('=');
2015
2016                                                 if (v.length > 1)
2017                                                         o[tr(v[0])] = tr(v[1]);
2018                                                 else
2019                                                         o[tr(v[0])] = tr(v);
2020                                         });
2021                                 } else
2022                                         o = v;
2023
2024                                 return o;
2025                         }
2026
2027                         return v;
2028                 },
2029
2030                 /**
2031                  * Distpaches out a onNodeChange event to all observers. This method should be called when you
2032                  * need to update the UI states or element path etc.
2033                  *
2034                  * @method nodeChanged
2035                  * @param {Object} o Optional object to pass along for the node changed event.
2036                  */
2037                 nodeChanged : function(o) {
2038                         var t = this, s = t.selection, n = s.getStart() || t.getBody();
2039
2040                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
2041                         if (t.initialized) {
2042                                 o = o || {};
2043                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state
2044
2045                                 // Get parents and add them to object
2046                                 o.parents = [];
2047                                 t.dom.getParent(n, function(node) {
2048                                         if (node.nodeName == 'BODY')
2049                                                 return true;
2050
2051                                         o.parents.push(node);
2052                                 });
2053
2054                                 t.onNodeChange.dispatch(
2055                                         t,
2056                                         o ? o.controlManager || t.controlManager : t.controlManager,
2057                                         n,
2058                                         s.isCollapsed(),
2059                                         o
2060                                 );
2061                         }
2062                 },
2063
2064                 /**
2065                  * Adds a button that later gets created by the ControlManager. This is a shorter and easier method
2066                  * of adding buttons without the need to deal with the ControlManager directly. But it's also less
2067                  * powerfull if you need more control use the ControlManagers factory methods instead.
2068                  *
2069                  * @method addButton
2070                  * @param {String} n Button name to add.
2071                  * @param {Object} s Settings object with title, cmd etc.
2072                  * @example
2073                  * // Adds a custom button to the editor and when a user clicks the button it will open
2074                  * // an alert box with the selected contents as plain text.
2075                  * tinyMCE.init({
2076                  *    ...
2077                  * 
2078                  *    theme_advanced_buttons1 : 'example,..'
2079                  * 
2080                  *    setup : function(ed) {
2081                  *       // Register example button
2082                  *       ed.addButton('example', {
2083                  *          title : 'example.desc',
2084                  *          image : '../jscripts/tiny_mce/plugins/example/img/example.gif',
2085                  *          onclick : function() {
2086                  *             ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format : 'text'}));
2087                  *          }
2088                  *       });
2089                  *    }
2090                  * });
2091                  */
2092                 addButton : function(n, s) {
2093                         var t = this;
2094
2095                         t.buttons = t.buttons || {};
2096                         t.buttons[n] = s;
2097                 },
2098
2099                 /**
2100                  * Adds a custom command to the editor, you can also override existing commands with this method.
2101                  * The command that you add can be executed with execCommand.
2102                  *
2103                  * @method addCommand
2104                  * @param {String} name Command name to add/override.
2105                  * @param {addCommandCallback} callback Function to execute when the command occurs.
2106                  * @param {Object} scope Optional scope to execute the function in.
2107                  * @example
2108                  * // Adds a custom command that later can be executed using execCommand
2109                  * tinyMCE.init({
2110                  *    ...
2111                  * 
2112                  *    setup : function(ed) {
2113                  *       // Register example command
2114                  *       ed.addCommand('mycommand', function(ui, v) {
2115                  *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format : 'text'}));
2116                  *       });
2117                  *    }
2118                  * });
2119                  */
2120                 addCommand : function(name, callback, scope) {
2121                         /**
2122                          * Callback function that gets called when a command is executed.
2123                          *
2124                          * @callback addCommandCallback
2125                          * @param {Boolean} ui Display UI state true/false.
2126                          * @param {Object} value Optional value for command.
2127                          * @return {Boolean} True/false state if the command was handled or not.
2128                          */
2129                         this.execCommands[name] = {func : callback, scope : scope || this};
2130                 },
2131
2132                 /**
2133                  * Adds a custom query state command to the editor, you can also override existing commands with this method.
2134                  * The command that you add can be executed with queryCommandState function.
2135                  *
2136                  * @method addQueryStateHandler
2137                  * @param {String} name Command name to add/override.
2138                  * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
2139                  * @param {Object} scope Optional scope to execute the function in.
2140                  */
2141                 addQueryStateHandler : function(name, callback, scope) {
2142                         /**
2143                          * Callback function that gets called when a queryCommandState is executed.
2144                          *
2145                          * @callback addQueryStateHandlerCallback
2146                          * @return {Boolean} True/false state if the command is enabled or not like is it bold.
2147                          */
2148                         this.queryStateCommands[name] = {func : callback, scope : scope || this};
2149                 },
2150
2151                 /**
2152                  * Adds a custom query value command to the editor, you can also override existing commands with this method.
2153                  * The command that you add can be executed with queryCommandValue function.
2154                  *
2155                  * @method addQueryValueHandler
2156                  * @param {String} name Command name to add/override.
2157                  * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
2158                  * @param {Object} scope Optional scope to execute the function in.
2159                  */
2160                 addQueryValueHandler : function(name, callback, scope) {
2161                         /**
2162                          * Callback function that gets called when a queryCommandValue is executed.
2163                          *
2164                          * @callback addQueryValueHandlerCallback
2165                          * @return {Object} Value of the command or undefined.
2166                          */
2167                         this.queryValueCommands[name] = {func : callback, scope : scope || this};
2168                 },
2169
2170                 /**
2171                  * Adds a keyboard shortcut for some command or function.
2172                  *
2173                  * @method addShortcut
2174                  * @param {String} pa Shortcut pattern. Like for example: ctrl+alt+o.
2175                  * @param {String} desc Text description for the command.
2176                  * @param {String/Function} cmd_func Command name string or function to execute when the key is pressed.
2177                  * @param {Object} sc Optional scope to execute the function in.
2178                  * @return {Boolean} true/false state if the shortcut was added or not.
2179                  */
2180                 addShortcut : function(pa, desc, cmd_func, sc) {
2181                         var t = this, c;
2182
2183                         if (!t.settings.custom_shortcuts)
2184                                 return false;
2185
2186                         t.shortcuts = t.shortcuts || {};
2187
2188                         if (is(cmd_func, 'string')) {
2189                                 c = cmd_func;
2190
2191                                 cmd_func = function() {
2192                                         t.execCommand(c, false, null);
2193                                 };
2194                         }
2195
2196                         if (is(cmd_func, 'object')) {
2197                                 c = cmd_func;
2198
2199                                 cmd_func = function() {
2200                                         t.execCommand(c[0], c[1], c[2]);
2201                                 };
2202                         }
2203
2204                         each(explode(pa), function(pa) {
2205                                 var o = {
2206                                         func : cmd_func,
2207                                         scope : sc || this,
2208                                         desc : desc,
2209                                         alt : false,
2210                                         ctrl : false,
2211                                         shift : false
2212                                 };
2213
2214                                 each(explode(pa, '+'), function(v) {
2215                                         switch (v) {
2216                                                 case 'alt':
2217                                                 case 'ctrl':
2218                                                 case 'shift':
2219                                                         o[v] = true;
2220                                                         break;
2221
2222                                                 default:
2223                                                         o.charCode = v.charCodeAt(0);
2224                                                         o.keyCode = v.toUpperCase().charCodeAt(0);
2225                                         }
2226                                 });
2227
2228                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
2229                         });
2230
2231                         return true;
2232                 },
2233
2234                 /**
2235                  * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
2236                  * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
2237                  * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
2238                  * return true it will handle the command as a internal browser command.
2239                  *
2240                  * @method execCommand
2241                  * @param {String} cmd Command name to execute, for example mceLink or Bold.
2242                  * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
2243                  * @param {mixed} val Optional command value, this can be anything.
2244                  * @param {Object} a Optional arguments object.
2245                  * @return {Boolean} True/false if the command was executed or not.
2246                  */
2247                 execCommand : function(cmd, ui, val, a) {
2248                         var t = this, s = 0, o, st;
2249
2250                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
2251                                 t.focus();
2252
2253                         o = {};
2254                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
2255                         if (o.terminate)
2256                                 return false;
2257
2258                         // Command callback
2259                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
2260                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);
2261                                 return true;
2262                         }
2263
2264                         // Registred commands
2265                         if (o = t.execCommands[cmd]) {
2266                                 st = o.func.call(o.scope, ui, val);
2267
2268                                 // Fall through on true
2269                                 if (st !== true) {
2270                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);
2271                                         return st;
2272                                 }
2273                         }
2274
2275                         // Plugin commands
2276                         each(t.plugins, function(p) {
2277                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {
2278                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);
2279                                         s = 1;
2280                                         return false;
2281                                 }
2282                         });
2283
2284                         if (s)
2285                                 return true;
2286
2287                         // Theme commands
2288                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
2289                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);
2290                                 return true;
2291                         }
2292
2293                         // Editor commands
2294                         if (t.editorCommands.execCommand(cmd, ui, val)) {
2295                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);
2296                                 return true;
2297                         }
2298
2299                         // Browser commands
2300                         t.getDoc().execCommand(cmd, ui, val);
2301                         t.onExecCommand.dispatch(t, cmd, ui, val, a);
2302                 },
2303
2304                 /**
2305                  * Returns a command specific state, for example if bold is enabled or not.
2306                  *
2307                  * @method queryCommandState
2308                  * @param {string} cmd Command to query state from.
2309                  * @return {Boolean} Command specific state, for example if bold is enabled or not.
2310                  */
2311                 queryCommandState : function(cmd) {
2312                         var t = this, o, s;
2313
2314                         // Is hidden then return undefined
2315                         if (t._isHidden())
2316                                 return;
2317
2318                         // Registred commands
2319                         if (o = t.queryStateCommands[cmd]) {
2320                                 s = o.func.call(o.scope);
2321
2322                                 // Fall though on true
2323                                 if (s !== true)
2324                                         return s;
2325                         }
2326
2327                         // Registred commands
2328                         o = t.editorCommands.queryCommandState(cmd);
2329                         if (o !== -1)
2330                                 return o;
2331
2332                         // Browser commands
2333                         try {
2334                                 return this.getDoc().queryCommandState(cmd);
2335                         } catch (ex) {
2336                                 // Fails sometimes see bug: 1896577
2337                         }
2338                 },
2339
2340                 /**
2341                  * Returns a command specific value, for example the current font size.
2342                  *
2343                  * @method queryCommandValue
2344                  * @param {string} c Command to query value from.
2345                  * @return {Object} Command specific value, for example the current font size.
2346                  */
2347                 queryCommandValue : function(c) {
2348                         var t = this, o, s;
2349
2350                         // Is hidden then return undefined
2351                         if (t._isHidden())
2352                                 return;
2353
2354                         // Registred commands
2355                         if (o = t.queryValueCommands[c]) {
2356                                 s = o.func.call(o.scope);
2357
2358                                 // Fall though on true
2359                                 if (s !== true)
2360                                         return s;
2361                         }
2362
2363                         // Registred commands
2364                         o = t.editorCommands.queryCommandValue(c);
2365                         if (is(o))
2366                                 return o;
2367
2368                         // Browser commands
2369                         try {
2370                                 return this.getDoc().queryCommandValue(c);
2371                         } catch (ex) {
2372                                 // Fails sometimes see bug: 1896577
2373                         }
2374                 },
2375
2376                 /**
2377                  * Shows the editor and hides any textarea/div that the editor is supposed to replace.
2378                  *
2379                  * @method show
2380                  */
2381                 show : function() {
2382                         var t = this;
2383
2384                         DOM.show(t.getContainer());
2385                         DOM.hide(t.id);
2386                         t.load();
2387                 },
2388
2389                 /**
2390                  * Hides the editor and shows any textarea/div that the editor is supposed to replace.
2391                  *
2392                  * @method hide
2393                  */
2394                 hide : function() {
2395                         var t = this, d = t.getDoc();
2396
2397                         // Fixed bug where IE has a blinking cursor left from the editor
2398                         if (isIE && d)
2399                                 d.execCommand('SelectAll');
2400
2401                         // We must save before we hide so Safari doesn't crash
2402                         t.save();
2403                         DOM.hide(t.getContainer());
2404                         DOM.setStyle(t.id, 'display', t.orgDisplay);
2405                 },
2406
2407                 /**
2408                  * Returns true/false if the editor is hidden or not.
2409                  *
2410                  * @method isHidden
2411                  * @return {Boolean} True/false if the editor is hidden or not.
2412                  */
2413                 isHidden : function() {
2414                         return !DOM.isHidden(this.id);
2415                 },
2416
2417                 /**
2418                  * Sets the progress state, this will display a throbber/progess for the editor.
2419                  * This is ideal for asycronous operations like an AJAX save call.
2420                  *
2421                  * @method setProgressState
2422                  * @param {Boolean} b Boolean state if the progress should be shown or hidden.
2423                  * @param {Number} ti Optional time to wait before the progress gets shown.
2424                  * @param {Object} o Optional object to pass to the progress observers.
2425                  * @return {Boolean} Same as the input state.
2426                  * @example
2427                  * // Show progress for the active editor
2428                  * tinyMCE.activeEditor.setProgressState(true);
2429                  * 
2430                  * // Hide progress for the active editor
2431                  * tinyMCE.activeEditor.setProgressState(false);
2432                  * 
2433                  * // Show progress after 3 seconds
2434                  * tinyMCE.activeEditor.setProgressState(true, 3000);
2435                  */
2436                 setProgressState : function(b, ti, o) {
2437                         this.onSetProgressState.dispatch(this, b, ti, o);
2438
2439                         return b;
2440                 },
2441
2442                 /**
2443                  * Loads contents from the textarea or div element that got converted into an editor instance.
2444                  * This method will move the contents from that textarea or div into the editor by using setContent
2445                  * so all events etc that method has will get dispatched as well.
2446                  *
2447                  * @method load
2448                  * @param {Object} o Optional content object, this gets passed around through the whole load process.
2449                  * @return {String} HTML string that got set into the editor.
2450                  */
2451                 load : function(o) {
2452                         var t = this, e = t.getElement(), h;
2453
2454                         if (e) {
2455                                 o = o || {};
2456                                 o.load = true;
2457
2458                                 // Double encode existing entities in the value
2459                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
2460                                 o.element = e;
2461
2462                                 if (!o.no_events)
2463                                         t.onLoadContent.dispatch(t, o);
2464
2465                                 o.element = e = null;
2466
2467                                 return h;
2468                         }
2469                 },
2470
2471                 /**
2472                  * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
2473                  * This method will move the HTML contents from the editor into that textarea or div by getContent
2474                  * so all events etc that method has will get dispatched as well.
2475                  *
2476                  * @method save
2477                  * @param {Object} o Optional content object, this gets passed around through the whole save process.
2478                  * @return {String} HTML string that got set into the textarea/div.
2479                  */
2480                 save : function(o) {
2481                         var t = this, e = t.getElement(), h, f;
2482
2483                         if (!e || !t.initialized)
2484                                 return;
2485
2486                         o = o || {};
2487                         o.save = true;
2488
2489                         // Add undo level will trigger onchange event
2490                         if (!o.no_events) {
2491                                 t.undoManager.typing = false;
2492                                 t.undoManager.add();
2493                         }
2494
2495                         o.element = e;
2496                         h = o.content = t.getContent(o);
2497
2498                         if (!o.no_events)
2499                                 t.onSaveContent.dispatch(t, o);
2500
2501                         h = o.content;
2502
2503                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
2504                                 e.innerHTML = h;
2505
2506                                 // Update hidden form element
2507                                 if (f = DOM.getParent(t.id, 'form')) {
2508                                         each(f.elements, function(e) {
2509                                                 if (e.name == t.id) {
2510                                                         e.value = h;
2511                                                         return false;
2512                                                 }
2513                                         });
2514                                 }
2515                         } else
2516                                 e.value = h;
2517
2518                         o.element = e = null;
2519
2520                         return h;
2521                 },
2522
2523                 /**
2524                  * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
2525                  * the different cleanup rules options.
2526                  *
2527                  * @method setContent
2528                  * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
2529                  * @param {Object} args Optional content object, this gets passed around through the whole set process.
2530                  * @return {String} HTML string that got set into the editor.
2531                  * @example
2532                  * // Sets the HTML contents of the activeEditor editor
2533                  * tinyMCE.activeEditor.setContent('<span>some</span> html');
2534                  * 
2535                  * // Sets the raw contents of the activeEditor editor
2536                  * tinyMCE.activeEditor.setContent('<span>some</span> html', {format : 'raw'});
2537                  * 
2538                  * // Sets the content of a specific editor (my_editor in this example)
2539                  * tinyMCE.get('my_editor').setContent(data);
2540                  * 
2541                  * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
2542                  * tinyMCE.activeEditor.setContent('[b]some[/b] html', {format : 'bbcode'});
2543                  */
2544                 setContent : function(content, args) {
2545                         var self = this, rootNode, body = self.getBody(), forcedRootBlockName;
2546
2547                         // Setup args object
2548                         args = args || {};
2549                         args.format = args.format || 'html';
2550                         args.set = true;
2551                         args.content = content;
2552
2553                         // Do preprocessing
2554                         if (!args.no_events)
2555                                 self.onBeforeSetContent.dispatch(self, args);
2556
2557                         content = args.content;
2558
2559                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
2560                         // It will also be impossible to place the caret in the editor unless there is a BR element present
2561                         if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {
2562                                 forcedRootBlockName = self.settings.forced_root_block;
2563                                 if (forcedRootBlockName)
2564                                         content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';
2565                                 else
2566                                         content = '<br data-mce-bogus="1">';
2567
2568                                 body.innerHTML = content;
2569                                 self.selection.select(body, true);
2570                                 self.selection.collapse(true);
2571                                 return;
2572                         }
2573
2574                         // Parse and serialize the html
2575                         if (args.format !== 'raw') {
2576                                 content = new tinymce.html.Serializer({}, self.schema).serialize(
2577                                         self.parser.parse(content)
2578                                 );
2579                         }
2580
2581                         // Set the new cleaned contents to the editor
2582                         args.content = tinymce.trim(content);
2583                         self.dom.setHTML(body, args.content);
2584
2585                         // Do post processing
2586                         if (!args.no_events)
2587                                 self.onSetContent.dispatch(self, args);
2588
2589                         self.selection.normalize();
2590
2591                         return args.content;
2592                 },
2593
2594                 /**
2595                  * Gets the content from the editor instance, this will cleanup the content before it gets returned using
2596                  * the different cleanup rules options.
2597                  *
2598                  * @method getContent
2599                  * @param {Object} args Optional content object, this gets passed around through the whole get process.
2600                  * @return {String} Cleaned content string, normally HTML contents.
2601                  * @example
2602                  * // Get the HTML contents of the currently active editor
2603                  * console.debug(tinyMCE.activeEditor.getContent());
2604                  * 
2605                  * // Get the raw contents of the currently active editor
2606                  * tinyMCE.activeEditor.getContent({format : 'raw'});
2607                  * 
2608                  * // Get content of a specific editor:
2609                  * tinyMCE.get('content id').getContent()
2610                  */
2611                 getContent : function(args) {
2612                         var self = this, content;
2613
2614                         // Setup args object
2615                         args = args || {};
2616                         args.format = args.format || 'html';
2617                         args.get = true;
2618
2619                         // Do preprocessing
2620                         if (!args.no_events)
2621                                 self.onBeforeGetContent.dispatch(self, args);
2622
2623                         // Get raw contents or by default the cleaned contents
2624                         if (args.format == 'raw')
2625                                 content = self.getBody().innerHTML;
2626                         else
2627                                 content = self.serializer.serialize(self.getBody(), args);
2628
2629                         args.content = tinymce.trim(content);
2630
2631                         // Do post processing
2632                         if (!args.no_events)
2633                                 self.onGetContent.dispatch(self, args);
2634
2635                         return args.content;
2636                 },
2637
2638                 /**
2639                  * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
2640                  *
2641                  * @method isDirty
2642                  * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
2643                  * @example
2644                  * if (tinyMCE.activeEditor.isDirty())
2645                  *     alert("You must save your contents.");
2646                  */
2647                 isDirty : function() {
2648                         var self = this;
2649
2650                         return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;
2651                 },
2652
2653                 /**
2654                  * Returns the editors container element. The container element wrappes in
2655                  * all the elements added to the page for the editor. Such as UI, iframe etc.
2656                  *
2657                  * @method getContainer
2658                  * @return {Element} HTML DOM element for the editor container.
2659                  */
2660                 getContainer : function() {
2661                         var t = this;
2662
2663                         if (!t.container)
2664                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');
2665
2666                         return t.container;
2667                 },
2668
2669                 /**
2670                  * Returns the editors content area container element. The this element is the one who
2671                  * holds the iframe or the editable element.
2672                  *
2673                  * @method getContentAreaContainer
2674                  * @return {Element} HTML DOM element for the editor area container.
2675                  */
2676                 getContentAreaContainer : function() {
2677                         return this.contentAreaContainer;
2678                 },
2679
2680                 /**
2681                  * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
2682                  *
2683                  * @method getElement
2684                  * @return {Element} HTML DOM element for the replaced element.
2685                  */
2686                 getElement : function() {
2687                         return DOM.get(this.settings.content_element || this.id);
2688                 },
2689
2690                 /**
2691                  * Returns the iframes window object.
2692                  *
2693                  * @method getWin
2694                  * @return {Window} Iframe DOM window object.
2695                  */
2696                 getWin : function() {
2697                         var t = this, e;
2698
2699                         if (!t.contentWindow) {
2700                                 e = DOM.get(t.id + "_ifr");
2701
2702                                 if (e)
2703                                         t.contentWindow = e.contentWindow;
2704                         }
2705
2706                         return t.contentWindow;
2707                 },
2708
2709                 /**
2710                  * Returns the iframes document object.
2711                  *
2712                  * @method getDoc
2713                  * @return {Document} Iframe DOM document object.
2714                  */
2715                 getDoc : function() {
2716                         var t = this, w;
2717
2718                         if (!t.contentDocument) {
2719                                 w = t.getWin();
2720
2721                                 if (w)
2722                                         t.contentDocument = w.document;
2723                         }
2724
2725                         return t.contentDocument;
2726                 },
2727
2728                 /**
2729                  * Returns the iframes body element.
2730                  *
2731                  * @method getBody
2732                  * @return {Element} Iframe body element.
2733                  */
2734                 getBody : function() {
2735                         return this.bodyElement || this.getDoc().body;
2736                 },
2737
2738                 /**
2739                  * URL converter function this gets executed each time a user adds an img, a or
2740                  * any other element that has a URL in it. This will be called both by the DOM and HTML
2741                  * manipulation functions.
2742                  *
2743                  * @method convertURL
2744                  * @param {string} u URL to convert.
2745                  * @param {string} n Attribute name src, href etc.
2746                  * @param {string/HTMLElement} Tag name or HTML DOM element depending on HTML or DOM insert.
2747                  * @return {string} Converted URL string.
2748                  */
2749                 convertURL : function(u, n, e) {
2750                         var t = this, s = t.settings;
2751
2752                         // Use callback instead
2753                         if (s.urlconverter_callback)
2754                                 return t.execCallback('urlconverter_callback', u, e, true, n);
2755
2756                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
2757                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
2758                                 return u;
2759
2760                         // Convert to relative
2761                         if (s.relative_urls)
2762                                 return t.documentBaseURI.toRelative(u);
2763
2764                         // Convert to absolute
2765                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
2766
2767                         return u;
2768                 },
2769
2770                 /**
2771                  * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
2772                  *
2773                  * @method addVisual
2774                  * @param {Element} e Optional root element to loop though to find tables etc that needs the visual aid.
2775                  */
2776                 addVisual : function(e) {
2777                         var t = this, s = t.settings;
2778
2779                         e = e || t.getBody();
2780
2781                         if (!is(t.hasVisual))
2782                                 t.hasVisual = s.visual;
2783
2784                         each(t.dom.select('table,a', e), function(e) {
2785                                 var v;
2786
2787                                 switch (e.nodeName) {
2788                                         case 'TABLE':
2789                                                 v = t.dom.getAttrib(e, 'border');
2790
2791                                                 if (!v || v == '0') {
2792                                                         if (t.hasVisual)
2793                                                                 t.dom.addClass(e, s.visual_table_class);
2794                                                         else
2795                                                                 t.dom.removeClass(e, s.visual_table_class);
2796                                                 }
2797
2798                                                 return;
2799
2800                                         case 'A':
2801                                                 v = t.dom.getAttrib(e, 'name');
2802
2803                                                 if (v) {
2804                                                         if (t.hasVisual)
2805                                                                 t.dom.addClass(e, 'mceItemAnchor');
2806                                                         else
2807                                                                 t.dom.removeClass(e, 'mceItemAnchor');
2808                                                 }
2809
2810                                                 return;
2811                                 }
2812                         });
2813
2814                         t.onVisualAid.dispatch(t, e, t.hasVisual);
2815                 },
2816
2817                 /**
2818                  * Removes the editor from the dom and tinymce collection.
2819                  *
2820                  * @method remove
2821                  */
2822                 remove : function() {
2823                         var t = this, e = t.getContainer();
2824
2825                         t.removed = 1; // Cancels post remove event execution
2826                         t.hide();
2827
2828                         t.execCallback('remove_instance_callback', t);
2829                         t.onRemove.dispatch(t);
2830
2831                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
2832                         t.onExecCommand.listeners = [];
2833
2834                         tinymce.remove(t);
2835                         DOM.remove(e);
2836                 },
2837
2838                 /**
2839                  * Destroys the editor instance by removing all events, element references or other resources
2840                  * that could leak memory. This method will be called automatically when the page is unloaded
2841                  * but you can also call it directly if you know what you are doing.
2842                  *
2843                  * @method destroy
2844                  * @param {Boolean} s Optional state if the destroy is an automatic destroy or user called one.
2845                  */
2846                 destroy : function(s) {
2847                         var t = this;
2848
2849                         // One time is enough
2850                         if (t.destroyed)
2851                                 return;
2852
2853                         if (!s) {
2854                                 tinymce.removeUnload(t.destroy);
2855                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
2856
2857                                 // Manual destroy
2858                                 if (t.theme && t.theme.destroy)
2859                                         t.theme.destroy();
2860
2861                                 // Destroy controls, selection and dom
2862                                 t.controlManager.destroy();
2863                                 t.selection.destroy();
2864                                 t.dom.destroy();
2865
2866                                 // Remove all events
2867
2868                                 // Don't clear the window or document if content editable
2869                                 // is enabled since other instances might still be present
2870                                 if (!t.settings.content_editable) {
2871                                         Event.clear(t.getWin());
2872                                         Event.clear(t.getDoc());
2873                                 }
2874
2875                                 Event.clear(t.getBody());
2876                                 Event.clear(t.formElement);
2877                         }
2878
2879                         if (t.formElement) {
2880                                 t.formElement.submit = t.formElement._mceOldSubmit;
2881                                 t.formElement._mceOldSubmit = null;
2882                         }
2883
2884                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
2885
2886                         if (t.selection)
2887                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
2888
2889                         t.destroyed = 1;
2890                 },
2891
2892                 // Internal functions
2893
2894                 _addEvents : function() {
2895                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
2896                         var t = this, i, s = t.settings, dom = t.dom, lo = {
2897                                 mouseup : 'onMouseUp',
2898                                 mousedown : 'onMouseDown',
2899                                 click : 'onClick',
2900                                 keyup : 'onKeyUp',
2901                                 keydown : 'onKeyDown',
2902                                 keypress : 'onKeyPress',
2903                                 submit : 'onSubmit',
2904                                 reset : 'onReset',
2905                                 contextmenu : 'onContextMenu',
2906                                 dblclick : 'onDblClick',
2907                                 paste : 'onPaste' // Doesn't work in all browsers yet
2908                         };
2909
2910                         function eventHandler(e, o) {
2911                                 var ty = e.type;
2912
2913                                 // Don't fire events when it's removed
2914                                 if (t.removed)
2915                                         return;
2916
2917                                 // Generic event handler
2918                                 if (t.onEvent.dispatch(t, e, o) !== false) {
2919                                         // Specific event handler
2920                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);
2921                                 }
2922                         };
2923
2924                         // Add DOM events
2925                         each(lo, function(v, k) {
2926                                 switch (k) {
2927                                         case 'contextmenu':
2928                                                 dom.bind(t.getDoc(), k, eventHandler);
2929                                                 break;
2930
2931                                         case 'paste':
2932                                                 dom.bind(t.getBody(), k, function(e) {
2933                                                         eventHandler(e);
2934                                                 });
2935                                                 break;
2936
2937                                         case 'submit':
2938                                         case 'reset':
2939                                                 dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
2940                                                 break;
2941
2942                                         default:
2943                                                 dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
2944                                 }
2945                         });
2946
2947                         dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
2948                                 t.focus(true);
2949                         });
2950
2951                         // #ifdef contentEditable
2952
2953                         if (s.content_editable && tinymce.isOpera) {
2954                                 // Opera doesn't support focus event for contentEditable elements so we need to fake it
2955                                 function doFocus(e) {
2956                                         t.focus(true);
2957                                 };
2958
2959                                 dom.bind(t.getBody(), 'click', doFocus);
2960                                 dom.bind(t.getBody(), 'keydown', doFocus);
2961                         }
2962
2963                         // #endif
2964
2965                         // Fixes bug where a specified document_base_uri could result in broken images
2966                         // This will also fix drag drop of images in Gecko
2967                         if (tinymce.isGecko) {
2968                                 dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {
2969                                         var v;
2970
2971                                         e = e.target;
2972
2973                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))
2974                                                 e.src = t.documentBaseURI.toAbsolute(v);
2975                                 });
2976                         }
2977
2978                         // Set various midas options in Gecko
2979                         if (isGecko) {
2980                                 function setOpts() {
2981                                         var t = this, d = t.getDoc(), s = t.settings;
2982
2983                                         if (isGecko && !s.readonly) {
2984                                                 if (t._isHidden()) {
2985                                                         try {
2986                                                                 if (!s.content_editable) {
2987                                                                         d.body.contentEditable = false;
2988                                                                         d.body.contentEditable = true;
2989                                                                 }
2990                                                         } catch (ex) {
2991                                                                 // Fails if it's hidden
2992                                                         }
2993                                                 }
2994
2995                                                 try {
2996                                                         // Try new Gecko method
2997                                                         d.execCommand("styleWithCSS", 0, false);
2998                                                 } catch (ex) {
2999                                                         // Use old method
3000                                                         if (!t._isHidden())
3001                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}
3002                                                 }
3003
3004                                                 if (!s.table_inline_editing)
3005                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
3006
3007                                                 if (!s.object_resizing)
3008                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
3009                                         }
3010                                 };
3011
3012                                 t.onBeforeExecCommand.add(setOpts);
3013                                 t.onMouseDown.add(setOpts);
3014                         }
3015
3016                         t.onClick.add(function(ed, e) {
3017                                 e = e.target;
3018
3019                                 // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
3020                                 // WebKit can't even do simple things like selecting an image
3021                                 // Needs tobe the setBaseAndExtend or it will fail to select floated images
3022                                 if (tinymce.isWebKit && e.nodeName == 'IMG')
3023                                         t.selection.getSel().setBaseAndExtent(e, 0, e, 1);
3024
3025                                 if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))
3026                                         t.selection.select(e);
3027
3028                                 t.nodeChanged();
3029                         });
3030
3031                         // Add node change handlers
3032                         t.onMouseUp.add(t.nodeChanged);
3033                         //t.onClick.add(t.nodeChanged);
3034                         t.onKeyUp.add(function(ed, e) {
3035                                 var c = e.keyCode;
3036
3037                                 if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey)
3038                                         t.nodeChanged();
3039                         });
3040
3041
3042                         // Add block quote deletion handler
3043                         t.onKeyDown.add(function(ed, e) {
3044                                 // Was the BACKSPACE key pressed?
3045                                 if (e.keyCode != 8)
3046                                         return;
3047
3048                                 var n = ed.selection.getRng().startContainer;
3049                                 var offset = ed.selection.getRng().startOffset;
3050
3051                                 while (n && n.nodeType && n.nodeType != 1 && n.parentNode)
3052                                         n = n.parentNode;
3053                                         
3054                                 // Is the cursor at the beginning of a blockquote?
3055                                 if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {
3056                                         // Remove the blockquote
3057                                         ed.formatter.toggle('blockquote', null, n.parentNode);
3058
3059                                         // Move the caret to the beginning of n
3060                                         var rng = ed.selection.getRng();
3061                                         rng.setStart(n, 0);
3062                                         rng.setEnd(n, 0);
3063                                         ed.selection.setRng(rng);
3064                                         ed.selection.collapse(false);
3065                                 }
3066                         });
3067  
3068
3069
3070                         // Add reset handler
3071                         t.onReset.add(function() {
3072                                 t.setContent(t.startContent, {format : 'raw'});
3073                         });
3074
3075                         // Add shortcuts
3076                         if (s.custom_shortcuts) {
3077                                 if (s.custom_undo_redo_keyboard_shortcuts) {
3078                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
3079                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
3080                                 }
3081
3082                                 // Add default shortcuts for gecko
3083                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
3084                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
3085                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
3086
3087                                 // BlockFormat shortcuts keys
3088                                 for (i=1; i<=6; i++)
3089                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
3090
3091                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
3092                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
3093                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
3094
3095                                 function find(e) {
3096                                         var v = null;
3097
3098                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)
3099                                                 return v;
3100
3101                                         each(t.shortcuts, function(o) {
3102                                                 if (tinymce.isMac && o.ctrl != e.metaKey)
3103                                                         return;
3104                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
3105                                                         return;
3106
3107                                                 if (o.alt != e.altKey)
3108                                                         return;
3109
3110                                                 if (o.shift != e.shiftKey)
3111                                                         return;
3112
3113                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
3114                                                         v = o;
3115                                                         return false;
3116                                                 }
3117                                         });
3118
3119                                         return v;
3120                                 };
3121
3122                                 t.onKeyUp.add(function(ed, e) {
3123                                         var o = find(e);
3124
3125                                         if (o)
3126                                                 return Event.cancel(e);
3127                                 });
3128
3129                                 t.onKeyPress.add(function(ed, e) {
3130                                         var o = find(e);
3131
3132                                         if (o)
3133                                                 return Event.cancel(e);
3134                                 });
3135
3136                                 t.onKeyDown.add(function(ed, e) {
3137                                         var o = find(e);
3138
3139                                         if (o) {
3140                                                 o.func.call(o.scope);
3141                                                 return Event.cancel(e);
3142                                         }
3143                                 });
3144                         }
3145
3146                         if (tinymce.isIE) {
3147                                 // Fix so resize will only update the width and height attributes not the styles of an image
3148                                 // It will also block mceItemNoResize items
3149                                 dom.bind(t.getDoc(), 'controlselect', function(e) {
3150                                         var re = t.resizeInfo, cb;
3151
3152                                         e = e.target;
3153
3154                                         // Don't do this action for non image elements
3155                                         if (e.nodeName !== 'IMG')
3156                                                 return;
3157
3158                                         if (re)
3159                                                 dom.unbind(re.node, re.ev, re.cb);
3160
3161                                         if (!dom.hasClass(e, 'mceItemNoResize')) {
3162                                                 ev = 'resizeend';
3163                                                 cb = dom.bind(e, ev, function(e) {
3164                                                         var v;
3165
3166                                                         e = e.target;
3167
3168                                                         if (v = dom.getStyle(e, 'width')) {
3169                                                                 dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
3170                                                                 dom.setStyle(e, 'width', '');
3171                                                         }
3172
3173                                                         if (v = dom.getStyle(e, 'height')) {
3174                                                                 dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
3175                                                                 dom.setStyle(e, 'height', '');
3176                                                         }
3177                                                 });
3178                                         } else {
3179                                                 ev = 'resizestart';
3180                                                 cb = dom.bind(e, 'resizestart', Event.cancel, Event);
3181                                         }
3182
3183                                         re = t.resizeInfo = {
3184                                                 node : e,
3185                                                 ev : ev,
3186                                                 cb : cb
3187                                         };
3188                                 });
3189                         }
3190
3191                         if (tinymce.isOpera) {
3192                                 t.onClick.add(function(ed, e) {
3193                                         Event.prevent(e);
3194                                 });
3195                         }
3196
3197                         // Add custom undo/redo handlers
3198                         if (s.custom_undo_redo) {
3199                                 function addUndo() {
3200                                         t.undoManager.typing = false;
3201                                         t.undoManager.add();
3202                                 };
3203
3204                                 dom.bind(t.getDoc(), 'focusout', function(e) {
3205                                         if (!t.removed && t.undoManager.typing)
3206                                                 addUndo();
3207                                 });
3208
3209                                 // Add undo level when contents is drag/dropped within the editor
3210                                 t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {
3211                                         addUndo();
3212                                 });
3213
3214                                 t.onKeyUp.add(function(ed, e) {
3215                                         var keyCode = e.keyCode;
3216
3217                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || e.ctrlKey)
3218                                                 addUndo();
3219                                 });
3220
3221                                 t.onKeyDown.add(function(ed, e) {
3222                                         var keyCode = e.keyCode, sel;
3223
3224                                         if (keyCode == 8) {
3225                                                 sel = t.getDoc().selection;
3226
3227                                                 // Fix IE control + backspace browser bug
3228                                                 if (sel && sel.createRange && sel.createRange().item) {
3229                                                         t.undoManager.beforeChange();
3230                                                         ed.dom.remove(sel.createRange().item(0));
3231                                                         addUndo();
3232
3233                                                         return Event.cancel(e);
3234                                                 }
3235                                         }
3236
3237                                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
3238                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {
3239                                                 // Add position before enter key is pressed, used by IE since it still uses the default browser behavior
3240                                                 // Todo: Remove this once we normalize enter behavior on IE
3241                                                 if (tinymce.isIE && keyCode == 13)
3242                                                         t.undoManager.beforeChange();
3243
3244                                                 if (t.undoManager.typing)
3245                                                         addUndo();
3246
3247                                                 return;
3248                                         }
3249
3250                                         // If key isn't shift,ctrl,alt,capslock,metakey
3251                                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {
3252                                                 t.undoManager.beforeChange();
3253                                                 t.undoManager.typing = true;
3254                                                 t.undoManager.add();
3255                                         }
3256                                 });
3257
3258                                 t.onMouseDown.add(function() {
3259                                         if (t.undoManager.typing)
3260                                                 addUndo();
3261                                 });
3262                         }
3263
3264                         // Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5
3265                         // It only fires the nodeChange event every 50ms since it would other wise update the UI when you type and it hogs the CPU
3266                         if (tinymce.isWebKit) {
3267                                 dom.bind(t.getDoc(), 'selectionchange', function() {
3268                                         if (t.selectionTimer) {
3269                                                 window.clearTimeout(t.selectionTimer);
3270                                                 t.selectionTimer = 0;
3271                                         }
3272
3273                                         t.selectionTimer = window.setTimeout(function() {
3274                                                 t.nodeChanged();
3275                                         }, 50);
3276                                 });
3277                         }
3278
3279                         // Bug fix for FireFox keeping styles from end of selection instead of start.
3280                         if (tinymce.isGecko) {
3281                                 function getAttributeApplyFunction() {
3282                                         var template = t.dom.getAttribs(t.selection.getStart().cloneNode(false));
3283
3284                                         return function() {
3285                                                 var target = t.selection.getStart();
3286
3287                                                 if (target !== t.getBody()) {
3288                                                         t.dom.removeAllAttribs(target);
3289
3290                                                         each(template, function(attr) {
3291                                                                 target.setAttributeNode(attr.cloneNode(true));
3292                                                         });
3293                                                 }
3294                                         };
3295                                 }
3296
3297                                 function isSelectionAcrossElements() {
3298                                         var s = t.selection;
3299
3300                                         return !s.isCollapsed() && s.getStart() != s.getEnd();
3301                                 }
3302
3303                                 t.onKeyPress.add(function(ed, e) {
3304                                         var applyAttributes;
3305
3306                                         if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
3307                                                 applyAttributes = getAttributeApplyFunction();
3308                                                 t.getDoc().execCommand('delete', false, null);
3309                                                 applyAttributes();
3310
3311                                                 return Event.cancel(e);
3312                                         }
3313                                 });
3314
3315                                 t.dom.bind(t.getDoc(), 'cut', function(e) {
3316                                         var applyAttributes;
3317
3318                                         if (isSelectionAcrossElements()) {
3319                                                 applyAttributes = getAttributeApplyFunction();
3320                                                 t.onKeyUp.addToTop(Event.cancel, Event);
3321
3322                                                 setTimeout(function() {
3323                                                         applyAttributes();
3324                                                         t.onKeyUp.remove(Event.cancel, Event);
3325                                                 }, 0);
3326                                         }
3327                                 });
3328                         }
3329                 },
3330
3331                 _isHidden : function() {
3332                         var s;
3333
3334                         if (!isGecko)
3335                                 return 0;
3336
3337                         // Weird, wheres that cursor selection?
3338                         s = this.selection.getSel();
3339                         return (!s || !s.rangeCount || s.rangeCount == 0);
3340                 }
3341         });
3342 })(tinymce);