]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/Editor.js
Release 6.2.2
[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, then skip initialization. We need to sniff here since the
893                         // browser says it has contentEditable support but there is no visible caret
894                         // We will remove this check ones Apple implements full contentEditable support
895                         if (tinymce.isIDevice)
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 && p.charAt(0) != '-' && !PluginManager.urls[p]) {
987                                                 // Skip safari plugin, since it is removed as of 3.3b1
988                                                 if (p == 'safari')
989                                                         return;
990
991                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
992                                         }
993                                 });
994
995                                 // Init when que is loaded
996                                 sl.loadQueue(function() {
997                                         if (!t.removed)
998                                                 t.init();
999                                 });
1000                         };
1001
1002                         loadScripts();
1003                 },
1004
1005                 /**
1006                  * Initializes the editor this will be called automatically when
1007                  * all plugins/themes and language packs are loaded by the rendered method.
1008                  * This method will setup the iframe and create the theme and plugin instances.
1009                  *
1010                  * @method init
1011                  */
1012                 init : function() {
1013                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i;
1014
1015                         tinymce.add(t);
1016
1017                         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));
1018
1019                         /**
1020                          * Reference to the theme instance that was used to generate the UI. 
1021                          *
1022                          * @property theme
1023                          * @type tinymce.Theme
1024                          * @example
1025                          * // Executes a method on the theme directly
1026                          * tinyMCE.activeEditor.theme.someMethod();
1027                          */
1028                         if (s.theme) {
1029                                 s.theme = s.theme.replace(/-/, '');
1030                                 o = ThemeManager.get(s.theme);
1031                                 t.theme = new o();
1032
1033                                 if (t.theme.init && s.init_theme)
1034                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
1035                         }
1036
1037                         // Create all plugins
1038                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {
1039                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
1040
1041                                 if (c) {
1042                                         po = new c(t, u);
1043
1044                                         t.plugins[p] = po;
1045
1046                                         if (po.init)
1047                                                 po.init(t, u);
1048                                 }
1049                         });
1050
1051                         // Setup popup CSS path(s)
1052                         if (s.popup_css !== false) {
1053                                 if (s.popup_css)
1054                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
1055                                 else
1056                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
1057                         }
1058
1059                         if (s.popup_css_add)
1060                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
1061
1062                         /**
1063                          * Control manager instance for the editor. Will enables you to create new UI elements and change their states etc.
1064                          *
1065                          * @property controlManager
1066                          * @type tinymce.ControlManager
1067                          * @example
1068                          * // Disables the bold button
1069                          * tinyMCE.activeEditor.controlManager.setDisabled('bold', true);
1070                          */
1071                         t.controlManager = new tinymce.ControlManager(t);
1072
1073                         if (s.custom_undo_redo) {
1074                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {
1075                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
1076                                                 t.undoManager.beforeChange();
1077                                 });
1078
1079                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {
1080                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
1081                                                 t.undoManager.add();
1082                                 });
1083                         }
1084
1085                         t.onExecCommand.add(function(ed, c) {
1086                                 // Don't refresh the select lists until caret move
1087                                 if (!/^(FontName|FontSize)$/.test(c))
1088                                         t.nodeChanged();
1089                         });
1090
1091                         // Remove ghost selections on images and tables in Gecko
1092                         if (isGecko) {
1093                                 function repaint(a, o) {
1094                                         if (!o || !o.initial)
1095                                                 t.execCommand('mceRepaint');
1096                                 };
1097
1098                                 t.onUndo.add(repaint);
1099                                 t.onRedo.add(repaint);
1100                                 t.onSetContent.add(repaint);
1101                         }
1102
1103                         // Enables users to override the control factory
1104                         t.onBeforeRenderUI.dispatch(t, t.controlManager);
1105
1106                         // Measure box
1107                         if (s.render_ui) {
1108                                 w = s.width || e.style.width || e.offsetWidth;
1109                                 h = s.height || e.style.height || e.offsetHeight;
1110                                 t.orgDisplay = e.style.display;
1111                                 re = /^[0-9\.]+(|px)$/i;
1112
1113                                 if (re.test('' + w))
1114                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
1115
1116                                 if (re.test('' + h))
1117                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
1118
1119                                 // Render UI
1120                                 o = t.theme.renderUI({
1121                                         targetNode : e,
1122                                         width : w,
1123                                         height : h,
1124                                         deltaWidth : s.delta_width,
1125                                         deltaHeight : s.delta_height
1126                                 });
1127
1128                                 t.editorContainer = o.editorContainer;
1129                         }
1130
1131                         // #ifdef contentEditable
1132
1133                         // Content editable mode ends here
1134                         if (s.content_editable) {
1135                                 e = n = o = null; // Fix IE leak
1136                                 return t.setupContentEditable();
1137                         }
1138
1139                         // #endif
1140
1141                         // User specified a document.domain value
1142                         if (document.domain && location.hostname != document.domain)
1143                                 tinymce.relaxedDomain = document.domain;
1144
1145                         // Resize editor
1146                         DOM.setStyles(o.sizeContainer || o.editorContainer, {
1147                                 width : w,
1148                                 height : h
1149                         });
1150
1151                         // Load specified content CSS last
1152                         if (s.content_css) {
1153                                 tinymce.each(explode(s.content_css), function(u) {
1154                                         t.contentCSS.push(t.documentBaseURI.toAbsolute(u));
1155                                 });
1156                         }
1157
1158                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
1159                         if (h < 100)
1160                                 h = 100;
1161
1162                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
1163
1164                         // We only need to override paths if we have to
1165                         // IE has a bug where it remove site absolute urls to relative ones if this is specified
1166                         if (s.document_base_url != tinymce.documentBaseURL)
1167                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';
1168
1169                         // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
1170                         if (s.ie7_compat)
1171                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
1172                         else
1173                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';
1174
1175                         t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
1176
1177                         // Firefox 2 doesn't load stylesheets correctly this way
1178                         if (!isGecko || !/Firefox\/2/.test(navigator.userAgent)) {
1179                                 for (i = 0; i < t.contentCSS.length; i++)
1180                                         t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';
1181
1182                                 t.contentCSS = [];
1183                         }
1184
1185                         bi = s.body_id || 'tinymce';
1186                         if (bi.indexOf('=') != -1) {
1187                                 bi = t.getParam('body_id', '', 'hash');
1188                                 bi = bi[t.id] || bi;
1189                         }
1190
1191                         bc = s.body_class || '';
1192                         if (bc.indexOf('=') != -1) {
1193                                 bc = t.getParam('body_class', '', 'hash');
1194                                 bc = bc[t.id] || '';
1195                         }
1196
1197                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
1198
1199                         // Domain relaxing enabled, then set document domain
1200                         if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
1201                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button
1202                                 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();})()';                         
1203                         }
1204
1205                         // Create iframe
1206                         // TODO: ACC add the appropriate description on this.
1207                         n = DOM.add(o.iframeContainer, 'iframe', { 
1208                                 id : t.id + "_ifr",
1209                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
1210                                 frameBorder : '0', 
1211                                 title : s.aria_label,
1212                                 style : {
1213                                         width : '100%',
1214                                         height : h
1215                                 }
1216                         });
1217
1218                         t.contentAreaContainer = o.iframeContainer;
1219                         DOM.get(o.editorContainer).style.display = t.orgDisplay;
1220                         DOM.get(t.id).style.display = 'none';
1221                         DOM.setAttrib(t.id, 'aria-hidden', true);
1222
1223                         if (!tinymce.relaxedDomain || !u)
1224                                 t.setupIframe();
1225
1226                         e = n = o = null; // Cleanup
1227                 },
1228
1229                 /**
1230                  * This method get called by the init method ones the iframe is loaded.
1231                  * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
1232                  * This method should not be called directly.
1233                  *
1234                  * @method setupIframe
1235                  */
1236                 setupIframe : function() {
1237                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
1238
1239                         // Setup iframe body
1240                         if (!isIE || !tinymce.relaxedDomain) {
1241                                 d.open();
1242                                 d.write(t.iframeHTML);
1243                                 d.close();
1244
1245                                 if (tinymce.relaxedDomain)
1246                                         d.domain = tinymce.relaxedDomain;
1247                         }
1248
1249                         // Design mode needs to be added here Ctrl+A will fail otherwise
1250                         if (!isIE) {
1251                                 try {
1252                                         if (!s.readonly)
1253                                                 d.designMode = 'On';
1254                                 } catch (ex) {
1255                                         // Will fail on Gecko if the editor is placed in an hidden container element
1256                                         // The design mode will be set ones the editor is focused
1257                                 }
1258                         }
1259
1260                         // IE needs to use contentEditable or it will display non secure items for HTTPS
1261                         if (isIE) {
1262                                 // It will not steal focus if we hide it while setting contentEditable
1263                                 b = t.getBody();
1264                                 DOM.hide(b);
1265
1266                                 if (!s.readonly)
1267                                         b.contentEditable = true;
1268
1269                                 DOM.show(b);
1270                         }
1271
1272                         /**
1273                          * Schema instance, enables you to validate elements and it's children.
1274                          *
1275                          * @property schema
1276                          * @type tinymce.html.Schema
1277                          */
1278                         t.schema = new tinymce.html.Schema(s);
1279
1280                         /**
1281                          * DOM instance for the editor.
1282                          *
1283                          * @property dom
1284                          * @type tinymce.dom.DOMUtils
1285                          * @example
1286                          * // Adds a class to all paragraphs within the editor
1287                          * tinyMCE.activeEditor.dom.addClass(tinyMCE.activeEditor.dom.select('p'), 'someclass');
1288                          */
1289                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
1290                                 keep_values : true,
1291                                 url_converter : t.convertURL,
1292                                 url_converter_scope : t,
1293                                 hex_colors : s.force_hex_style_colors,
1294                                 class_filter : s.class_filter,
1295                                 update_styles : 1,
1296                                 fix_ie_paragraphs : 1,
1297                                 schema : t.schema
1298                         });
1299
1300                         /**
1301                          * HTML parser will be used when contents is inserted into the editor.
1302                          *
1303                          * @property parser
1304                          * @type tinymce.html.DomParser
1305                          */
1306                         t.parser = new tinymce.html.DomParser(s, t.schema);
1307
1308                         // Force anchor names closed
1309                         t.parser.addAttributeFilter('name', function(nodes, name) {
1310                                 var i = nodes.length, sibling, prevSibling, parent, node;
1311
1312                                 while (i--) {
1313                                         node = nodes[i];
1314                                         if (node.name === 'a' && node.firstChild) {
1315                                                 parent = node.parent;
1316
1317                                                 // Move children after current node
1318                                                 sibling = node.lastChild;
1319                                                 do {
1320                                                         prevSibling = sibling.prev;
1321                                                         parent.insert(sibling, node);
1322                                                         sibling = prevSibling;
1323                                                 } while (sibling);
1324                                         }
1325                                 }
1326                         });
1327
1328                         // Convert src and href into data-mce-src, data-mce-href and data-mce-style
1329                         t.parser.addAttributeFilter('src,href,style', function(nodes, name) {
1330                                 var i = nodes.length, node, dom = t.dom, value;
1331
1332                                 while (i--) {
1333                                         node = nodes[i];
1334                                         value = node.attr(name);
1335
1336                                         if (name === "style")
1337                                                 node.attr('data-mce-style', dom.serializeStyle(dom.parseStyle(value), node.name));
1338                                         else
1339                                                 node.attr('data-mce-' + name, t.convertURL(value, name, node.name));
1340                                 }
1341                         });
1342
1343                         // Keep scripts from executing
1344                         t.parser.addNodeFilter('script', function(nodes, name) {
1345                                 var i = nodes.length;
1346
1347                                 while (i--)
1348                                         nodes[i].attr('type', 'mce-text/javascript');
1349                         });
1350
1351                         t.parser.addNodeFilter('#cdata', function(nodes, name) {
1352                                 var i = nodes.length, node;
1353
1354                                 while (i--) {
1355                                         node = nodes[i];
1356                                         node.type = 8;
1357                                         node.name = '#comment';
1358                                         node.value = '[CDATA[' + node.value + ']]';
1359                                 }
1360                         });
1361
1362                         t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
1363                                 var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();
1364
1365                                 while (i--) {
1366                                         node = nodes[i];
1367
1368                                         if (node.isEmpty(nonEmptyElements))
1369                                                 node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;
1370                                 }
1371                         });
1372
1373                         /**
1374                          * DOM serializer for the editor. Will be used when contents is extracted from the editor.
1375                          *
1376                          * @property serializer
1377                          * @type tinymce.dom.Serializer
1378                          * @example
1379                          * // Serializes the first paragraph in the editor into a string
1380                          * tinyMCE.activeEditor.serializer.serialize(tinyMCE.activeEditor.dom.select('p')[0]);
1381                          */
1382                         t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);
1383
1384                         /**
1385                          * Selection instance for the editor.
1386                          *
1387                          * @property selection
1388                          * @type tinymce.dom.Selection
1389                          * @example
1390                          * // Sets some contents to the current selection in the editor
1391                          * tinyMCE.activeEditor.selection.setContent('Some contents');
1392                          *
1393                          * // Gets the current selection
1394                          * alert(tinyMCE.activeEditor.selection.getContent());
1395                          *
1396                          * // Selects the first paragraph found
1397                          * tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
1398                          */
1399                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
1400
1401                         /**
1402                          * Formatter instance.
1403                          *
1404                          * @property formatter
1405                          * @type tinymce.Formatter
1406                          */
1407                         t.formatter = new tinymce.Formatter(this);
1408
1409                         // Register default formats
1410                         t.formatter.register({
1411                                 alignleft : [
1412                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
1413                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
1414                                 ],
1415
1416                                 aligncenter : [
1417                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
1418                                         {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
1419                                         {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
1420                                 ],
1421
1422                                 alignright : [
1423                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
1424                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
1425                                 ],
1426
1427                                 alignfull : [
1428                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}
1429                                 ],
1430
1431                                 bold : [
1432                                         {inline : 'strong', remove : 'all'},
1433                                         {inline : 'span', styles : {fontWeight : 'bold'}},
1434                                         {inline : 'b', remove : 'all'}
1435                                 ],
1436
1437                                 italic : [
1438                                         {inline : 'em', remove : 'all'},
1439                                         {inline : 'span', styles : {fontStyle : 'italic'}},
1440                                         {inline : 'i', remove : 'all'}
1441                                 ],
1442
1443                                 underline : [
1444                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
1445                                         {inline : 'u', remove : 'all'}
1446                                 ],
1447
1448                                 strikethrough : [
1449                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
1450                                         {inline : 'strike', remove : 'all'}
1451                                 ],
1452
1453                                 forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
1454                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
1455                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},
1456                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},
1457                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
1458                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
1459                                 subscript : {inline : 'sub'},
1460                                 superscript : {inline : 'sup'},
1461
1462                                 removeformat : [
1463                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
1464                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
1465                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
1466                                 ]
1467                         });
1468
1469                         // Register default block formats
1470                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
1471                                 t.formatter.register(name, {block : name, remove : 'all'});
1472                         });
1473
1474                         // Register user defined formats
1475                         t.formatter.register(t.settings.formats);
1476
1477                         /**
1478                          * Undo manager instance, responsible for handling undo levels. 
1479                          *
1480                          * @property undoManager
1481                          * @type tinymce.UndoManager
1482                          * @example
1483                          * // Undoes the last modification to the editor
1484                          * tinyMCE.activeEditor.undoManager.undo();
1485                          */
1486                         t.undoManager = new tinymce.UndoManager(t);
1487
1488                         // Pass through
1489                         t.undoManager.onAdd.add(function(um, l) {
1490                                 if (um.hasUndo())
1491                                         return t.onChange.dispatch(t, l, um);
1492                         });
1493
1494                         t.undoManager.onUndo.add(function(um, l) {
1495                                 return t.onUndo.dispatch(t, l, um);
1496                         });
1497
1498                         t.undoManager.onRedo.add(function(um, l) {
1499                                 return t.onRedo.dispatch(t, l, um);
1500                         });
1501
1502                         t.forceBlocks = new tinymce.ForceBlocks(t, {
1503                                 forced_root_block : s.forced_root_block
1504                         });
1505
1506                         t.editorCommands = new tinymce.EditorCommands(t);
1507
1508                         // Pass through
1509                         t.serializer.onPreProcess.add(function(se, o) {
1510                                 return t.onPreProcess.dispatch(t, o, se);
1511                         });
1512
1513                         t.serializer.onPostProcess.add(function(se, o) {
1514                                 return t.onPostProcess.dispatch(t, o, se);
1515                         });
1516
1517                         t.onPreInit.dispatch(t);
1518
1519                         if (!s.gecko_spellcheck)
1520                                 t.getBody().spellcheck = 0;
1521
1522                         if (!s.readonly)
1523                                 t._addEvents();
1524
1525                         t.controlManager.onPostRender.dispatch(t, t.controlManager);
1526                         t.onPostRender.dispatch(t);
1527
1528                         if (s.directionality)
1529                                 t.getBody().dir = s.directionality;
1530
1531                         if (s.nowrap)
1532                                 t.getBody().style.whiteSpace = "nowrap";
1533
1534                         if (s.handle_node_change_callback) {
1535                                 t.onNodeChange.add(function(ed, cm, n) {
1536                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
1537                                 });
1538                         }
1539
1540                         if (s.save_callback) {
1541                                 t.onSaveContent.add(function(ed, o) {
1542                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
1543
1544                                         if (h)
1545                                                 o.content = h;
1546                                 });
1547                         }
1548
1549                         if (s.onchange_callback) {
1550                                 t.onChange.add(function(ed, l) {
1551                                         t.execCallback('onchange_callback', t, l);
1552                                 });
1553                         }
1554
1555                         if (s.protect) {
1556                                 t.onBeforeSetContent.add(function(ed, o) {
1557                                         if (s.protect) {
1558                                                 each(s.protect, function(pattern) {
1559                                                         o.content = o.content.replace(pattern, function(str) {
1560                                                                 return '<!--mce:protected ' + escape(str) + '-->';
1561                                                         });
1562                                                 });
1563                                         }
1564                                 });
1565                         }
1566
1567                         if (s.convert_newlines_to_brs) {
1568                                 t.onBeforeSetContent.add(function(ed, o) {
1569                                         if (o.initial)
1570                                                 o.content = o.content.replace(/\r?\n/g, '<br />');
1571                                 });
1572                         }
1573
1574                         if (s.preformatted) {
1575                                 t.onPostProcess.add(function(ed, o) {
1576                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');
1577                                         o.content = o.content.replace(/<\/pre>\s*$/, '');
1578
1579                                         if (o.set)
1580                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
1581                                 });
1582                         }
1583
1584                         if (s.verify_css_classes) {
1585                                 t.serializer.attribValueFilter = function(n, v) {
1586                                         var s, cl;
1587
1588                                         if (n == 'class') {
1589                                                 // Build regexp for classes
1590                                                 if (!t.classesRE) {
1591                                                         cl = t.dom.getClasses();
1592
1593                                                         if (cl.length > 0) {
1594                                                                 s = '';
1595
1596                                                                 each (cl, function(o) {
1597                                                                         s += (s ? '|' : '') + o['class'];
1598                                                                 });
1599
1600                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');
1601                                                         }
1602                                                 }
1603
1604                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
1605                                         }
1606
1607                                         return v;
1608                                 };
1609                         }
1610
1611                         if (s.cleanup_callback) {
1612                                 t.onBeforeSetContent.add(function(ed, o) {
1613                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
1614                                 });
1615
1616                                 t.onPreProcess.add(function(ed, o) {
1617                                         if (o.set)
1618                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
1619
1620                                         if (o.get)
1621                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
1622                                 });
1623
1624                                 t.onPostProcess.add(function(ed, o) {
1625                                         if (o.set)
1626                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
1627
1628                                         if (o.get)                                              
1629                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
1630                                 });
1631                         }
1632
1633                         if (s.save_callback) {
1634                                 t.onGetContent.add(function(ed, o) {
1635                                         if (o.save)
1636                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
1637                                 });
1638                         }
1639
1640                         if (s.handle_event_callback) {
1641                                 t.onEvent.add(function(ed, e, o) {
1642                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)
1643                                                 Event.cancel(e);
1644                                 });
1645                         }
1646
1647                         // Add visual aids when new contents is added
1648                         t.onSetContent.add(function() {
1649                                 t.addVisual(t.getBody());
1650                         });
1651
1652                         // Remove empty contents
1653                         if (s.padd_empty_editor) {
1654                                 t.onPostProcess.add(function(ed, o) {
1655                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
1656                                 });
1657                         }
1658
1659                         if (isGecko) {
1660                                 // Fix gecko link bug, when a link is placed at the end of block elements there is
1661                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link
1662                                 function fixLinks(ed, o) {
1663                                         each(ed.dom.select('a'), function(n) {
1664                                                 var pn = n.parentNode;
1665
1666                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)
1667                                                         ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});
1668                                         });
1669                                 };
1670
1671                                 t.onExecCommand.add(function(ed, cmd) {
1672                                         if (cmd === 'CreateLink')
1673                                                 fixLinks(ed);
1674                                 });
1675
1676                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
1677
1678                                 if (!s.readonly) {
1679                                         try {
1680                                                 // Design mode must be set here once again to fix a bug where
1681                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
1682                                                 d.designMode = 'Off';
1683                                                 d.designMode = 'On';
1684                                         } catch (ex) {
1685                                                 // Will fail on Gecko if the editor is placed in an hidden container element
1686                                                 // The design mode will be set ones the editor is focused
1687                                         }
1688                                 }
1689                         }
1690
1691                         // A small timeout was needed since firefox will remove. Bug: #1838304
1692                         setTimeout(function () {
1693                                 if (t.removed)
1694                                         return;
1695
1696                                 t.load({initial : true, format : 'html'});
1697                                 t.startContent = t.getContent({format : 'raw'});
1698                                 t.undoManager.add();
1699                                 t.initialized = true;
1700
1701                                 t.onInit.dispatch(t);
1702                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
1703                                 t.execCallback('init_instance_callback', t);
1704                                 t.focus(true);
1705                                 t.nodeChanged({initial : 1});
1706
1707                                 // Load specified content CSS last
1708                                 each(t.contentCSS, function(u) {
1709                                         t.dom.loadCSS(u);
1710                                 });
1711
1712                                 // Handle auto focus
1713                                 if (s.auto_focus) {
1714                                         setTimeout(function () {
1715                                                 var ed = tinymce.get(s.auto_focus);
1716
1717                                                 ed.selection.select(ed.getBody(), 1);
1718                                                 ed.selection.collapse(1);
1719                                                 ed.getWin().focus();
1720                                         }, 100);
1721                                 }
1722                         }, 1);
1723         
1724                         e = null;
1725                 },
1726
1727                 // #ifdef contentEditable
1728
1729                 /**
1730                  * Sets up the contentEditable mode.
1731                  *
1732                  * @method setupContentEditable
1733                  */
1734                 setupContentEditable : function() {
1735                         var t = this, s = t.settings, e = t.getElement();
1736
1737                         t.contentDocument = s.content_document || document;
1738                         t.contentWindow = s.content_window || window;
1739                         t.bodyElement = e;
1740
1741                         // Prevent leak in IE
1742                         s.content_document = s.content_window = null;
1743
1744                         DOM.hide(e);
1745                         e.contentEditable = t.getParam('content_editable_state', true);
1746                         DOM.show(e);
1747
1748                         if (!s.gecko_spellcheck)
1749                                 t.getDoc().body.spellcheck = 0;
1750
1751                         // Setup objects
1752                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
1753                                 keep_values : true,
1754                                 url_converter : t.convertURL,
1755                                 url_converter_scope : t,
1756                                 hex_colors : s.force_hex_style_colors,
1757                                 class_filter : s.class_filter,
1758                                 root_element : t.id,
1759                                 fix_ie_paragraphs : 1,
1760                                 update_styles : 1
1761                         });
1762
1763                         t.serializer = new tinymce.dom.Serializer(s, t.dom, schema);
1764
1765                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
1766                         t.forceBlocks = new tinymce.ForceBlocks(t, {
1767                                 forced_root_block : s.forced_root_block
1768                         });
1769
1770                         t.editorCommands = new tinymce.EditorCommands(t);
1771
1772                         // Pass through
1773                         t.serializer.onPreProcess.add(function(se, o) {
1774                                 return t.onPreProcess.dispatch(t, o, se);
1775                         });
1776
1777                         t.serializer.onPostProcess.add(function(se, o) {
1778                                 return t.onPostProcess.dispatch(t, o, se);
1779                         });
1780
1781                         t.onPreInit.dispatch(t);
1782                         t._addEvents();
1783
1784                         t.controlManager.onPostRender.dispatch(t, t.controlManager);
1785                         t.onPostRender.dispatch(t);
1786
1787                         t.onSetContent.add(function() {
1788                                 t.addVisual(t.getBody());
1789                         });
1790
1791                         //t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
1792                         t.startContent = t.getContent({format : 'raw'});
1793                         t.undoManager.add({initial : true});
1794                         t.initialized = true;
1795
1796                         t.onInit.dispatch(t);
1797                         t.focus(true);
1798                         t.nodeChanged({initial : 1});
1799
1800                         // Load specified content CSS last
1801                         if (s.content_css) {
1802                                 each(explode(s.content_css), function(u) {
1803                                         t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
1804                                 });
1805                         }
1806
1807                         if (isIE) {
1808                                 // Store away selection
1809                                 t.dom.bind(t.getElement(), 'beforedeactivate', function() {
1810                                         t.lastSelectionBookmark = t.selection.getBookmark(1);
1811                                 });
1812
1813                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, o) {
1814                                         if (!DOM.getParent(ed.selection.getStart(), function(n) {return n == ed.getBody();}))
1815                                                 o.terminate = 1;
1816
1817                                         if (!DOM.getParent(ed.selection.getEnd(), function(n) {return n == ed.getBody();}))
1818                                                 o.terminate = 1;
1819                                 });
1820                         }
1821
1822                         e = null; // Cleanup
1823                 },
1824
1825                 // #endif
1826
1827                 /**
1828                  * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
1829                  * it will also place DOM focus inside the editor.
1830                  *
1831                  * @method focus
1832                  * @param {Boolean} sf Skip DOM focus. Just set is as the active editor.
1833                  */
1834                 focus : function(sf) {
1835                         var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();
1836
1837                         if (!sf) {
1838                                 // Get selected control element
1839                                 ieRng = t.selection.getRng();
1840                                 if (ieRng.item) {
1841                                         controlElm = ieRng.item(0);
1842                                 }
1843
1844                                 // Is not content editable
1845                                 if (!ce)
1846                                         t.getWin().focus();
1847
1848                                 // Restore selected control element
1849                                 // This is needed when for example an image is selected within a
1850                                 // layer a call to focus will then remove the control selection
1851                                 if (controlElm && controlElm.ownerDocument == doc) {
1852                                         ieRng = doc.body.createControlRange();
1853                                         ieRng.addElement(controlElm);
1854                                         ieRng.select();
1855                                 }
1856
1857                                 // #ifdef contentEditable
1858
1859                                 // Content editable mode ends here
1860                                 if (ce) {
1861                                         if (tinymce.isWebKit)
1862                                                 t.getWin().focus();
1863                                         else {
1864                                                 if (tinymce.isIE)
1865                                                         t.getElement().setActive();
1866                                                 else
1867                                                         t.getElement().focus();
1868                                         }
1869                                 }
1870
1871                                 // #endif
1872                         }
1873
1874                         if (tinymce.activeEditor != t) {
1875                                 if ((oed = tinymce.activeEditor) != null)
1876                                         oed.onDeactivate.dispatch(oed, t);
1877
1878                                 t.onActivate.dispatch(t, oed);
1879                         }
1880
1881                         tinymce._setActive(t);
1882                 },
1883
1884                 /**
1885                  * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
1886                  * There new event model is a better way to add callback so this method might be removed in the future.
1887                  *
1888                  * @method execCallback
1889                  * @param {String} n Name of the callback to execute.
1890                  * @return {Object} Return value passed from callback function.
1891                  */
1892                 execCallback : function(n) {
1893                         var t = this, f = t.settings[n], s;
1894
1895                         if (!f)
1896                                 return;
1897
1898                         // Look through lookup
1899                         if (t.callbackLookup && (s = t.callbackLookup[n])) {
1900                                 f = s.func;
1901                                 s = s.scope;
1902                         }
1903
1904                         if (is(f, 'string')) {
1905                                 s = f.replace(/\.\w+$/, '');
1906                                 s = s ? tinymce.resolve(s) : 0;
1907                                 f = tinymce.resolve(f);
1908                                 t.callbackLookup = t.callbackLookup || {};
1909                                 t.callbackLookup[n] = {func : f, scope : s};
1910                         }
1911
1912                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
1913                 },
1914
1915                 /**
1916                  * Translates the specified string by replacing variables with language pack items it will also check if there is
1917                  * a key mathcin the input.
1918                  *
1919                  * @method translate
1920                  * @param {String} s String to translate by the language pack data.
1921                  * @return {String} Translated string.
1922                  */
1923                 translate : function(s) {
1924                         var c = this.settings.language || 'en', i18n = tinymce.i18n;
1925
1926                         if (!s)
1927                                 return '';
1928
1929                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
1930                                 return i18n[c + '.' + b] || '{#' + b + '}';
1931                         });
1932                 },
1933
1934                 /**
1935                  * Returns a language pack item by name/key.
1936                  *
1937                  * @method getLang
1938                  * @param {String} n Name/key to get from the language pack.
1939                  * @param {String} dv Optional default value to retrive.
1940                  */
1941                 getLang : function(n, dv) {
1942                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
1943                 },
1944
1945                 /**
1946                  * Returns a configuration parameter by name.
1947                  *
1948                  * @method getParam
1949                  * @param {String} n Configruation parameter to retrive.
1950                  * @param {String} dv Optional default value to return.
1951                  * @param {String} ty Optional type parameter.
1952                  * @return {String} Configuration parameter value or default value.
1953                  * @example
1954                  * // Returns a specific config value from the currently active editor
1955                  * var someval = tinyMCE.activeEditor.getParam('myvalue');
1956                  * 
1957                  * // Returns a specific config value from a specific editor instance by id
1958                  * var someval2 = tinyMCE.get('my_editor').getParam('myvalue');
1959                  */
1960                 getParam : function(n, dv, ty) {
1961                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
1962
1963                         if (ty === 'hash') {
1964                                 o = {};
1965
1966                                 if (is(v, 'string')) {
1967                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
1968                                                 v = v.split('=');
1969
1970                                                 if (v.length > 1)
1971                                                         o[tr(v[0])] = tr(v[1]);
1972                                                 else
1973                                                         o[tr(v[0])] = tr(v);
1974                                         });
1975                                 } else
1976                                         o = v;
1977
1978                                 return o;
1979                         }
1980
1981                         return v;
1982                 },
1983
1984                 /**
1985                  * Distpaches out a onNodeChange event to all observers. This method should be called when you
1986                  * need to update the UI states or element path etc.
1987                  *
1988                  * @method nodeChanged
1989                  * @param {Object} o Optional object to pass along for the node changed event.
1990                  */
1991                 nodeChanged : function(o) {
1992                         var t = this, s = t.selection, n = s.getStart() || t.getBody();
1993
1994                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
1995                         if (t.initialized) {
1996                                 o = o || {};
1997                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state
1998
1999                                 // Get parents and add them to object
2000                                 o.parents = [];
2001                                 t.dom.getParent(n, function(node) {
2002                                         if (node.nodeName == 'BODY')
2003                                                 return true;
2004
2005                                         o.parents.push(node);
2006                                 });
2007
2008                                 t.onNodeChange.dispatch(
2009                                         t,
2010                                         o ? o.controlManager || t.controlManager : t.controlManager,
2011                                         n,
2012                                         s.isCollapsed(),
2013                                         o
2014                                 );
2015                         }
2016                 },
2017
2018                 /**
2019                  * Adds a button that later gets created by the ControlManager. This is a shorter and easier method
2020                  * of adding buttons without the need to deal with the ControlManager directly. But it's also less
2021                  * powerfull if you need more control use the ControlManagers factory methods instead.
2022                  *
2023                  * @method addButton
2024                  * @param {String} n Button name to add.
2025                  * @param {Object} s Settings object with title, cmd etc.
2026                  * @example
2027                  * // Adds a custom button to the editor and when a user clicks the button it will open
2028                  * // an alert box with the selected contents as plain text.
2029                  * tinyMCE.init({
2030                  *    ...
2031                  * 
2032                  *    theme_advanced_buttons1 : 'example,..'
2033                  * 
2034                  *    setup : function(ed) {
2035                  *       // Register example button
2036                  *       ed.addButton('example', {
2037                  *          title : 'example.desc',
2038                  *          image : '../jscripts/tiny_mce/plugins/example/img/example.gif',
2039                  *          onclick : function() {
2040                  *             ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format : 'text'}));
2041                  *          }
2042                  *       });
2043                  *    }
2044                  * });
2045                  */
2046                 addButton : function(n, s) {
2047                         var t = this;
2048
2049                         t.buttons = t.buttons || {};
2050                         t.buttons[n] = s;
2051                 },
2052
2053                 /**
2054                  * Adds a custom command to the editor, you can also override existing commands with this method.
2055                  * The command that you add can be executed with execCommand.
2056                  *
2057                  * @method addCommand
2058                  * @param {String} name Command name to add/override.
2059                  * @param {addCommandCallback} callback Function to execute when the command occurs.
2060                  * @param {Object} scope Optional scope to execute the function in.
2061                  * @example
2062                  * // Adds a custom command that later can be executed using execCommand
2063                  * tinyMCE.init({
2064                  *    ...
2065                  * 
2066                  *    setup : function(ed) {
2067                  *       // Register example command
2068                  *       ed.addCommand('mycommand', function(ui, v) {
2069                  *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format : 'text'}));
2070                  *       });
2071                  *    }
2072                  * });
2073                  */
2074                 addCommand : function(name, callback, scope) {
2075                         /**
2076                          * Callback function that gets called when a command is executed.
2077                          *
2078                          * @callback addCommandCallback
2079                          * @param {Boolean} ui Display UI state true/false.
2080                          * @param {Object} value Optional value for command.
2081                          * @return {Boolean} True/false state if the command was handled or not.
2082                          */
2083                         this.execCommands[name] = {func : callback, scope : scope || this};
2084                 },
2085
2086                 /**
2087                  * Adds a custom query state command to the editor, you can also override existing commands with this method.
2088                  * The command that you add can be executed with queryCommandState function.
2089                  *
2090                  * @method addQueryStateHandler
2091                  * @param {String} name Command name to add/override.
2092                  * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
2093                  * @param {Object} scope Optional scope to execute the function in.
2094                  */
2095                 addQueryStateHandler : function(name, callback, scope) {
2096                         /**
2097                          * Callback function that gets called when a queryCommandState is executed.
2098                          *
2099                          * @callback addQueryStateHandlerCallback
2100                          * @return {Boolean} True/false state if the command is enabled or not like is it bold.
2101                          */
2102                         this.queryStateCommands[name] = {func : callback, scope : scope || this};
2103                 },
2104
2105                 /**
2106                  * Adds a custom query value command to the editor, you can also override existing commands with this method.
2107                  * The command that you add can be executed with queryCommandValue function.
2108                  *
2109                  * @method addQueryValueHandler
2110                  * @param {String} name Command name to add/override.
2111                  * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
2112                  * @param {Object} scope Optional scope to execute the function in.
2113                  */
2114                 addQueryValueHandler : function(name, callback, scope) {
2115                         /**
2116                          * Callback function that gets called when a queryCommandValue is executed.
2117                          *
2118                          * @callback addQueryValueHandlerCallback
2119                          * @return {Object} Value of the command or undefined.
2120                          */
2121                         this.queryValueCommands[name] = {func : callback, scope : scope || this};
2122                 },
2123
2124                 /**
2125                  * Adds a keyboard shortcut for some command or function.
2126                  *
2127                  * @method addShortcut
2128                  * @param {String} pa Shortcut pattern. Like for example: ctrl+alt+o.
2129                  * @param {String} desc Text description for the command.
2130                  * @param {String/Function} cmd_func Command name string or function to execute when the key is pressed.
2131                  * @param {Object} sc Optional scope to execute the function in.
2132                  * @return {Boolean} true/false state if the shortcut was added or not.
2133                  */
2134                 addShortcut : function(pa, desc, cmd_func, sc) {
2135                         var t = this, c;
2136
2137                         if (!t.settings.custom_shortcuts)
2138                                 return false;
2139
2140                         t.shortcuts = t.shortcuts || {};
2141
2142                         if (is(cmd_func, 'string')) {
2143                                 c = cmd_func;
2144
2145                                 cmd_func = function() {
2146                                         t.execCommand(c, false, null);
2147                                 };
2148                         }
2149
2150                         if (is(cmd_func, 'object')) {
2151                                 c = cmd_func;
2152
2153                                 cmd_func = function() {
2154                                         t.execCommand(c[0], c[1], c[2]);
2155                                 };
2156                         }
2157
2158                         each(explode(pa), function(pa) {
2159                                 var o = {
2160                                         func : cmd_func,
2161                                         scope : sc || this,
2162                                         desc : desc,
2163                                         alt : false,
2164                                         ctrl : false,
2165                                         shift : false
2166                                 };
2167
2168                                 each(explode(pa, '+'), function(v) {
2169                                         switch (v) {
2170                                                 case 'alt':
2171                                                 case 'ctrl':
2172                                                 case 'shift':
2173                                                         o[v] = true;
2174                                                         break;
2175
2176                                                 default:
2177                                                         o.charCode = v.charCodeAt(0);
2178                                                         o.keyCode = v.toUpperCase().charCodeAt(0);
2179                                         }
2180                                 });
2181
2182                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
2183                         });
2184
2185                         return true;
2186                 },
2187
2188                 /**
2189                  * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
2190                  * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
2191                  * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
2192                  * return true it will handle the command as a internal browser command.
2193                  *
2194                  * @method execCommand
2195                  * @param {String} cmd Command name to execute, for example mceLink or Bold.
2196                  * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
2197                  * @param {mixed} val Optional command value, this can be anything.
2198                  * @param {Object} a Optional arguments object.
2199                  * @return {Boolean} True/false if the command was executed or not.
2200                  */
2201                 execCommand : function(cmd, ui, val, a) {
2202                         var t = this, s = 0, o, st;
2203
2204                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
2205                                 t.focus();
2206
2207                         o = {};
2208                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
2209                         if (o.terminate)
2210                                 return false;
2211
2212                         // Command callback
2213                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
2214                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);
2215                                 return true;
2216                         }
2217
2218                         // Registred commands
2219                         if (o = t.execCommands[cmd]) {
2220                                 st = o.func.call(o.scope, ui, val);
2221
2222                                 // Fall through on true
2223                                 if (st !== true) {
2224                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);
2225                                         return st;
2226                                 }
2227                         }
2228
2229                         // Plugin commands
2230                         each(t.plugins, function(p) {
2231                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {
2232                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);
2233                                         s = 1;
2234                                         return false;
2235                                 }
2236                         });
2237
2238                         if (s)
2239                                 return true;
2240
2241                         // Theme commands
2242                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
2243                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);
2244                                 return true;
2245                         }
2246
2247                         // Editor commands
2248                         if (t.editorCommands.execCommand(cmd, ui, val)) {
2249                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);
2250                                 return true;
2251                         }
2252
2253                         // Browser commands
2254                         t.getDoc().execCommand(cmd, ui, val);
2255                         t.onExecCommand.dispatch(t, cmd, ui, val, a);
2256                 },
2257
2258                 /**
2259                  * Returns a command specific state, for example if bold is enabled or not.
2260                  *
2261                  * @method queryCommandState
2262                  * @param {string} cmd Command to query state from.
2263                  * @return {Boolean} Command specific state, for example if bold is enabled or not.
2264                  */
2265                 queryCommandState : function(cmd) {
2266                         var t = this, o, s;
2267
2268                         // Is hidden then return undefined
2269                         if (t._isHidden())
2270                                 return;
2271
2272                         // Registred commands
2273                         if (o = t.queryStateCommands[cmd]) {
2274                                 s = o.func.call(o.scope);
2275
2276                                 // Fall though on true
2277                                 if (s !== true)
2278                                         return s;
2279                         }
2280
2281                         // Registred commands
2282                         o = t.editorCommands.queryCommandState(cmd);
2283                         if (o !== -1)
2284                                 return o;
2285
2286                         // Browser commands
2287                         try {
2288                                 return this.getDoc().queryCommandState(cmd);
2289                         } catch (ex) {
2290                                 // Fails sometimes see bug: 1896577
2291                         }
2292                 },
2293
2294                 /**
2295                  * Returns a command specific value, for example the current font size.
2296                  *
2297                  * @method queryCommandValue
2298                  * @param {string} c Command to query value from.
2299                  * @return {Object} Command specific value, for example the current font size.
2300                  */
2301                 queryCommandValue : function(c) {
2302                         var t = this, o, s;
2303
2304                         // Is hidden then return undefined
2305                         if (t._isHidden())
2306                                 return;
2307
2308                         // Registred commands
2309                         if (o = t.queryValueCommands[c]) {
2310                                 s = o.func.call(o.scope);
2311
2312                                 // Fall though on true
2313                                 if (s !== true)
2314                                         return s;
2315                         }
2316
2317                         // Registred commands
2318                         o = t.editorCommands.queryCommandValue(c);
2319                         if (is(o))
2320                                 return o;
2321
2322                         // Browser commands
2323                         try {
2324                                 return this.getDoc().queryCommandValue(c);
2325                         } catch (ex) {
2326                                 // Fails sometimes see bug: 1896577
2327                         }
2328                 },
2329
2330                 /**
2331                  * Shows the editor and hides any textarea/div that the editor is supposed to replace.
2332                  *
2333                  * @method show
2334                  */
2335                 show : function() {
2336                         var t = this;
2337
2338                         DOM.show(t.getContainer());
2339                         DOM.hide(t.id);
2340                         t.load();
2341                 },
2342
2343                 /**
2344                  * Hides the editor and shows any textarea/div that the editor is supposed to replace.
2345                  *
2346                  * @method hide
2347                  */
2348                 hide : function() {
2349                         var t = this, d = t.getDoc();
2350
2351                         // Fixed bug where IE has a blinking cursor left from the editor
2352                         if (isIE && d)
2353                                 d.execCommand('SelectAll');
2354
2355                         // We must save before we hide so Safari doesn't crash
2356                         t.save();
2357                         DOM.hide(t.getContainer());
2358                         DOM.setStyle(t.id, 'display', t.orgDisplay);
2359                 },
2360
2361                 /**
2362                  * Returns true/false if the editor is hidden or not.
2363                  *
2364                  * @method isHidden
2365                  * @return {Boolean} True/false if the editor is hidden or not.
2366                  */
2367                 isHidden : function() {
2368                         return !DOM.isHidden(this.id);
2369                 },
2370
2371                 /**
2372                  * Sets the progress state, this will display a throbber/progess for the editor.
2373                  * This is ideal for asycronous operations like an AJAX save call.
2374                  *
2375                  * @method setProgressState
2376                  * @param {Boolean} b Boolean state if the progress should be shown or hidden.
2377                  * @param {Number} ti Optional time to wait before the progress gets shown.
2378                  * @param {Object} o Optional object to pass to the progress observers.
2379                  * @return {Boolean} Same as the input state.
2380                  * @example
2381                  * // Show progress for the active editor
2382                  * tinyMCE.activeEditor.setProgressState(true);
2383                  * 
2384                  * // Hide progress for the active editor
2385                  * tinyMCE.activeEditor.setProgressState(false);
2386                  * 
2387                  * // Show progress after 3 seconds
2388                  * tinyMCE.activeEditor.setProgressState(true, 3000);
2389                  */
2390                 setProgressState : function(b, ti, o) {
2391                         this.onSetProgressState.dispatch(this, b, ti, o);
2392
2393                         return b;
2394                 },
2395
2396                 /**
2397                  * Loads contents from the textarea or div element that got converted into an editor instance.
2398                  * This method will move the contents from that textarea or div into the editor by using setContent
2399                  * so all events etc that method has will get dispatched as well.
2400                  *
2401                  * @method load
2402                  * @param {Object} o Optional content object, this gets passed around through the whole load process.
2403                  * @return {String} HTML string that got set into the editor.
2404                  */
2405                 load : function(o) {
2406                         var t = this, e = t.getElement(), h;
2407
2408                         if (e) {
2409                                 o = o || {};
2410                                 o.load = true;
2411
2412                                 // Double encode existing entities in the value
2413                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
2414                                 o.element = e;
2415
2416                                 if (!o.no_events)
2417                                         t.onLoadContent.dispatch(t, o);
2418
2419                                 o.element = e = null;
2420
2421                                 return h;
2422                         }
2423                 },
2424
2425                 /**
2426                  * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
2427                  * This method will move the HTML contents from the editor into that textarea or div by getContent
2428                  * so all events etc that method has will get dispatched as well.
2429                  *
2430                  * @method save
2431                  * @param {Object} o Optional content object, this gets passed around through the whole save process.
2432                  * @return {String} HTML string that got set into the textarea/div.
2433                  */
2434                 save : function(o) {
2435                         var t = this, e = t.getElement(), h, f;
2436
2437                         if (!e || !t.initialized)
2438                                 return;
2439
2440                         o = o || {};
2441                         o.save = true;
2442
2443                         // Add undo level will trigger onchange event
2444                         if (!o.no_events) {
2445                                 t.undoManager.typing = false;
2446                                 t.undoManager.add();
2447                         }
2448
2449                         o.element = e;
2450                         h = o.content = t.getContent(o);
2451
2452                         if (!o.no_events)
2453                                 t.onSaveContent.dispatch(t, o);
2454
2455                         h = o.content;
2456
2457                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
2458                                 e.innerHTML = h;
2459
2460                                 // Update hidden form element
2461                                 if (f = DOM.getParent(t.id, 'form')) {
2462                                         each(f.elements, function(e) {
2463                                                 if (e.name == t.id) {
2464                                                         e.value = h;
2465                                                         return false;
2466                                                 }
2467                                         });
2468                                 }
2469                         } else
2470                                 e.value = h;
2471
2472                         o.element = e = null;
2473
2474                         return h;
2475                 },
2476
2477                 /**
2478                  * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
2479                  * the different cleanup rules options.
2480                  *
2481                  * @method setContent
2482                  * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
2483                  * @param {Object} args Optional content object, this gets passed around through the whole set process.
2484                  * @return {String} HTML string that got set into the editor.
2485                  * @example
2486                  * // Sets the HTML contents of the activeEditor editor
2487                  * tinyMCE.activeEditor.setContent('<span>some</span> html');
2488                  * 
2489                  * // Sets the raw contents of the activeEditor editor
2490                  * tinyMCE.activeEditor.setContent('<span>some</span> html', {format : 'raw'});
2491                  * 
2492                  * // Sets the content of a specific editor (my_editor in this example)
2493                  * tinyMCE.get('my_editor').setContent(data);
2494                  * 
2495                  * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
2496                  * tinyMCE.activeEditor.setContent('[b]some[/b] html', {format : 'bbcode'});
2497                  */
2498                 setContent : function(content, args) {
2499                         var self = this, rootNode, body = self.getBody();
2500
2501                         // Setup args object
2502                         args = args || {};
2503                         args.format = args.format || 'html';
2504                         args.set = true;
2505                         args.content = content;
2506
2507                         // Do preprocessing
2508                         if (!args.no_events)
2509                                 self.onBeforeSetContent.dispatch(self, args);
2510
2511                         content = args.content;
2512
2513                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
2514                         // It will also be impossible to place the caret in the editor unless there is a BR element present
2515                         if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {
2516                                 body.innerHTML = '<br data-mce-bogus="1" />';
2517                                 return;
2518                         }
2519
2520                         // Parse and serialize the html
2521                         if (args.format !== 'raw') {
2522                                 content = new tinymce.html.Serializer({}, self.schema).serialize(
2523                                         self.parser.parse(content)
2524                                 );
2525                         }
2526
2527                         // Set the new cleaned contents to the editor
2528                         args.content = tinymce.trim(content);
2529                         self.dom.setHTML(body, args.content);
2530
2531                         // Do post processing
2532                         if (!args.no_events)
2533                                 self.onSetContent.dispatch(self, args);
2534
2535                         return args.content;
2536                 },
2537
2538                 /**
2539                  * Gets the content from the editor instance, this will cleanup the content before it gets returned using
2540                  * the different cleanup rules options.
2541                  *
2542                  * @method getContent
2543                  * @param {Object} args Optional content object, this gets passed around through the whole get process.
2544                  * @return {String} Cleaned content string, normally HTML contents.
2545                  * @example
2546                  * // Get the HTML contents of the currently active editor
2547                  * console.debug(tinyMCE.activeEditor.getContent());
2548                  * 
2549                  * // Get the raw contents of the currently active editor
2550                  * tinyMCE.activeEditor.getContent({format : 'raw'});
2551                  * 
2552                  * // Get content of a specific editor:
2553                  * tinyMCE.get('content id').getContent()
2554                  */
2555                 getContent : function(args) {
2556                         var self = this, content;
2557
2558                         // Setup args object
2559                         args = args || {};
2560                         args.format = args.format || 'html';
2561                         args.get = true;
2562
2563                         // Do preprocessing
2564                         if (!args.no_events)
2565                                 self.onBeforeGetContent.dispatch(self, args);
2566
2567                         // Get raw contents or by default the cleaned contents
2568                         if (args.format == 'raw')
2569                                 content = self.getBody().innerHTML;
2570                         else
2571                                 content = self.serializer.serialize(self.getBody(), args);
2572
2573                         args.content = tinymce.trim(content);
2574
2575                         // Do post processing
2576                         if (!args.no_events)
2577                                 self.onGetContent.dispatch(self, args);
2578
2579                         return args.content;
2580                 },
2581
2582                 /**
2583                  * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
2584                  *
2585                  * @method isDirty
2586                  * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
2587                  * @example
2588                  * if (tinyMCE.activeEditor.isDirty())
2589                  *     alert("You must save your contents.");
2590                  */
2591                 isDirty : function() {
2592                         var self = this;
2593
2594                         return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;
2595                 },
2596
2597                 /**
2598                  * Returns the editors container element. The container element wrappes in
2599                  * all the elements added to the page for the editor. Such as UI, iframe etc.
2600                  *
2601                  * @method getContainer
2602                  * @return {Element} HTML DOM element for the editor container.
2603                  */
2604                 getContainer : function() {
2605                         var t = this;
2606
2607                         if (!t.container)
2608                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');
2609
2610                         return t.container;
2611                 },
2612
2613                 /**
2614                  * Returns the editors content area container element. The this element is the one who
2615                  * holds the iframe or the editable element.
2616                  *
2617                  * @method getContentAreaContainer
2618                  * @return {Element} HTML DOM element for the editor area container.
2619                  */
2620                 getContentAreaContainer : function() {
2621                         return this.contentAreaContainer;
2622                 },
2623
2624                 /**
2625                  * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
2626                  *
2627                  * @method getElement
2628                  * @return {Element} HTML DOM element for the replaced element.
2629                  */
2630                 getElement : function() {
2631                         return DOM.get(this.settings.content_element || this.id);
2632                 },
2633
2634                 /**
2635                  * Returns the iframes window object.
2636                  *
2637                  * @method getWin
2638                  * @return {Window} Iframe DOM window object.
2639                  */
2640                 getWin : function() {
2641                         var t = this, e;
2642
2643                         if (!t.contentWindow) {
2644                                 e = DOM.get(t.id + "_ifr");
2645
2646                                 if (e)
2647                                         t.contentWindow = e.contentWindow;
2648                         }
2649
2650                         return t.contentWindow;
2651                 },
2652
2653                 /**
2654                  * Returns the iframes document object.
2655                  *
2656                  * @method getDoc
2657                  * @return {Document} Iframe DOM document object.
2658                  */
2659                 getDoc : function() {
2660                         var t = this, w;
2661
2662                         if (!t.contentDocument) {
2663                                 w = t.getWin();
2664
2665                                 if (w)
2666                                         t.contentDocument = w.document;
2667                         }
2668
2669                         return t.contentDocument;
2670                 },
2671
2672                 /**
2673                  * Returns the iframes body element.
2674                  *
2675                  * @method getBody
2676                  * @return {Element} Iframe body element.
2677                  */
2678                 getBody : function() {
2679                         return this.bodyElement || this.getDoc().body;
2680                 },
2681
2682                 /**
2683                  * URL converter function this gets executed each time a user adds an img, a or
2684                  * any other element that has a URL in it. This will be called both by the DOM and HTML
2685                  * manipulation functions.
2686                  *
2687                  * @method convertURL
2688                  * @param {string} u URL to convert.
2689                  * @param {string} n Attribute name src, href etc.
2690                  * @param {string/HTMLElement} Tag name or HTML DOM element depending on HTML or DOM insert.
2691                  * @return {string} Converted URL string.
2692                  */
2693                 convertURL : function(u, n, e) {
2694                         var t = this, s = t.settings;
2695
2696                         // Use callback instead
2697                         if (s.urlconverter_callback)
2698                                 return t.execCallback('urlconverter_callback', u, e, true, n);
2699
2700                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
2701                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
2702                                 return u;
2703
2704                         // Convert to relative
2705                         if (s.relative_urls)
2706                                 return t.documentBaseURI.toRelative(u);
2707
2708                         // Convert to absolute
2709                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
2710
2711                         return u;
2712                 },
2713
2714                 /**
2715                  * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
2716                  *
2717                  * @method addVisual
2718                  * @param {Element} e Optional root element to loop though to find tables etc that needs the visual aid.
2719                  */
2720                 addVisual : function(e) {
2721                         var t = this, s = t.settings;
2722
2723                         e = e || t.getBody();
2724
2725                         if (!is(t.hasVisual))
2726                                 t.hasVisual = s.visual;
2727
2728                         each(t.dom.select('table,a', e), function(e) {
2729                                 var v;
2730
2731                                 switch (e.nodeName) {
2732                                         case 'TABLE':
2733                                                 v = t.dom.getAttrib(e, 'border');
2734
2735                                                 if (!v || v == '0') {
2736                                                         if (t.hasVisual)
2737                                                                 t.dom.addClass(e, s.visual_table_class);
2738                                                         else
2739                                                                 t.dom.removeClass(e, s.visual_table_class);
2740                                                 }
2741
2742                                                 return;
2743
2744                                         case 'A':
2745                                                 v = t.dom.getAttrib(e, 'name');
2746
2747                                                 if (v) {
2748                                                         if (t.hasVisual)
2749                                                                 t.dom.addClass(e, 'mceItemAnchor');
2750                                                         else
2751                                                                 t.dom.removeClass(e, 'mceItemAnchor');
2752                                                 }
2753
2754                                                 return;
2755                                 }
2756                         });
2757
2758                         t.onVisualAid.dispatch(t, e, t.hasVisual);
2759                 },
2760
2761                 /**
2762                  * Removes the editor from the dom and tinymce collection.
2763                  *
2764                  * @method remove
2765                  */
2766                 remove : function() {
2767                         var t = this, e = t.getContainer();
2768
2769                         t.removed = 1; // Cancels post remove event execution
2770                         t.hide();
2771
2772                         t.execCallback('remove_instance_callback', t);
2773                         t.onRemove.dispatch(t);
2774
2775                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
2776                         t.onExecCommand.listeners = [];
2777
2778                         tinymce.remove(t);
2779                         DOM.remove(e);
2780                 },
2781
2782                 /**
2783                  * Destroys the editor instance by removing all events, element references or other resources
2784                  * that could leak memory. This method will be called automatically when the page is unloaded
2785                  * but you can also call it directly if you know what you are doing.
2786                  *
2787                  * @method destroy
2788                  * @param {Boolean} s Optional state if the destroy is an automatic destroy or user called one.
2789                  */
2790                 destroy : function(s) {
2791                         var t = this;
2792
2793                         // One time is enough
2794                         if (t.destroyed)
2795                                 return;
2796
2797                         if (!s) {
2798                                 tinymce.removeUnload(t.destroy);
2799                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
2800
2801                                 // Manual destroy
2802                                 if (t.theme && t.theme.destroy)
2803                                         t.theme.destroy();
2804
2805                                 // Destroy controls, selection and dom
2806                                 t.controlManager.destroy();
2807                                 t.selection.destroy();
2808                                 t.dom.destroy();
2809
2810                                 // Remove all events
2811
2812                                 // Don't clear the window or document if content editable
2813                                 // is enabled since other instances might still be present
2814                                 if (!t.settings.content_editable) {
2815                                         Event.clear(t.getWin());
2816                                         Event.clear(t.getDoc());
2817                                 }
2818
2819                                 Event.clear(t.getBody());
2820                                 Event.clear(t.formElement);
2821                         }
2822
2823                         if (t.formElement) {
2824                                 t.formElement.submit = t.formElement._mceOldSubmit;
2825                                 t.formElement._mceOldSubmit = null;
2826                         }
2827
2828                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
2829
2830                         if (t.selection)
2831                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
2832
2833                         t.destroyed = 1;
2834                 },
2835
2836                 // Internal functions
2837
2838                 _addEvents : function() {
2839                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
2840                         var t = this, i, s = t.settings, dom = t.dom, lo = {
2841                                 mouseup : 'onMouseUp',
2842                                 mousedown : 'onMouseDown',
2843                                 click : 'onClick',
2844                                 keyup : 'onKeyUp',
2845                                 keydown : 'onKeyDown',
2846                                 keypress : 'onKeyPress',
2847                                 submit : 'onSubmit',
2848                                 reset : 'onReset',
2849                                 contextmenu : 'onContextMenu',
2850                                 dblclick : 'onDblClick',
2851                                 paste : 'onPaste' // Doesn't work in all browsers yet
2852                         };
2853
2854                         function eventHandler(e, o) {
2855                                 var ty = e.type;
2856
2857                                 // Don't fire events when it's removed
2858                                 if (t.removed)
2859                                         return;
2860
2861                                 // Generic event handler
2862                                 if (t.onEvent.dispatch(t, e, o) !== false) {
2863                                         // Specific event handler
2864                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);
2865                                 }
2866                         };
2867
2868                         // Add DOM events
2869                         each(lo, function(v, k) {
2870                                 switch (k) {
2871                                         case 'contextmenu':
2872                                                 dom.bind(t.getDoc(), k, eventHandler);
2873                                                 break;
2874
2875                                         case 'paste':
2876                                                 dom.bind(t.getBody(), k, function(e) {
2877                                                         eventHandler(e);
2878                                                 });
2879                                                 break;
2880
2881                                         case 'submit':
2882                                         case 'reset':
2883                                                 dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
2884                                                 break;
2885
2886                                         default:
2887                                                 dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
2888                                 }
2889                         });
2890
2891                         dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
2892                                 t.focus(true);
2893                         });
2894
2895                         // #ifdef contentEditable
2896
2897                         if (s.content_editable && tinymce.isOpera) {
2898                                 // Opera doesn't support focus event for contentEditable elements so we need to fake it
2899                                 function doFocus(e) {
2900                                         t.focus(true);
2901                                 };
2902
2903                                 dom.bind(t.getBody(), 'click', doFocus);
2904                                 dom.bind(t.getBody(), 'keydown', doFocus);
2905                         }
2906
2907                         // #endif
2908
2909                         // Fixes bug where a specified document_base_uri could result in broken images
2910                         // This will also fix drag drop of images in Gecko
2911                         if (tinymce.isGecko) {
2912                                 dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {
2913                                         var v;
2914
2915                                         e = e.target;
2916
2917                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))
2918                                                 e.src = t.documentBaseURI.toAbsolute(v);
2919                                 });
2920                         }
2921
2922                         // Set various midas options in Gecko
2923                         if (isGecko) {
2924                                 function setOpts() {
2925                                         var t = this, d = t.getDoc(), s = t.settings;
2926
2927                                         if (isGecko && !s.readonly) {
2928                                                 if (t._isHidden()) {
2929                                                         try {
2930                                                                 if (!s.content_editable)
2931                                                                         d.designMode = 'On';
2932                                                         } catch (ex) {
2933                                                                 // Fails if it's hidden
2934                                                         }
2935                                                 }
2936
2937                                                 try {
2938                                                         // Try new Gecko method
2939                                                         d.execCommand("styleWithCSS", 0, false);
2940                                                 } catch (ex) {
2941                                                         // Use old method
2942                                                         if (!t._isHidden())
2943                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}
2944                                                 }
2945
2946                                                 if (!s.table_inline_editing)
2947                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
2948
2949                                                 if (!s.object_resizing)
2950                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
2951                                         }
2952                                 };
2953
2954                                 t.onBeforeExecCommand.add(setOpts);
2955                                 t.onMouseDown.add(setOpts);
2956                         }
2957
2958                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
2959                         // WebKit can't even do simple things like selecting an image
2960                         // This also fixes so it's possible to select mceItemAnchors
2961                         if (tinymce.isWebKit) {
2962                                 t.onClick.add(function(ed, e) {
2963                                         e = e.target;
2964
2965                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images
2966                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))) {
2967                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);
2968                                                 t.nodeChanged();
2969                                         }
2970                                 });
2971                         }
2972
2973                         // Add node change handlers
2974                         t.onMouseUp.add(t.nodeChanged);
2975                         //t.onClick.add(t.nodeChanged);
2976                         t.onKeyUp.add(function(ed, e) {
2977                                 var c = e.keyCode;
2978
2979                                 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)
2980                                         t.nodeChanged();
2981                         });
2982
2983                         // Add reset handler
2984                         t.onReset.add(function() {
2985                                 t.setContent(t.startContent, {format : 'raw'});
2986                         });
2987
2988                         // Add shortcuts
2989                         if (s.custom_shortcuts) {
2990                                 if (s.custom_undo_redo_keyboard_shortcuts) {
2991                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
2992                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
2993                                 }
2994
2995                                 // Add default shortcuts for gecko
2996                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
2997                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
2998                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
2999
3000                                 // BlockFormat shortcuts keys
3001                                 for (i=1; i<=6; i++)
3002                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
3003
3004                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
3005                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
3006                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);
3007
3008                                 function find(e) {
3009                                         var v = null;
3010
3011                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)
3012                                                 return v;
3013
3014                                         each(t.shortcuts, function(o) {
3015                                                 if (tinymce.isMac && o.ctrl != e.metaKey)
3016                                                         return;
3017                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
3018                                                         return;
3019
3020                                                 if (o.alt != e.altKey)
3021                                                         return;
3022
3023                                                 if (o.shift != e.shiftKey)
3024                                                         return;
3025
3026                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
3027                                                         v = o;
3028                                                         return false;
3029                                                 }
3030                                         });
3031
3032                                         return v;
3033                                 };
3034
3035                                 t.onKeyUp.add(function(ed, e) {
3036                                         var o = find(e);
3037
3038                                         if (o)
3039                                                 return Event.cancel(e);
3040                                 });
3041
3042                                 t.onKeyPress.add(function(ed, e) {
3043                                         var o = find(e);
3044
3045                                         if (o)
3046                                                 return Event.cancel(e);
3047                                 });
3048
3049                                 t.onKeyDown.add(function(ed, e) {
3050                                         var o = find(e);
3051
3052                                         if (o) {
3053                                                 o.func.call(o.scope);
3054                                                 return Event.cancel(e);
3055                                         }
3056                                 });
3057                         }
3058
3059                         if (tinymce.isIE) {
3060                                 // Fix so resize will only update the width and height attributes not the styles of an image
3061                                 // It will also block mceItemNoResize items
3062                                 dom.bind(t.getDoc(), 'controlselect', function(e) {
3063                                         var re = t.resizeInfo, cb;
3064
3065                                         e = e.target;
3066
3067                                         // Don't do this action for non image elements
3068                                         if (e.nodeName !== 'IMG')
3069                                                 return;
3070
3071                                         if (re)
3072                                                 dom.unbind(re.node, re.ev, re.cb);
3073
3074                                         if (!dom.hasClass(e, 'mceItemNoResize')) {
3075                                                 ev = 'resizeend';
3076                                                 cb = dom.bind(e, ev, function(e) {
3077                                                         var v;
3078
3079                                                         e = e.target;
3080
3081                                                         if (v = dom.getStyle(e, 'width')) {
3082                                                                 dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
3083                                                                 dom.setStyle(e, 'width', '');
3084                                                         }
3085
3086                                                         if (v = dom.getStyle(e, 'height')) {
3087                                                                 dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
3088                                                                 dom.setStyle(e, 'height', '');
3089                                                         }
3090                                                 });
3091                                         } else {
3092                                                 ev = 'resizestart';
3093                                                 cb = dom.bind(e, 'resizestart', Event.cancel, Event);
3094                                         }
3095
3096                                         re = t.resizeInfo = {
3097                                                 node : e,
3098                                                 ev : ev,
3099                                                 cb : cb
3100                                         };
3101                                 });
3102
3103                                 t.onKeyDown.add(function(ed, e) {
3104                                         var sel;
3105
3106                                         switch (e.keyCode) {
3107                                                 case 8:
3108                                                         sel = t.getDoc().selection;
3109
3110                                                         // Fix IE control + backspace browser bug
3111                                                         if (sel.createRange && sel.createRange().item) {
3112                                                                 ed.dom.remove(sel.createRange().item(0));
3113                                                                 return Event.cancel(e);
3114                                                         }
3115                                         }
3116                                 });
3117                         }
3118
3119                         if (tinymce.isOpera) {
3120                                 t.onClick.add(function(ed, e) {
3121                                         Event.prevent(e);
3122                                 });
3123                         }
3124
3125                         // Add custom undo/redo handlers
3126                         if (s.custom_undo_redo) {
3127                                 function addUndo() {
3128                                         t.undoManager.typing = false;
3129                                         t.undoManager.add();
3130                                 };
3131
3132                                 dom.bind(t.getDoc(), 'focusout', function(e) {
3133                                         if (!t.removed && t.undoManager.typing)
3134                                                 addUndo();
3135                                 });
3136
3137                                 // Add undo level when contents is drag/dropped within the editor
3138                                 t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {
3139                                         addUndo();
3140                                 });
3141
3142                                 t.onKeyUp.add(function(ed, e) {
3143                                         var rng, parent, bookmark;
3144
3145                                         // Fix for bug #3168, to remove odd ".." nodes from the DOM we need to get/set the HTML of the parent node.
3146                                         if (isIE && e.keyCode == 8) {
3147                                                 rng = t.selection.getRng();
3148                                                 if (rng.parentElement) {
3149                                                         parent = rng.parentElement();
3150                                                         bookmark = t.selection.getBookmark();
3151                                                         parent.innerHTML = parent.innerHTML;
3152                                                         t.selection.moveToBookmark(bookmark);
3153                                                 }
3154                                         }
3155
3156                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)
3157                                                 addUndo();
3158                                 });
3159
3160                                 t.onKeyDown.add(function(ed, e) {
3161                                         var rng, parent, bookmark, keyCode = e.keyCode;
3162
3163                                         // IE has a really odd bug where the DOM might include an node that doesn't have
3164                                         // a proper structure. If you try to access nodeValue it would throw an illegal value exception.
3165                                         // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element
3166                                         // after you delete contents from it. See: #3008923
3167                                         if (isIE && keyCode == 46) {
3168                                                 rng = t.selection.getRng();
3169
3170                                                 if (rng.parentElement) {
3171                                                         parent = rng.parentElement();
3172
3173                                                         if (!t.undoManager.typing) {
3174                                                                 t.undoManager.beforeChange();
3175                                                                 t.undoManager.typing = true;
3176                                                                 t.undoManager.add();
3177                                                         }
3178
3179                                                         // Select next word when ctrl key is used in combo with delete
3180                                                         if (e.ctrlKey) {
3181                                                                 rng.moveEnd('word', 1);
3182                                                                 rng.select();
3183                                                         }
3184
3185                                                         // Delete contents
3186                                                         t.selection.getSel().clear();
3187
3188                                                         // Check if we are within the same parent
3189                                                         if (rng.parentElement() == parent) {
3190                                                                 bookmark = t.selection.getBookmark();
3191
3192                                                                 try {
3193                                                                         // Update the HTML and hopefully it will remove the artifacts
3194                                                                         parent.innerHTML = parent.innerHTML;
3195                                                                 } catch (ex) {
3196                                                                         // And since it's IE it can sometimes produce an unknown runtime error
3197                                                                 }
3198
3199                                                                 // Restore the caret position
3200                                                                 t.selection.moveToBookmark(bookmark);
3201                                                         }
3202
3203                                                         // Block the default delete behavior since it might be broken
3204                                                         e.preventDefault();
3205                                                         return;
3206                                                 }
3207                                         }
3208
3209                                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
3210                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {
3211                                                 // Add position before enter key is pressed, used by IE since it still uses the default browser behavior
3212                                                 // Todo: Remove this once we normalize enter behavior on IE
3213                                                 if (tinymce.isIE && keyCode == 13)
3214                                                         t.undoManager.beforeChange();
3215
3216                                                 if (t.undoManager.typing)
3217                                                         addUndo();
3218
3219                                                 return;
3220                                         }
3221
3222                                         // If key isn't shift,ctrl,alt,capslock,metakey
3223                                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {
3224                                                 t.undoManager.beforeChange();
3225                                                 t.undoManager.add();
3226                                                 t.undoManager.typing = true;
3227                                         }
3228                                 });
3229
3230                                 t.onMouseDown.add(function() {
3231                                         if (t.undoManager.typing)
3232                                                 addUndo();
3233                                 });
3234                         }
3235                         
3236                         // Bug fix for FireFox keeping styles from end of selection instead of start.
3237                         if (tinymce.isGecko) {
3238                                 function getAttributeApplyFunction() {
3239                                         var template = t.dom.getAttribs(t.selection.getStart().cloneNode(false));
3240
3241                                         return function() {
3242                                                 var target = t.selection.getStart();
3243                                                 t.dom.removeAllAttribs(target);
3244                                                 each(template, function(attr) {
3245                                                         target.setAttributeNode(attr.cloneNode(true));
3246                                                 });
3247                                         };
3248                                 }
3249
3250                                 function isSelectionAcrossElements() {
3251                                         var s = t.selection;
3252
3253                                         return !s.isCollapsed() && s.getStart() != s.getEnd();
3254                                 }
3255
3256                                 t.onKeyPress.add(function(ed, e) {
3257                                         var applyAttributes;
3258
3259                                         if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
3260                                                 applyAttributes = getAttributeApplyFunction();
3261                                                 t.getDoc().execCommand('delete', false, null);
3262                                                 applyAttributes();
3263
3264                                                 return Event.cancel(e);
3265                                         }
3266                                 });
3267
3268                                 t.dom.bind(t.getDoc(), 'cut', function(e) {
3269                                         var applyAttributes;
3270
3271                                         if (isSelectionAcrossElements()) {
3272                                                 applyAttributes = getAttributeApplyFunction();
3273                                                 t.onKeyUp.addToTop(Event.cancel, Event);
3274
3275                                                 setTimeout(function() {
3276                                                         applyAttributes();
3277                                                         t.onKeyUp.remove(Event.cancel, Event);
3278                                                 }, 0);
3279                                         }
3280                                 });
3281                         }
3282                 },
3283
3284                 _isHidden : function() {
3285                         var s;
3286
3287                         if (!isGecko)
3288                                 return 0;
3289
3290                         // Weird, wheres that cursor selection?
3291                         s = this.selection.getSel();
3292                         return (!s || !s.rangeCount || s.rangeCount == 0);
3293                 }
3294         });
3295 })(tinymce);