src/pyams_skin/resources/js/ext/tinymce/dev/classes/Editor.js
changeset 69 a361355b55c7
equal deleted inserted replaced
68:fd8fb93e1b6a 69:a361355b55c7
       
     1 /**
       
     2  * Editor.js
       
     3  *
       
     4  * Copyright, Moxiecode Systems AB
       
     5  * Released under LGPL License.
       
     6  *
       
     7  * License: http://www.tinymce.com/license
       
     8  * Contributing: http://www.tinymce.com/contributing
       
     9  */
       
    10 
       
    11 /*jshint scripturl:true */
       
    12 
       
    13 /**
       
    14  * Include the base event class documentation.
       
    15  *
       
    16  * @include ../../../tools/docs/tinymce.Event.js
       
    17  */
       
    18 
       
    19 /**
       
    20  * This class contains the core logic for a TinyMCE editor.
       
    21  *
       
    22  * @class tinymce.Editor
       
    23  * @mixes tinymce.util.Observable
       
    24  * @example
       
    25  * // Add a class to all paragraphs in the editor.
       
    26  * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
    27  *
       
    28  * // Gets the current editors selection as text
       
    29  * tinymce.activeEditor.selection.getContent({format: 'text'});
       
    30  *
       
    31  * // Creates a new editor instance
       
    32  * var ed = new tinymce.Editor('textareaid', {
       
    33  *     some_setting: 1
       
    34  * }, tinymce.EditorManager);
       
    35  *
       
    36  * // Select each item the user clicks on
       
    37  * ed.on('click', function(e) {
       
    38  *     ed.selection.select(e.target);
       
    39  * });
       
    40  *
       
    41  * ed.render();
       
    42  */
       
    43 define("tinymce/Editor", [
       
    44 	"tinymce/dom/DOMUtils",
       
    45 	"tinymce/dom/DomQuery",
       
    46 	"tinymce/AddOnManager",
       
    47 	"tinymce/NodeChange",
       
    48 	"tinymce/html/Node",
       
    49 	"tinymce/dom/Serializer",
       
    50 	"tinymce/html/Serializer",
       
    51 	"tinymce/dom/Selection",
       
    52 	"tinymce/Formatter",
       
    53 	"tinymce/UndoManager",
       
    54 	"tinymce/EnterKey",
       
    55 	"tinymce/ForceBlocks",
       
    56 	"tinymce/EditorCommands",
       
    57 	"tinymce/util/URI",
       
    58 	"tinymce/dom/ScriptLoader",
       
    59 	"tinymce/dom/EventUtils",
       
    60 	"tinymce/WindowManager",
       
    61 	"tinymce/html/Schema",
       
    62 	"tinymce/html/DomParser",
       
    63 	"tinymce/util/Quirks",
       
    64 	"tinymce/Env",
       
    65 	"tinymce/util/Tools",
       
    66 	"tinymce/EditorObservable",
       
    67 	"tinymce/Shortcuts"
       
    68 ], function(
       
    69 	DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
       
    70 	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
       
    71 	URI, ScriptLoader, EventUtils, WindowManager,
       
    72 	Schema, DomParser, Quirks, Env, Tools, EditorObservable, Shortcuts
       
    73 ) {
       
    74 	// Shorten these names
       
    75 	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
       
    76 	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
       
    77 	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
       
    78 	var Event = EventUtils.Event;
       
    79 	var isGecko = Env.gecko, ie = Env.ie;
       
    80 
       
    81 	/**
       
    82 	 * Include documentation for all the events.
       
    83 	 *
       
    84 	 * @include ../../../tools/docs/tinymce.Editor.js
       
    85 	 */
       
    86 
       
    87 	/**
       
    88 	 * Constructs a editor instance by id.
       
    89 	 *
       
    90 	 * @constructor
       
    91 	 * @method Editor
       
    92 	 * @param {String} id Unique id for the editor.
       
    93 	 * @param {Object} settings Settings for the editor.
       
    94 	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
       
    95 	 * @author Moxiecode
       
    96 	 */
       
    97 	function Editor(id, settings, editorManager) {
       
    98 		var self = this, documentBaseUrl, baseUri;
       
    99 
       
   100 		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
       
   101 		baseUri = editorManager.baseURI;
       
   102 
       
   103 		/**
       
   104 		 * Name/value collection with editor settings.
       
   105 		 *
       
   106 		 * @property settings
       
   107 		 * @type Object
       
   108 		 * @example
       
   109 		 * // Get the value of the theme setting
       
   110 		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
       
   111 		 */
       
   112 		self.settings = settings = extend({
       
   113 			id: id,
       
   114 			theme: 'modern',
       
   115 			delta_width: 0,
       
   116 			delta_height: 0,
       
   117 			popup_css: '',
       
   118 			plugins: '',
       
   119 			document_base_url: documentBaseUrl,
       
   120 			add_form_submit_trigger: true,
       
   121 			submit_patch: true,
       
   122 			add_unload_trigger: true,
       
   123 			convert_urls: true,
       
   124 			relative_urls: true,
       
   125 			remove_script_host: true,
       
   126 			object_resizing: true,
       
   127 			doctype: '<!DOCTYPE html>',
       
   128 			visual: true,
       
   129 			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
       
   130 
       
   131 			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
       
   132 			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
       
   133 			forced_root_block: 'p',
       
   134 			hidden_input: true,
       
   135 			padd_empty_editor: true,
       
   136 			render_ui: true,
       
   137 			indentation: '30px',
       
   138 			inline_styles: true,
       
   139 			convert_fonts_to_spans: true,
       
   140 			indent: 'simple',
       
   141 			indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' +
       
   142 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
       
   143 			indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' +
       
   144 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
       
   145 			validate: true,
       
   146 			entity_encoding: 'named',
       
   147 			url_converter: self.convertURL,
       
   148 			url_converter_scope: self,
       
   149 			ie7_compat: true
       
   150 		}, settings);
       
   151 
       
   152 		AddOnManager.language = settings.language || 'en';
       
   153 		AddOnManager.languageLoad = settings.language_load;
       
   154 
       
   155 		AddOnManager.baseURL = editorManager.baseURL;
       
   156 
       
   157 		/**
       
   158 		 * Editor instance id, normally the same as the div/textarea that was replaced.
       
   159 		 *
       
   160 		 * @property id
       
   161 		 * @type String
       
   162 		 */
       
   163 		self.id = settings.id = id;
       
   164 
       
   165 		/**
       
   166 		 * State to force the editor to return false on a isDirty call.
       
   167 		 *
       
   168 		 * @property isNotDirty
       
   169 		 * @type Boolean
       
   170 		 * @example
       
   171 		 * function ajaxSave() {
       
   172 		 *     var ed = tinymce.get('elm1');
       
   173 		 *
       
   174 		 *     // Save contents using some XHR call
       
   175 		 *     alert(ed.getContent());
       
   176 		 *
       
   177 		 *     ed.isNotDirty = true; // Force not dirty state
       
   178 		 * }
       
   179 		 */
       
   180 		self.isNotDirty = true;
       
   181 
       
   182 		/**
       
   183 		 * Name/Value object containting plugin instances.
       
   184 		 *
       
   185 		 * @property plugins
       
   186 		 * @type Object
       
   187 		 * @example
       
   188 		 * // Execute a method inside a plugin directly
       
   189 		 * tinymce.activeEditor.plugins.someplugin.someMethod();
       
   190 		 */
       
   191 		self.plugins = {};
       
   192 
       
   193 		/**
       
   194 		 * URI object to document configured for the TinyMCE instance.
       
   195 		 *
       
   196 		 * @property documentBaseURI
       
   197 		 * @type tinymce.util.URI
       
   198 		 * @example
       
   199 		 * // Get relative URL from the location of document_base_url
       
   200 		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
       
   201 		 *
       
   202 		 * // Get absolute URL from the location of document_base_url
       
   203 		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
       
   204 		 */
       
   205 		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
       
   206 			base_uri: baseUri
       
   207 		});
       
   208 
       
   209 		/**
       
   210 		 * URI object to current document that holds the TinyMCE editor instance.
       
   211 		 *
       
   212 		 * @property baseURI
       
   213 		 * @type tinymce.util.URI
       
   214 		 * @example
       
   215 		 * // Get relative URL from the location of the API
       
   216 		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
       
   217 		 *
       
   218 		 * // Get absolute URL from the location of the API
       
   219 		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
       
   220 		 */
       
   221 		self.baseURI = baseUri;
       
   222 
       
   223 		/**
       
   224 		 * Array with CSS files to load into the iframe.
       
   225 		 *
       
   226 		 * @property contentCSS
       
   227 		 * @type Array
       
   228 		 */
       
   229 		self.contentCSS = [];
       
   230 
       
   231 		/**
       
   232 		 * Array of CSS styles to add to head of document when the editor loads.
       
   233 		 *
       
   234 		 * @property contentStyles
       
   235 		 * @type Array
       
   236 		 */
       
   237 		self.contentStyles = [];
       
   238 
       
   239 		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
       
   240 		self.shortcuts = new Shortcuts(self);
       
   241 		self.loadedCSS = {};
       
   242 		self.editorCommands = new EditorCommands(self);
       
   243 
       
   244 		if (settings.target) {
       
   245 			self.targetElm = settings.target;
       
   246 		}
       
   247 
       
   248 		self.suffix = editorManager.suffix;
       
   249 		self.editorManager = editorManager;
       
   250 		self.inline = settings.inline;
       
   251 
       
   252 		if (settings.cache_suffix) {
       
   253 			Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, '');
       
   254 		}
       
   255 
       
   256 		// Call setup
       
   257 		editorManager.fire('SetupEditor', self);
       
   258 		self.execCallback('setup', self);
       
   259 
       
   260 		/**
       
   261 		 * Dom query instance with default scope to the editor document and default element is the body of the editor.
       
   262 		 *
       
   263 		 * @property $
       
   264 		 * @type tinymce.dom.DomQuery
       
   265 		 * @example
       
   266 		 * tinymce.activeEditor.$('p').css('color', 'red');
       
   267 		 * tinymce.activeEditor.$().append('<p>new</p>');
       
   268 		 */
       
   269 		self.$ = DomQuery.overrideDefaults(function() {
       
   270 			return {
       
   271 				context: self.inline ? self.getBody() : self.getDoc(),
       
   272 				element: self.getBody()
       
   273 			};
       
   274 		});
       
   275 	}
       
   276 
       
   277 	Editor.prototype = {
       
   278 		/**
       
   279 		 * Renderes the editor/adds it to the page.
       
   280 		 *
       
   281 		 * @method render
       
   282 		 */
       
   283 		render: function() {
       
   284 			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
       
   285 
       
   286 			function readyHandler() {
       
   287 				DOM.unbind(window, 'ready', readyHandler);
       
   288 				self.render();
       
   289 			}
       
   290 
       
   291 			// Page is not loaded yet, wait for it
       
   292 			if (!Event.domLoaded) {
       
   293 				DOM.bind(window, 'ready', readyHandler);
       
   294 				return;
       
   295 			}
       
   296 
       
   297 			// Element not found, then skip initialization
       
   298 			if (!self.getElement()) {
       
   299 				return;
       
   300 			}
       
   301 
       
   302 			// No editable support old iOS versions etc
       
   303 			if (!Env.contentEditable) {
       
   304 				return;
       
   305 			}
       
   306 
       
   307 			// Hide target element early to prevent content flashing
       
   308 			if (!settings.inline) {
       
   309 				self.orgVisibility = self.getElement().style.visibility;
       
   310 				self.getElement().style.visibility = 'hidden';
       
   311 			} else {
       
   312 				self.inline = true;
       
   313 			}
       
   314 
       
   315 			var form = self.getElement().form || DOM.getParent(id, 'form');
       
   316 			if (form) {
       
   317 				self.formElement = form;
       
   318 
       
   319 				// Add hidden input for non input elements inside form elements
       
   320 				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
       
   321 					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
       
   322 					self.hasHiddenInput = true;
       
   323 				}
       
   324 
       
   325 				// Pass submit/reset from form to editor instance
       
   326 				self.formEventDelegate = function(e) {
       
   327 					self.fire(e.type, e);
       
   328 				};
       
   329 
       
   330 				DOM.bind(form, 'submit reset', self.formEventDelegate);
       
   331 
       
   332 				// Reset contents in editor when the form is reset
       
   333 				self.on('reset', function() {
       
   334 					self.setContent(self.startContent, {format: 'raw'});
       
   335 				});
       
   336 
       
   337 				// Check page uses id="submit" or name="submit" for it's submit button
       
   338 				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
       
   339 					form._mceOldSubmit = form.submit;
       
   340 					form.submit = function() {
       
   341 						self.editorManager.triggerSave();
       
   342 						self.isNotDirty = true;
       
   343 
       
   344 						return form._mceOldSubmit(form);
       
   345 					};
       
   346 				}
       
   347 			}
       
   348 
       
   349 			/**
       
   350 			 * Window manager reference, use this to open new windows and dialogs.
       
   351 			 *
       
   352 			 * @property windowManager
       
   353 			 * @type tinymce.WindowManager
       
   354 			 * @example
       
   355 			 * // Shows an alert message
       
   356 			 * tinymce.activeEditor.windowManager.alert('Hello world!');
       
   357 			 *
       
   358 			 * // Opens a new dialog with the file.htm file and the size 320x240
       
   359 			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
       
   360 			 * tinymce.activeEditor.windowManager.open({
       
   361 			 *    url: 'file.htm',
       
   362 			 *    width: 320,
       
   363 			 *    height: 240
       
   364 			 * }, {
       
   365 			 *    custom_param: 1
       
   366 			 * });
       
   367 			 */
       
   368 			self.windowManager = new WindowManager(self);
       
   369 
       
   370 			if (settings.encoding == 'xml') {
       
   371 				self.on('GetContent', function(e) {
       
   372 					if (e.save) {
       
   373 						e.content = DOM.encode(e.content);
       
   374 					}
       
   375 				});
       
   376 			}
       
   377 
       
   378 			if (settings.add_form_submit_trigger) {
       
   379 				self.on('submit', function() {
       
   380 					if (self.initialized) {
       
   381 						self.save();
       
   382 					}
       
   383 				});
       
   384 			}
       
   385 
       
   386 			if (settings.add_unload_trigger) {
       
   387 				self._beforeUnload = function() {
       
   388 					if (self.initialized && !self.destroyed && !self.isHidden()) {
       
   389 						self.save({format: 'raw', no_events: true, set_dirty: false});
       
   390 					}
       
   391 				};
       
   392 
       
   393 				self.editorManager.on('BeforeUnload', self._beforeUnload);
       
   394 			}
       
   395 
       
   396 			// Load scripts
       
   397 			function loadScripts() {
       
   398 				var scriptLoader = ScriptLoader.ScriptLoader;
       
   399 
       
   400 				if (settings.language && settings.language != 'en' && !settings.language_url) {
       
   401 					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
       
   402 				}
       
   403 
       
   404 				if (settings.language_url) {
       
   405 					scriptLoader.add(settings.language_url);
       
   406 				}
       
   407 
       
   408 				if (settings.theme && typeof settings.theme != "function" &&
       
   409 					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
       
   410 					var themeUrl = settings.theme_url;
       
   411 
       
   412 					if (themeUrl) {
       
   413 						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
       
   414 					} else {
       
   415 						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
       
   416 					}
       
   417 
       
   418 					ThemeManager.load(settings.theme, themeUrl);
       
   419 				}
       
   420 
       
   421 				if (Tools.isArray(settings.plugins)) {
       
   422 					settings.plugins = settings.plugins.join(' ');
       
   423 				}
       
   424 
       
   425 				each(settings.external_plugins, function(url, name) {
       
   426 					PluginManager.load(name, url);
       
   427 					settings.plugins += ' ' + name;
       
   428 				});
       
   429 
       
   430 				each(settings.plugins.split(/[ ,]/), function(plugin) {
       
   431 					plugin = trim(plugin);
       
   432 
       
   433 					if (plugin && !PluginManager.urls[plugin]) {
       
   434 						if (plugin.charAt(0) == '-') {
       
   435 							plugin = plugin.substr(1, plugin.length);
       
   436 
       
   437 							var dependencies = PluginManager.dependencies(plugin);
       
   438 
       
   439 							each(dependencies, function(dep) {
       
   440 								var defaultSettings = {
       
   441 									prefix: 'plugins/',
       
   442 									resource: dep,
       
   443 									suffix: '/plugin' + suffix + '.js'
       
   444 								};
       
   445 
       
   446 								dep = PluginManager.createUrl(defaultSettings, dep);
       
   447 								PluginManager.load(dep.resource, dep);
       
   448 							});
       
   449 						} else {
       
   450 							PluginManager.load(plugin, {
       
   451 								prefix: 'plugins/',
       
   452 								resource: plugin,
       
   453 								suffix: '/plugin' + suffix + '.js'
       
   454 							});
       
   455 						}
       
   456 					}
       
   457 				});
       
   458 
       
   459 				scriptLoader.loadQueue(function() {
       
   460 					if (!self.removed) {
       
   461 						self.init();
       
   462 					}
       
   463 				});
       
   464 			}
       
   465 
       
   466 			loadScripts();
       
   467 		},
       
   468 
       
   469 		/**
       
   470 		 * Initializes the editor this will be called automatically when
       
   471 		 * all plugins/themes and language packs are loaded by the rendered method.
       
   472 		 * This method will setup the iframe and create the theme and plugin instances.
       
   473 		 *
       
   474 		 * @method init
       
   475 		 */
       
   476 		init: function() {
       
   477 			var self = this, settings = self.settings, elm = self.getElement();
       
   478 			var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
       
   479 
       
   480 			this.editorManager.i18n.setCode(settings.language);
       
   481 			self.rtl = this.editorManager.i18n.rtl;
       
   482 			self.editorManager.add(self);
       
   483 
       
   484 			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
       
   485 
       
   486 			/**
       
   487 			 * Reference to the theme instance that was used to generate the UI.
       
   488 			 *
       
   489 			 * @property theme
       
   490 			 * @type tinymce.Theme
       
   491 			 * @example
       
   492 			 * // Executes a method on the theme directly
       
   493 			 * tinymce.activeEditor.theme.someMethod();
       
   494 			 */
       
   495 			if (settings.theme) {
       
   496 				if (typeof settings.theme != "function") {
       
   497 					settings.theme = settings.theme.replace(/-/, '');
       
   498 					Theme = ThemeManager.get(settings.theme);
       
   499 					self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
       
   500 
       
   501 					if (self.theme.init) {
       
   502 						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''), self.$);
       
   503 					}
       
   504 				} else {
       
   505 					self.theme = settings.theme;
       
   506 				}
       
   507 			}
       
   508 
       
   509 			function initPlugin(plugin) {
       
   510 				var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
       
   511 
       
   512 				pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
       
   513 				plugin = trim(plugin);
       
   514 				if (Plugin && inArray(initializedPlugins, plugin) === -1) {
       
   515 					each(PluginManager.dependencies(plugin), function(dep) {
       
   516 						initPlugin(dep);
       
   517 					});
       
   518 
       
   519 					pluginInstance = new Plugin(self, pluginUrl, self.$);
       
   520 
       
   521 					self.plugins[plugin] = pluginInstance;
       
   522 
       
   523 					if (pluginInstance.init) {
       
   524 						pluginInstance.init(self, pluginUrl);
       
   525 						initializedPlugins.push(plugin);
       
   526 					}
       
   527 				}
       
   528 			}
       
   529 
       
   530 			// Create all plugins
       
   531 			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
       
   532 
       
   533 			// Measure box
       
   534 			if (settings.render_ui && self.theme) {
       
   535 				self.orgDisplay = elm.style.display;
       
   536 
       
   537 				if (typeof settings.theme != "function") {
       
   538 					w = settings.width || elm.style.width || elm.offsetWidth;
       
   539 					h = settings.height || elm.style.height || elm.offsetHeight;
       
   540 					minHeight = settings.min_height || 100;
       
   541 					re = /^[0-9\.]+(|px)$/i;
       
   542 
       
   543 					if (re.test('' + w)) {
       
   544 						w = Math.max(parseInt(w, 10), 100);
       
   545 					}
       
   546 
       
   547 					if (re.test('' + h)) {
       
   548 						h = Math.max(parseInt(h, 10), minHeight);
       
   549 					}
       
   550 
       
   551 					// Render UI
       
   552 					o = self.theme.renderUI({
       
   553 						targetNode: elm,
       
   554 						width: w,
       
   555 						height: h,
       
   556 						deltaWidth: settings.delta_width,
       
   557 						deltaHeight: settings.delta_height
       
   558 					});
       
   559 
       
   560 					// Resize editor
       
   561 					if (!settings.content_editable) {
       
   562 						h = (o.iframeHeight || h) + (typeof h == 'number' ? (o.deltaHeight || 0) : '');
       
   563 						if (h < minHeight) {
       
   564 							h = minHeight;
       
   565 						}
       
   566 					}
       
   567 				} else {
       
   568 					o = settings.theme(self, elm);
       
   569 
       
   570 					// Convert element type to id:s
       
   571 					if (o.editorContainer.nodeType) {
       
   572 						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
       
   573 					}
       
   574 
       
   575 					// Convert element type to id:s
       
   576 					if (o.iframeContainer.nodeType) {
       
   577 						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
       
   578 					}
       
   579 
       
   580 					// Use specified iframe height or the targets offsetHeight
       
   581 					h = o.iframeHeight || elm.offsetHeight;
       
   582 				}
       
   583 
       
   584 				self.editorContainer = o.editorContainer;
       
   585 			}
       
   586 
       
   587 			// Load specified content CSS last
       
   588 			if (settings.content_css) {
       
   589 				each(explode(settings.content_css), function(u) {
       
   590 					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
       
   591 				});
       
   592 			}
       
   593 
       
   594 			// Load specified content CSS last
       
   595 			if (settings.content_style) {
       
   596 				self.contentStyles.push(settings.content_style);
       
   597 			}
       
   598 
       
   599 			// Content editable mode ends here
       
   600 			if (settings.content_editable) {
       
   601 				elm = n = o = null; // Fix IE leak
       
   602 				return self.initContentBody();
       
   603 			}
       
   604 
       
   605 			self.iframeHTML = settings.doctype + '<html><head>';
       
   606 
       
   607 			// We only need to override paths if we have to
       
   608 			// IE has a bug where it remove site absolute urls to relative ones if this is specified
       
   609 			if (settings.document_base_url != self.documentBaseUrl) {
       
   610 				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
       
   611 			}
       
   612 
       
   613 			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
       
   614 			if (!Env.caretAfter && settings.ie7_compat) {
       
   615 				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
       
   616 			}
       
   617 
       
   618 			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
       
   619 
       
   620 			// Load the CSS by injecting them into the HTML this will reduce "flicker"
       
   621 			for (i = 0; i < self.contentCSS.length; i++) {
       
   622 				var cssUrl = self.contentCSS[i];
       
   623 				self.iframeHTML += (
       
   624 					'<link type="text/css" ' +
       
   625 						'rel="stylesheet" ' +
       
   626 						'href="' + Tools._addCacheSuffix(cssUrl) + '" />'
       
   627 				);
       
   628 				self.loadedCSS[cssUrl] = true;
       
   629 			}
       
   630 
       
   631 			bodyId = settings.body_id || 'tinymce';
       
   632 			if (bodyId.indexOf('=') != -1) {
       
   633 				bodyId = self.getParam('body_id', '', 'hash');
       
   634 				bodyId = bodyId[self.id] || bodyId;
       
   635 			}
       
   636 
       
   637 			bodyClass = settings.body_class || '';
       
   638 			if (bodyClass.indexOf('=') != -1) {
       
   639 				bodyClass = self.getParam('body_class', '', 'hash');
       
   640 				bodyClass = bodyClass[self.id] || '';
       
   641 			}
       
   642 
       
   643 			if (settings.content_security_policy) {
       
   644 				self.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />';
       
   645 			}
       
   646 
       
   647 			self.iframeHTML += '</head><body id="' + bodyId +
       
   648 				'" class="mce-content-body ' + bodyClass +
       
   649 				'" data-id="' + self.id + '"><br></body></html>';
       
   650 
       
   651 			/*eslint no-script-url:0 */
       
   652 			var domainRelaxUrl = 'javascript:(function(){' +
       
   653 				'document.open();document.domain="' + document.domain + '";' +
       
   654 				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
       
   655 				'document.close();ed.initContentBody(true);})()';
       
   656 
       
   657 			// Domain relaxing is required since the user has messed around with document.domain
       
   658 			if (document.domain != location.hostname) {
       
   659 				url = domainRelaxUrl;
       
   660 			}
       
   661 
       
   662 			// Create iframe
       
   663 			// TODO: ACC add the appropriate description on this.
       
   664 			var ifr = DOM.create('iframe', {
       
   665 				id: self.id + "_ifr",
       
   666 				//src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
       
   667 				frameBorder: '0',
       
   668 				allowTransparency: "true",
       
   669 				title: self.editorManager.translate(
       
   670 						"Rich Text Area. Press ALT-F9 for menu. " +
       
   671 						"Press ALT-F10 for toolbar. Press ALT-0 for help"
       
   672 				),
       
   673 				style: {
       
   674 					width: '100%',
       
   675 					height: h,
       
   676 					display: 'block' // Important for Gecko to render the iframe correctly
       
   677 				}
       
   678 			});
       
   679 
       
   680 			ifr.onload = function() {
       
   681 				ifr.onload = null;
       
   682 				self.fire("load");
       
   683 			};
       
   684 
       
   685 			DOM.setAttrib(ifr, "src", url || 'javascript:""');
       
   686 
       
   687 			self.contentAreaContainer = o.iframeContainer;
       
   688 			self.iframeElement = ifr;
       
   689 
       
   690 			n = DOM.add(o.iframeContainer, ifr);
       
   691 
       
   692 			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
       
   693 			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
       
   694 			if (ie) {
       
   695 				try {
       
   696 					self.getDoc();
       
   697 				} catch (e) {
       
   698 					n.src = url = domainRelaxUrl;
       
   699 				}
       
   700 			}
       
   701 
       
   702 			if (o.editorContainer) {
       
   703 				DOM.get(o.editorContainer).style.display = self.orgDisplay;
       
   704 				self.hidden = DOM.isHidden(o.editorContainer);
       
   705 			}
       
   706 
       
   707 			self.getElement().style.display = 'none';
       
   708 			DOM.setAttrib(self.id, 'aria-hidden', true);
       
   709 
       
   710 			if (!url) {
       
   711 				self.initContentBody();
       
   712 			}
       
   713 
       
   714 			elm = n = o = null; // Cleanup
       
   715 		},
       
   716 
       
   717 		/**
       
   718 		 * This method get called by the init method ones the iframe is loaded.
       
   719 		 * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
       
   720 		 *
       
   721 		 * @method initContentBody
       
   722 		 * @private
       
   723 		 */
       
   724 		initContentBody: function(skipWrite) {
       
   725 			var self = this, settings = self.settings, targetElm = self.getElement(), doc = self.getDoc(), body, contentCssText;
       
   726 
       
   727 			// Restore visibility on target element
       
   728 			if (!settings.inline) {
       
   729 				self.getElement().style.visibility = self.orgVisibility;
       
   730 			}
       
   731 
       
   732 			// Setup iframe body
       
   733 			if (!skipWrite && !settings.content_editable) {
       
   734 				doc.open();
       
   735 				doc.write(self.iframeHTML);
       
   736 				doc.close();
       
   737 			}
       
   738 
       
   739 			if (settings.content_editable) {
       
   740 				self.on('remove', function() {
       
   741 					var bodyEl = this.getBody();
       
   742 
       
   743 					DOM.removeClass(bodyEl, 'mce-content-body');
       
   744 					DOM.removeClass(bodyEl, 'mce-edit-focus');
       
   745 					DOM.setAttrib(bodyEl, 'contentEditable', null);
       
   746 				});
       
   747 
       
   748 				DOM.addClass(targetElm, 'mce-content-body');
       
   749 				self.contentDocument = doc = settings.content_document || document;
       
   750 				self.contentWindow = settings.content_window || window;
       
   751 				self.bodyElement = targetElm;
       
   752 
       
   753 				// Prevent leak in IE
       
   754 				settings.content_document = settings.content_window = null;
       
   755 
       
   756 				// TODO: Fix this
       
   757 				settings.root_name = targetElm.nodeName.toLowerCase();
       
   758 			}
       
   759 
       
   760 			// It will not steal focus while setting contentEditable
       
   761 			body = self.getBody();
       
   762 			body.disabled = true;
       
   763 
       
   764 			if (!settings.readonly) {
       
   765 				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
       
   766 					body.style.position = 'relative';
       
   767 				}
       
   768 
       
   769 				body.contentEditable = self.getParam('content_editable_state', true);
       
   770 			}
       
   771 
       
   772 			body.disabled = false;
       
   773 
       
   774 			/**
       
   775 			 * Schema instance, enables you to validate elements and it's children.
       
   776 			 *
       
   777 			 * @property schema
       
   778 			 * @type tinymce.html.Schema
       
   779 			 */
       
   780 			self.schema = new Schema(settings);
       
   781 
       
   782 			/**
       
   783 			 * DOM instance for the editor.
       
   784 			 *
       
   785 			 * @property dom
       
   786 			 * @type tinymce.dom.DOMUtils
       
   787 			 * @example
       
   788 			 * // Adds a class to all paragraphs within the editor
       
   789 			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
   790 			 */
       
   791 			self.dom = new DOMUtils(doc, {
       
   792 				keep_values: true,
       
   793 				url_converter: self.convertURL,
       
   794 				url_converter_scope: self,
       
   795 				hex_colors: settings.force_hex_style_colors,
       
   796 				class_filter: settings.class_filter,
       
   797 				update_styles: true,
       
   798 				root_element: self.inline ? self.getBody() : null,
       
   799 				collect: settings.content_editable,
       
   800 				schema: self.schema,
       
   801 				onSetAttrib: function(e) {
       
   802 					self.fire('SetAttrib', e);
       
   803 				}
       
   804 			});
       
   805 
       
   806 			/**
       
   807 			 * HTML parser will be used when contents is inserted into the editor.
       
   808 			 *
       
   809 			 * @property parser
       
   810 			 * @type tinymce.html.DomParser
       
   811 			 */
       
   812 			self.parser = new DomParser(settings, self.schema);
       
   813 
       
   814 			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
       
   815 			self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
       
   816 				var i = nodes.length, node, dom = self.dom, value, internalName;
       
   817 
       
   818 				while (i--) {
       
   819 					node = nodes[i];
       
   820 					value = node.attr(name);
       
   821 					internalName = 'data-mce-' + name;
       
   822 
       
   823 					// Add internal attribute if we need to we don't on a refresh of the document
       
   824 					if (!node.attributes.map[internalName]) {
       
   825 						if (name === "style") {
       
   826 							value = dom.serializeStyle(dom.parseStyle(value), node.name);
       
   827 
       
   828 							if (!value.length) {
       
   829 								value = null;
       
   830 							}
       
   831 
       
   832 							node.attr(internalName, value);
       
   833 							node.attr(name, value);
       
   834 						} else if (name === "tabindex") {
       
   835 							node.attr(internalName, value);
       
   836 							node.attr(name, null);
       
   837 						} else {
       
   838 							node.attr(internalName, self.convertURL(value, name, node.name));
       
   839 						}
       
   840 					}
       
   841 				}
       
   842 			});
       
   843 
       
   844 			// Keep scripts from executing
       
   845 			self.parser.addNodeFilter('script', function(nodes) {
       
   846 				var i = nodes.length, node;
       
   847 
       
   848 				while (i--) {
       
   849 					node = nodes[i];
       
   850 					node.attr('type', 'mce-' + (node.attr('type') || 'no/type'));
       
   851 				}
       
   852 			});
       
   853 
       
   854 			self.parser.addNodeFilter('#cdata', function(nodes) {
       
   855 				var i = nodes.length, node;
       
   856 
       
   857 				while (i--) {
       
   858 					node = nodes[i];
       
   859 					node.type = 8;
       
   860 					node.name = '#comment';
       
   861 					node.value = '[CDATA[' + node.value + ']]';
       
   862 				}
       
   863 			});
       
   864 
       
   865 			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
       
   866 				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
       
   867 
       
   868 				while (i--) {
       
   869 					node = nodes[i];
       
   870 
       
   871 					if (node.isEmpty(nonEmptyElements)) {
       
   872 						node.append(new Node('br', 1)).shortEnded = true;
       
   873 					}
       
   874 				}
       
   875 			});
       
   876 
       
   877 			/**
       
   878 			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
       
   879 			 *
       
   880 			 * @property serializer
       
   881 			 * @type tinymce.dom.Serializer
       
   882 			 * @example
       
   883 			 * // Serializes the first paragraph in the editor into a string
       
   884 			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
       
   885 			 */
       
   886 			self.serializer = new DomSerializer(settings, self);
       
   887 
       
   888 			/**
       
   889 			 * Selection instance for the editor.
       
   890 			 *
       
   891 			 * @property selection
       
   892 			 * @type tinymce.dom.Selection
       
   893 			 * @example
       
   894 			 * // Sets some contents to the current selection in the editor
       
   895 			 * tinymce.activeEditor.selection.setContent('Some contents');
       
   896 			 *
       
   897 			 * // Gets the current selection
       
   898 			 * alert(tinymce.activeEditor.selection.getContent());
       
   899 			 *
       
   900 			 * // Selects the first paragraph found
       
   901 			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
       
   902 			 */
       
   903 			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
       
   904 
       
   905 			/**
       
   906 			 * Formatter instance.
       
   907 			 *
       
   908 			 * @property formatter
       
   909 			 * @type tinymce.Formatter
       
   910 			 */
       
   911 			self.formatter = new Formatter(self);
       
   912 
       
   913 			/**
       
   914 			 * Undo manager instance, responsible for handling undo levels.
       
   915 			 *
       
   916 			 * @property undoManager
       
   917 			 * @type tinymce.UndoManager
       
   918 			 * @example
       
   919 			 * // Undoes the last modification to the editor
       
   920 			 * tinymce.activeEditor.undoManager.undo();
       
   921 			 */
       
   922 			self.undoManager = new UndoManager(self);
       
   923 
       
   924 			self.forceBlocks = new ForceBlocks(self);
       
   925 			self.enterKey = new EnterKey(self);
       
   926 			self._nodeChangeDispatcher = new NodeChange(self);
       
   927 
       
   928 			self.fire('PreInit');
       
   929 
       
   930 			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
       
   931 				doc.body.spellcheck = false; // Gecko
       
   932 				DOM.setAttrib(body, "spellcheck", "false");
       
   933 			}
       
   934 
       
   935 			self.fire('PostRender');
       
   936 
       
   937 			self.quirks = new Quirks(self);
       
   938 
       
   939 			if (settings.directionality) {
       
   940 				body.dir = settings.directionality;
       
   941 			}
       
   942 
       
   943 			if (settings.nowrap) {
       
   944 				body.style.whiteSpace = "nowrap";
       
   945 			}
       
   946 
       
   947 			if (settings.protect) {
       
   948 				self.on('BeforeSetContent', function(e) {
       
   949 					each(settings.protect, function(pattern) {
       
   950 						e.content = e.content.replace(pattern, function(str) {
       
   951 							return '<!--mce:protected ' + escape(str) + '-->';
       
   952 						});
       
   953 					});
       
   954 				});
       
   955 			}
       
   956 
       
   957 			self.on('SetContent', function() {
       
   958 				self.addVisual(self.getBody());
       
   959 			});
       
   960 
       
   961 			// Remove empty contents
       
   962 			if (settings.padd_empty_editor) {
       
   963 				self.on('PostProcess', function(e) {
       
   964 					e.content = e.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
       
   965 				});
       
   966 			}
       
   967 
       
   968 			self.load({initial: true, format: 'html'});
       
   969 			self.startContent = self.getContent({format: 'raw'});
       
   970 
       
   971 			/**
       
   972 			 * Is set to true after the editor instance has been initialized
       
   973 			 *
       
   974 			 * @property initialized
       
   975 			 * @type Boolean
       
   976 			 * @example
       
   977 			 * function isEditorInitialized(editor) {
       
   978 			 *     return editor && editor.initialized;
       
   979 			 * }
       
   980 			 */
       
   981 			self.initialized = true;
       
   982 			self.bindPendingEventDelegates();
       
   983 
       
   984 			self.fire('init');
       
   985 			self.focus(true);
       
   986 			self.nodeChanged({initial: true});
       
   987 			self.execCallback('init_instance_callback', self);
       
   988 
       
   989 			// Add editor specific CSS styles
       
   990 			if (self.contentStyles.length > 0) {
       
   991 				contentCssText = '';
       
   992 
       
   993 				each(self.contentStyles, function(style) {
       
   994 					contentCssText += style + "\r\n";
       
   995 				});
       
   996 
       
   997 				self.dom.addStyle(contentCssText);
       
   998 			}
       
   999 
       
  1000 			// Load specified content CSS last
       
  1001 			each(self.contentCSS, function(cssUrl) {
       
  1002 				if (!self.loadedCSS[cssUrl]) {
       
  1003 					self.dom.loadCSS(cssUrl);
       
  1004 					self.loadedCSS[cssUrl] = true;
       
  1005 				}
       
  1006 			});
       
  1007 
       
  1008 			// Handle auto focus
       
  1009 			if (settings.auto_focus) {
       
  1010 				setTimeout(function() {
       
  1011 					var editor;
       
  1012 
       
  1013 					if (settings.auto_focus === true) {
       
  1014 						editor = self;
       
  1015 					} else {
       
  1016 						editor = self.editorManager.get(settings.auto_focus);
       
  1017 					}
       
  1018 
       
  1019 					if (!editor.destroyed) {
       
  1020 						editor.focus();
       
  1021 					}
       
  1022 				}, 100);
       
  1023 			}
       
  1024 
       
  1025 			// Clean up references for IE
       
  1026 			targetElm = doc = body = null;
       
  1027 		},
       
  1028 
       
  1029 		/**
       
  1030 		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
       
  1031 		 * it will also place DOM focus inside the editor.
       
  1032 		 *
       
  1033 		 * @method focus
       
  1034 		 * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor.
       
  1035 		 */
       
  1036 		focus: function(skipFocus) {
       
  1037 			var self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
       
  1038 			var controlElm, doc = self.getDoc(), body;
       
  1039 
       
  1040 			if (!skipFocus) {
       
  1041 				// Get selected control element
       
  1042 				rng = selection.getRng();
       
  1043 				if (rng.item) {
       
  1044 					controlElm = rng.item(0);
       
  1045 				}
       
  1046 
       
  1047 				self._refreshContentEditable();
       
  1048 
       
  1049 				// Focus the window iframe
       
  1050 				if (!contentEditable) {
       
  1051 					// WebKit needs this call to fire focusin event properly see #5948
       
  1052 					// But Opera pre Blink engine will produce an empty selection so skip Opera
       
  1053 					if (!Env.opera) {
       
  1054 						self.getBody().focus();
       
  1055 					}
       
  1056 
       
  1057 					self.getWin().focus();
       
  1058 				}
       
  1059 
       
  1060 				// Focus the body as well since it's contentEditable
       
  1061 				if (isGecko || contentEditable) {
       
  1062 					body = self.getBody();
       
  1063 
       
  1064 					// Check for setActive since it doesn't scroll to the element
       
  1065 					if (body.setActive) {
       
  1066 						// IE 11 sometimes throws "Invalid function" then fallback to focus
       
  1067 						try {
       
  1068 							body.setActive();
       
  1069 						} catch (ex) {
       
  1070 							body.focus();
       
  1071 						}
       
  1072 					} else {
       
  1073 						body.focus();
       
  1074 					}
       
  1075 
       
  1076 					if (contentEditable) {
       
  1077 						selection.normalize();
       
  1078 					}
       
  1079 				}
       
  1080 
       
  1081 				// Restore selected control element
       
  1082 				// This is needed when for example an image is selected within a
       
  1083 				// layer a call to focus will then remove the control selection
       
  1084 				if (controlElm && controlElm.ownerDocument == doc) {
       
  1085 					rng = doc.body.createControlRange();
       
  1086 					rng.addElement(controlElm);
       
  1087 					rng.select();
       
  1088 				}
       
  1089 			}
       
  1090 
       
  1091 			self.editorManager.setActive(self);
       
  1092 		},
       
  1093 
       
  1094 		/**
       
  1095 		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
       
  1096 		 * There new event model is a better way to add callback so this method might be removed in the future.
       
  1097 		 *
       
  1098 		 * @method execCallback
       
  1099 		 * @param {String} name Name of the callback to execute.
       
  1100 		 * @return {Object} Return value passed from callback function.
       
  1101 		 */
       
  1102 		execCallback: function(name) {
       
  1103 			var self = this, callback = self.settings[name], scope;
       
  1104 
       
  1105 			if (!callback) {
       
  1106 				return;
       
  1107 			}
       
  1108 
       
  1109 			// Look through lookup
       
  1110 			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
       
  1111 				callback = scope.func;
       
  1112 				scope = scope.scope;
       
  1113 			}
       
  1114 
       
  1115 			if (typeof callback === 'string') {
       
  1116 				scope = callback.replace(/\.\w+$/, '');
       
  1117 				scope = scope ? resolve(scope) : 0;
       
  1118 				callback = resolve(callback);
       
  1119 				self.callbackLookup = self.callbackLookup || {};
       
  1120 				self.callbackLookup[name] = {func: callback, scope: scope};
       
  1121 			}
       
  1122 
       
  1123 			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
       
  1124 		},
       
  1125 
       
  1126 		/**
       
  1127 		 * Translates the specified string by replacing variables with language pack items it will also check if there is
       
  1128 		 * a key mathcin the input.
       
  1129 		 *
       
  1130 		 * @method translate
       
  1131 		 * @param {String} text String to translate by the language pack data.
       
  1132 		 * @return {String} Translated string.
       
  1133 		 */
       
  1134 		translate: function(text) {
       
  1135 			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
       
  1136 
       
  1137 			if (!text) {
       
  1138 				return '';
       
  1139 			}
       
  1140 
       
  1141 			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
       
  1142 				return i18n.data[lang + '.' + b] || '{#' + b + '}';
       
  1143 			});
       
  1144 		},
       
  1145 
       
  1146 		/**
       
  1147 		 * Returns a language pack item by name/key.
       
  1148 		 *
       
  1149 		 * @method getLang
       
  1150 		 * @param {String} name Name/key to get from the language pack.
       
  1151 		 * @param {String} defaultVal Optional default value to retrive.
       
  1152 		 */
       
  1153 		getLang: function(name, defaultVal) {
       
  1154 			return (
       
  1155 				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
       
  1156 				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
       
  1157 			);
       
  1158 		},
       
  1159 
       
  1160 		/**
       
  1161 		 * Returns a configuration parameter by name.
       
  1162 		 *
       
  1163 		 * @method getParam
       
  1164 		 * @param {String} name Configruation parameter to retrive.
       
  1165 		 * @param {String} defaultVal Optional default value to return.
       
  1166 		 * @param {String} type Optional type parameter.
       
  1167 		 * @return {String} Configuration parameter value or default value.
       
  1168 		 * @example
       
  1169 		 * // Returns a specific config value from the currently active editor
       
  1170 		 * var someval = tinymce.activeEditor.getParam('myvalue');
       
  1171 		 *
       
  1172 		 * // Returns a specific config value from a specific editor instance by id
       
  1173 		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
       
  1174 		 */
       
  1175 		getParam: function(name, defaultVal, type) {
       
  1176 			var value = name in this.settings ? this.settings[name] : defaultVal, output;
       
  1177 
       
  1178 			if (type === 'hash') {
       
  1179 				output = {};
       
  1180 
       
  1181 				if (typeof value === 'string') {
       
  1182 					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
       
  1183 						value = value.split('=');
       
  1184 
       
  1185 						if (value.length > 1) {
       
  1186 							output[trim(value[0])] = trim(value[1]);
       
  1187 						} else {
       
  1188 							output[trim(value[0])] = trim(value);
       
  1189 						}
       
  1190 					});
       
  1191 				} else {
       
  1192 					output = value;
       
  1193 				}
       
  1194 
       
  1195 				return output;
       
  1196 			}
       
  1197 
       
  1198 			return value;
       
  1199 		},
       
  1200 
       
  1201 		/**
       
  1202 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
       
  1203 		 * need to update the UI states or element path etc.
       
  1204 		 *
       
  1205 		 * @method nodeChanged
       
  1206 		 * @param {Object} args Optional args to pass to NodeChange event handlers.
       
  1207 		 */
       
  1208 		nodeChanged: function(args) {
       
  1209 			this._nodeChangeDispatcher.nodeChanged(args);
       
  1210 		},
       
  1211 
       
  1212 		/**
       
  1213 		 * Adds a button that later gets created by the theme in the editors toolbars.
       
  1214 		 *
       
  1215 		 * @method addButton
       
  1216 		 * @param {String} name Button name to add.
       
  1217 		 * @param {Object} settings Settings object with title, cmd etc.
       
  1218 		 * @example
       
  1219 		 * // Adds a custom button to the editor that inserts contents when clicked
       
  1220 		 * tinymce.init({
       
  1221 		 *    ...
       
  1222 		 *
       
  1223 		 *    toolbar: 'example'
       
  1224 		 *
       
  1225 		 *    setup: function(ed) {
       
  1226 		 *       ed.addButton('example', {
       
  1227 		 *          title: 'My title',
       
  1228 		 *          image: '../js/tinymce/plugins/example/img/example.gif',
       
  1229 		 *          onclick: function() {
       
  1230 		 *             ed.insertContent('Hello world!!');
       
  1231 		 *          }
       
  1232 		 *       });
       
  1233 		 *    }
       
  1234 		 * });
       
  1235 		 */
       
  1236 		addButton: function(name, settings) {
       
  1237 			var self = this;
       
  1238 
       
  1239 			if (settings.cmd) {
       
  1240 				settings.onclick = function() {
       
  1241 					self.execCommand(settings.cmd);
       
  1242 				};
       
  1243 			}
       
  1244 
       
  1245 			if (!settings.text && !settings.icon) {
       
  1246 				settings.icon = name;
       
  1247 			}
       
  1248 
       
  1249 			self.buttons = self.buttons || {};
       
  1250 			settings.tooltip = settings.tooltip || settings.title;
       
  1251 			self.buttons[name] = settings;
       
  1252 		},
       
  1253 
       
  1254 		/**
       
  1255 		 * Adds a menu item to be used in the menus of the theme. There might be multiple instances
       
  1256 		 * of this menu item for example it might be used in the main menus of the theme but also in
       
  1257 		 * the context menu so make sure that it's self contained and supports multiple instances.
       
  1258 		 *
       
  1259 		 * @method addMenuItem
       
  1260 		 * @param {String} name Menu item name to add.
       
  1261 		 * @param {Object} settings Settings object with title, cmd etc.
       
  1262 		 * @example
       
  1263 		 * // Adds a custom menu item to the editor that inserts contents when clicked
       
  1264 		 * // The context option allows you to add the menu item to an existing default menu
       
  1265 		 * tinymce.init({
       
  1266 		 *    ...
       
  1267 		 *
       
  1268 		 *    setup: function(ed) {
       
  1269 		 *       ed.addMenuItem('example', {
       
  1270 		 *          text: 'My menu item',
       
  1271 		 *          context: 'tools',
       
  1272 		 *          onclick: function() {
       
  1273 		 *             ed.insertContent('Hello world!!');
       
  1274 		 *          }
       
  1275 		 *       });
       
  1276 		 *    }
       
  1277 		 * });
       
  1278 		 */
       
  1279 		addMenuItem: function(name, settings) {
       
  1280 			var self = this;
       
  1281 
       
  1282 			if (settings.cmd) {
       
  1283 				settings.onclick = function() {
       
  1284 					self.execCommand(settings.cmd);
       
  1285 				};
       
  1286 			}
       
  1287 
       
  1288 			self.menuItems = self.menuItems || {};
       
  1289 			self.menuItems[name] = settings;
       
  1290 		},
       
  1291 
       
  1292 		/**
       
  1293 		 * Adds a custom command to the editor, you can also override existing commands with this method.
       
  1294 		 * The command that you add can be executed with execCommand.
       
  1295 		 *
       
  1296 		 * @method addCommand
       
  1297 		 * @param {String} name Command name to add/override.
       
  1298 		 * @param {addCommandCallback} callback Function to execute when the command occurs.
       
  1299 		 * @param {Object} scope Optional scope to execute the function in.
       
  1300 		 * @example
       
  1301 		 * // Adds a custom command that later can be executed using execCommand
       
  1302 		 * tinymce.init({
       
  1303 		 *    ...
       
  1304 		 *
       
  1305 		 *    setup: function(ed) {
       
  1306 		 *       // Register example command
       
  1307 		 *       ed.addCommand('mycommand', function(ui, v) {
       
  1308 		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
       
  1309 		 *       });
       
  1310 		 *    }
       
  1311 		 * });
       
  1312 		 */
       
  1313 		addCommand: function(name, callback, scope) {
       
  1314 			/**
       
  1315 			 * Callback function that gets called when a command is executed.
       
  1316 			 *
       
  1317 			 * @callback addCommandCallback
       
  1318 			 * @param {Boolean} ui Display UI state true/false.
       
  1319 			 * @param {Object} value Optional value for command.
       
  1320 			 * @return {Boolean} True/false state if the command was handled or not.
       
  1321 			 */
       
  1322 			this.editorCommands.addCommand(name, callback, scope);
       
  1323 		},
       
  1324 
       
  1325 		/**
       
  1326 		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
       
  1327 		 * The command that you add can be executed with queryCommandState function.
       
  1328 		 *
       
  1329 		 * @method addQueryStateHandler
       
  1330 		 * @param {String} name Command name to add/override.
       
  1331 		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
       
  1332 		 * @param {Object} scope Optional scope to execute the function in.
       
  1333 		 */
       
  1334 		addQueryStateHandler: function(name, callback, scope) {
       
  1335 			/**
       
  1336 			 * Callback function that gets called when a queryCommandState is executed.
       
  1337 			 *
       
  1338 			 * @callback addQueryStateHandlerCallback
       
  1339 			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
       
  1340 			 */
       
  1341 			this.editorCommands.addQueryStateHandler(name, callback, scope);
       
  1342 		},
       
  1343 
       
  1344 		/**
       
  1345 		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
       
  1346 		 * The command that you add can be executed with queryCommandValue function.
       
  1347 		 *
       
  1348 		 * @method addQueryValueHandler
       
  1349 		 * @param {String} name Command name to add/override.
       
  1350 		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
       
  1351 		 * @param {Object} scope Optional scope to execute the function in.
       
  1352 		 */
       
  1353 		addQueryValueHandler: function(name, callback, scope) {
       
  1354 			/**
       
  1355 			 * Callback function that gets called when a queryCommandValue is executed.
       
  1356 			 *
       
  1357 			 * @callback addQueryValueHandlerCallback
       
  1358 			 * @return {Object} Value of the command or undefined.
       
  1359 			 */
       
  1360 			this.editorCommands.addQueryValueHandler(name, callback, scope);
       
  1361 		},
       
  1362 
       
  1363 		/**
       
  1364 		 * Adds a keyboard shortcut for some command or function.
       
  1365 		 *
       
  1366 		 * @method addShortcut
       
  1367 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
  1368 		 * @param {String} desc Text description for the command.
       
  1369 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
       
  1370 		 * @param {Object} sc Optional scope to execute the function in.
       
  1371 		 * @return {Boolean} true/false state if the shortcut was added or not.
       
  1372 		 */
       
  1373 		addShortcut: function(pattern, desc, cmdFunc, scope) {
       
  1374 			this.shortcuts.add(pattern, desc, cmdFunc, scope);
       
  1375 		},
       
  1376 
       
  1377 		/**
       
  1378 		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
       
  1379 		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
       
  1380 		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
       
  1381 		 * return true it will handle the command as a internal browser command.
       
  1382 		 *
       
  1383 		 * @method execCommand
       
  1384 		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
       
  1385 		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
       
  1386 		 * @param {mixed} value Optional command value, this can be anything.
       
  1387 		 * @param {Object} args Optional arguments object.
       
  1388 		 */
       
  1389 		execCommand: function(cmd, ui, value, args) {
       
  1390 			return this.editorCommands.execCommand(cmd, ui, value, args);
       
  1391 		},
       
  1392 
       
  1393 		/**
       
  1394 		 * Returns a command specific state, for example if bold is enabled or not.
       
  1395 		 *
       
  1396 		 * @method queryCommandState
       
  1397 		 * @param {string} cmd Command to query state from.
       
  1398 		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
       
  1399 		 */
       
  1400 		queryCommandState: function(cmd) {
       
  1401 			return this.editorCommands.queryCommandState(cmd);
       
  1402 		},
       
  1403 
       
  1404 		/**
       
  1405 		 * Returns a command specific value, for example the current font size.
       
  1406 		 *
       
  1407 		 * @method queryCommandValue
       
  1408 		 * @param {string} cmd Command to query value from.
       
  1409 		 * @return {Object} Command specific value, for example the current font size.
       
  1410 		 */
       
  1411 		queryCommandValue: function(cmd) {
       
  1412 			return this.editorCommands.queryCommandValue(cmd);
       
  1413 		},
       
  1414 
       
  1415 		/**
       
  1416 		 * Returns true/false if the command is supported or not.
       
  1417 		 *
       
  1418 		 * @method queryCommandSupported
       
  1419 		 * @param {String} cmd Command that we check support for.
       
  1420 		 * @return {Boolean} true/false if the command is supported or not.
       
  1421 		 */
       
  1422 		queryCommandSupported: function(cmd) {
       
  1423 			return this.editorCommands.queryCommandSupported(cmd);
       
  1424 		},
       
  1425 
       
  1426 		/**
       
  1427 		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
       
  1428 		 *
       
  1429 		 * @method show
       
  1430 		 */
       
  1431 		show: function() {
       
  1432 			var self = this;
       
  1433 
       
  1434 			if (self.hidden) {
       
  1435 				self.hidden = false;
       
  1436 
       
  1437 				if (self.inline) {
       
  1438 					self.getBody().contentEditable = true;
       
  1439 				} else {
       
  1440 					DOM.show(self.getContainer());
       
  1441 					DOM.hide(self.id);
       
  1442 				}
       
  1443 
       
  1444 				self.load();
       
  1445 				self.fire('show');
       
  1446 			}
       
  1447 		},
       
  1448 
       
  1449 		/**
       
  1450 		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
       
  1451 		 *
       
  1452 		 * @method hide
       
  1453 		 */
       
  1454 		hide: function() {
       
  1455 			var self = this, doc = self.getDoc();
       
  1456 
       
  1457 			if (!self.hidden) {
       
  1458 				// Fixed bug where IE has a blinking cursor left from the editor
       
  1459 				if (ie && doc && !self.inline) {
       
  1460 					doc.execCommand('SelectAll');
       
  1461 				}
       
  1462 
       
  1463 				// We must save before we hide so Safari doesn't crash
       
  1464 				self.save();
       
  1465 
       
  1466 				if (self.inline) {
       
  1467 					self.getBody().contentEditable = false;
       
  1468 
       
  1469 					// Make sure the editor gets blurred
       
  1470 					if (self == self.editorManager.focusedEditor) {
       
  1471 						self.editorManager.focusedEditor = null;
       
  1472 					}
       
  1473 				} else {
       
  1474 					DOM.hide(self.getContainer());
       
  1475 					DOM.setStyle(self.id, 'display', self.orgDisplay);
       
  1476 				}
       
  1477 
       
  1478 				self.hidden = true;
       
  1479 				self.fire('hide');
       
  1480 			}
       
  1481 		},
       
  1482 
       
  1483 		/**
       
  1484 		 * Returns true/false if the editor is hidden or not.
       
  1485 		 *
       
  1486 		 * @method isHidden
       
  1487 		 * @return {Boolean} True/false if the editor is hidden or not.
       
  1488 		 */
       
  1489 		isHidden: function() {
       
  1490 			return !!this.hidden;
       
  1491 		},
       
  1492 
       
  1493 		/**
       
  1494 		 * Sets the progress state, this will display a throbber/progess for the editor.
       
  1495 		 * This is ideal for asycronous operations like an AJAX save call.
       
  1496 		 *
       
  1497 		 * @method setProgressState
       
  1498 		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
       
  1499 		 * @param {Number} time Optional time to wait before the progress gets shown.
       
  1500 		 * @return {Boolean} Same as the input state.
       
  1501 		 * @example
       
  1502 		 * // Show progress for the active editor
       
  1503 		 * tinymce.activeEditor.setProgressState(true);
       
  1504 		 *
       
  1505 		 * // Hide progress for the active editor
       
  1506 		 * tinymce.activeEditor.setProgressState(false);
       
  1507 		 *
       
  1508 		 * // Show progress after 3 seconds
       
  1509 		 * tinymce.activeEditor.setProgressState(true, 3000);
       
  1510 		 */
       
  1511 		setProgressState: function(state, time) {
       
  1512 			this.fire('ProgressState', {state: state, time: time});
       
  1513 		},
       
  1514 
       
  1515 		/**
       
  1516 		 * Loads contents from the textarea or div element that got converted into an editor instance.
       
  1517 		 * This method will move the contents from that textarea or div into the editor by using setContent
       
  1518 		 * so all events etc that method has will get dispatched as well.
       
  1519 		 *
       
  1520 		 * @method load
       
  1521 		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
       
  1522 		 * @return {String} HTML string that got set into the editor.
       
  1523 		 */
       
  1524 		load: function(args) {
       
  1525 			var self = this, elm = self.getElement(), html;
       
  1526 
       
  1527 			if (elm) {
       
  1528 				args = args || {};
       
  1529 				args.load = true;
       
  1530 
       
  1531 				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
       
  1532 				args.element = elm;
       
  1533 
       
  1534 				if (!args.no_events) {
       
  1535 					self.fire('LoadContent', args);
       
  1536 				}
       
  1537 
       
  1538 				args.element = elm = null;
       
  1539 
       
  1540 				return html;
       
  1541 			}
       
  1542 		},
       
  1543 
       
  1544 		/**
       
  1545 		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
       
  1546 		 * This method will move the HTML contents from the editor into that textarea or div by getContent
       
  1547 		 * so all events etc that method has will get dispatched as well.
       
  1548 		 *
       
  1549 		 * @method save
       
  1550 		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
       
  1551 		 * @return {String} HTML string that got set into the textarea/div.
       
  1552 		 */
       
  1553 		save: function(args) {
       
  1554 			var self = this, elm = self.getElement(), html, form;
       
  1555 
       
  1556 			if (!elm || !self.initialized) {
       
  1557 				return;
       
  1558 			}
       
  1559 
       
  1560 			args = args || {};
       
  1561 			args.save = true;
       
  1562 
       
  1563 			args.element = elm;
       
  1564 			html = args.content = self.getContent(args);
       
  1565 
       
  1566 			if (!args.no_events) {
       
  1567 				self.fire('SaveContent', args);
       
  1568 			}
       
  1569 
       
  1570 			html = args.content;
       
  1571 
       
  1572 			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
       
  1573 				// Update DIV element when not in inline mode
       
  1574 				if (!self.inline) {
       
  1575 					elm.innerHTML = html;
       
  1576 				}
       
  1577 
       
  1578 				// Update hidden form element
       
  1579 				if ((form = DOM.getParent(self.id, 'form'))) {
       
  1580 					each(form.elements, function(elm) {
       
  1581 						if (elm.name == self.id) {
       
  1582 							elm.value = html;
       
  1583 							return false;
       
  1584 						}
       
  1585 					});
       
  1586 				}
       
  1587 			} else {
       
  1588 				elm.value = html;
       
  1589 			}
       
  1590 
       
  1591 			args.element = elm = null;
       
  1592 
       
  1593 			if (args.set_dirty !== false) {
       
  1594 				self.isNotDirty = true;
       
  1595 			}
       
  1596 
       
  1597 			return html;
       
  1598 		},
       
  1599 
       
  1600 		/**
       
  1601 		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
       
  1602 		 * the different cleanup rules options.
       
  1603 		 *
       
  1604 		 * @method setContent
       
  1605 		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
       
  1606 		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
       
  1607 		 * @return {String} HTML string that got set into the editor.
       
  1608 		 * @example
       
  1609 		 * // Sets the HTML contents of the activeEditor editor
       
  1610 		 * tinymce.activeEditor.setContent('<span>some</span> html');
       
  1611 		 *
       
  1612 		 * // Sets the raw contents of the activeEditor editor
       
  1613 		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
       
  1614 		 *
       
  1615 		 * // Sets the content of a specific editor (my_editor in this example)
       
  1616 		 * tinymce.get('my_editor').setContent(data);
       
  1617 		 *
       
  1618 		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
       
  1619 		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
       
  1620 		 */
       
  1621 		setContent: function(content, args) {
       
  1622 			var self = this, body = self.getBody(), forcedRootBlockName;
       
  1623 
       
  1624 			// Setup args object
       
  1625 			args = args || {};
       
  1626 			args.format = args.format || 'html';
       
  1627 			args.set = true;
       
  1628 			args.content = content;
       
  1629 
       
  1630 			// Do preprocessing
       
  1631 			if (!args.no_events) {
       
  1632 				self.fire('BeforeSetContent', args);
       
  1633 			}
       
  1634 
       
  1635 			content = args.content;
       
  1636 
       
  1637 			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
       
  1638 			// It will also be impossible to place the caret in the editor unless there is a BR element present
       
  1639 			if (content.length === 0 || /^\s+$/.test(content)) {
       
  1640 				forcedRootBlockName = self.settings.forced_root_block;
       
  1641 
       
  1642 				// Check if forcedRootBlock is configured and that the block is a valid child of the body
       
  1643 				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
       
  1644 					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
       
  1645 					content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
       
  1646 					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
       
  1647 				} else if (!ie) {
       
  1648 					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
       
  1649 					content = '<br data-mce-bogus="1">';
       
  1650 				}
       
  1651 
       
  1652 				self.dom.setHTML(body, content);
       
  1653 
       
  1654 				self.fire('SetContent', args);
       
  1655 			} else {
       
  1656 				// Parse and serialize the html
       
  1657 				if (args.format !== 'raw') {
       
  1658 					content = new Serializer({}, self.schema).serialize(
       
  1659 						self.parser.parse(content, {isRootContent: true})
       
  1660 					);
       
  1661 				}
       
  1662 
       
  1663 				// Set the new cleaned contents to the editor
       
  1664 				args.content = trim(content);
       
  1665 				self.dom.setHTML(body, args.content);
       
  1666 
       
  1667 				// Do post processing
       
  1668 				if (!args.no_events) {
       
  1669 					self.fire('SetContent', args);
       
  1670 				}
       
  1671 
       
  1672 				// Don't normalize selection if the focused element isn't the body in
       
  1673 				// content editable mode since it will steal focus otherwise
       
  1674 				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
       
  1675 					self.selection.normalize();
       
  1676 				}*/
       
  1677 			}
       
  1678 
       
  1679 			return args.content;
       
  1680 		},
       
  1681 
       
  1682 		/**
       
  1683 		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
       
  1684 		 * the different cleanup rules options.
       
  1685 		 *
       
  1686 		 * @method getContent
       
  1687 		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
       
  1688 		 * @return {String} Cleaned content string, normally HTML contents.
       
  1689 		 * @example
       
  1690 		 * // Get the HTML contents of the currently active editor
       
  1691 		 * console.debug(tinymce.activeEditor.getContent());
       
  1692 		 *
       
  1693 		 * // Get the raw contents of the currently active editor
       
  1694 		 * tinymce.activeEditor.getContent({format: 'raw'});
       
  1695 		 *
       
  1696 		 * // Get content of a specific editor:
       
  1697 		 * tinymce.get('content id').getContent()
       
  1698 		 */
       
  1699 		getContent: function(args) {
       
  1700 			var self = this, content, body = self.getBody();
       
  1701 
       
  1702 			// Setup args object
       
  1703 			args = args || {};
       
  1704 			args.format = args.format || 'html';
       
  1705 			args.get = true;
       
  1706 			args.getInner = true;
       
  1707 
       
  1708 			// Do preprocessing
       
  1709 			if (!args.no_events) {
       
  1710 				self.fire('BeforeGetContent', args);
       
  1711 			}
       
  1712 
       
  1713 			// Get raw contents or by default the cleaned contents
       
  1714 			if (args.format == 'raw') {
       
  1715 				content = body.innerHTML;
       
  1716 			} else if (args.format == 'text') {
       
  1717 				content = body.innerText || body.textContent;
       
  1718 			} else {
       
  1719 				content = self.serializer.serialize(body, args);
       
  1720 			}
       
  1721 
       
  1722 			// Trim whitespace in beginning/end of HTML
       
  1723 			if (args.format != 'text') {
       
  1724 				args.content = trim(content);
       
  1725 			} else {
       
  1726 				args.content = content;
       
  1727 			}
       
  1728 
       
  1729 			// Do post processing
       
  1730 			if (!args.no_events) {
       
  1731 				self.fire('GetContent', args);
       
  1732 			}
       
  1733 
       
  1734 			return args.content;
       
  1735 		},
       
  1736 
       
  1737 		/**
       
  1738 		 * Inserts content at caret position.
       
  1739 		 *
       
  1740 		 * @method insertContent
       
  1741 		 * @param {String} content Content to insert.
       
  1742 		 * @param {Object} args Optional args to pass to insert call.
       
  1743 		 */
       
  1744 		insertContent: function(content, args) {
       
  1745 			if (args) {
       
  1746 				content = extend({content: content}, args);
       
  1747 			}
       
  1748 
       
  1749 			this.execCommand('mceInsertContent', false, content);
       
  1750 		},
       
  1751 
       
  1752 		/**
       
  1753 		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
       
  1754 		 *
       
  1755 		 * @method isDirty
       
  1756 		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
       
  1757 		 * @example
       
  1758 		 * if (tinymce.activeEditor.isDirty())
       
  1759 		 *     alert("You must save your contents.");
       
  1760 		 */
       
  1761 		isDirty: function() {
       
  1762 			return !this.isNotDirty;
       
  1763 		},
       
  1764 
       
  1765 		/**
       
  1766 		 * Returns the editors container element. The container element wrappes in
       
  1767 		 * all the elements added to the page for the editor. Such as UI, iframe etc.
       
  1768 		 *
       
  1769 		 * @method getContainer
       
  1770 		 * @return {Element} HTML DOM element for the editor container.
       
  1771 		 */
       
  1772 		getContainer: function() {
       
  1773 			var self = this;
       
  1774 
       
  1775 			if (!self.container) {
       
  1776 				self.container = DOM.get(self.editorContainer || self.id + '_parent');
       
  1777 			}
       
  1778 
       
  1779 			return self.container;
       
  1780 		},
       
  1781 
       
  1782 		/**
       
  1783 		 * Returns the editors content area container element. The this element is the one who
       
  1784 		 * holds the iframe or the editable element.
       
  1785 		 *
       
  1786 		 * @method getContentAreaContainer
       
  1787 		 * @return {Element} HTML DOM element for the editor area container.
       
  1788 		 */
       
  1789 		getContentAreaContainer: function() {
       
  1790 			return this.contentAreaContainer;
       
  1791 		},
       
  1792 
       
  1793 		/**
       
  1794 		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
       
  1795 		 *
       
  1796 		 * @method getElement
       
  1797 		 * @return {Element} HTML DOM element for the replaced element.
       
  1798 		 */
       
  1799 		getElement: function() {
       
  1800 			if (!this.targetElm) {
       
  1801 				this.targetElm = DOM.get(this.id);
       
  1802 			}
       
  1803 
       
  1804 			return this.targetElm;
       
  1805 		},
       
  1806 
       
  1807 		/**
       
  1808 		 * Returns the iframes window object.
       
  1809 		 *
       
  1810 		 * @method getWin
       
  1811 		 * @return {Window} Iframe DOM window object.
       
  1812 		 */
       
  1813 		getWin: function() {
       
  1814 			var self = this, elm;
       
  1815 
       
  1816 			if (!self.contentWindow) {
       
  1817 				elm = self.iframeElement;
       
  1818 
       
  1819 				if (elm) {
       
  1820 					self.contentWindow = elm.contentWindow;
       
  1821 				}
       
  1822 			}
       
  1823 
       
  1824 			return self.contentWindow;
       
  1825 		},
       
  1826 
       
  1827 		/**
       
  1828 		 * Returns the iframes document object.
       
  1829 		 *
       
  1830 		 * @method getDoc
       
  1831 		 * @return {Document} Iframe DOM document object.
       
  1832 		 */
       
  1833 		getDoc: function() {
       
  1834 			var self = this, win;
       
  1835 
       
  1836 			if (!self.contentDocument) {
       
  1837 				win = self.getWin();
       
  1838 
       
  1839 				if (win) {
       
  1840 					self.contentDocument = win.document;
       
  1841 				}
       
  1842 			}
       
  1843 
       
  1844 			return self.contentDocument;
       
  1845 		},
       
  1846 
       
  1847 		/**
       
  1848 		 * Returns the root element of the editable area.
       
  1849 		 * For a non-inline iframe-based editor, returns the iframe's body element.
       
  1850 		 *
       
  1851 		 * @method getBody
       
  1852 		 * @return {Element} The root element of the editable area.
       
  1853 		 */
       
  1854 		getBody: function() {
       
  1855 			return this.bodyElement || this.getDoc().body;
       
  1856 		},
       
  1857 
       
  1858 		/**
       
  1859 		 * URL converter function this gets executed each time a user adds an img, a or
       
  1860 		 * any other element that has a URL in it. This will be called both by the DOM and HTML
       
  1861 		 * manipulation functions.
       
  1862 		 *
       
  1863 		 * @method convertURL
       
  1864 		 * @param {string} url URL to convert.
       
  1865 		 * @param {string} name Attribute name src, href etc.
       
  1866 		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
       
  1867 		 * @return {string} Converted URL string.
       
  1868 		 */
       
  1869 		convertURL: function(url, name, elm) {
       
  1870 			var self = this, settings = self.settings;
       
  1871 
       
  1872 			// Use callback instead
       
  1873 			if (settings.urlconverter_callback) {
       
  1874 				return self.execCallback('urlconverter_callback', url, elm, true, name);
       
  1875 			}
       
  1876 
       
  1877 			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
       
  1878 			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
       
  1879 				return url;
       
  1880 			}
       
  1881 
       
  1882 			// Convert to relative
       
  1883 			if (settings.relative_urls) {
       
  1884 				return self.documentBaseURI.toRelative(url);
       
  1885 			}
       
  1886 
       
  1887 			// Convert to absolute
       
  1888 			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
       
  1889 
       
  1890 			return url;
       
  1891 		},
       
  1892 
       
  1893 		/**
       
  1894 		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
       
  1895 		 *
       
  1896 		 * @method addVisual
       
  1897 		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
       
  1898 		 */
       
  1899 		addVisual: function(elm) {
       
  1900 			var self = this, settings = self.settings, dom = self.dom, cls;
       
  1901 
       
  1902 			elm = elm || self.getBody();
       
  1903 
       
  1904 			if (self.hasVisual === undefined) {
       
  1905 				self.hasVisual = settings.visual;
       
  1906 			}
       
  1907 
       
  1908 			each(dom.select('table,a', elm), function(elm) {
       
  1909 				var value;
       
  1910 
       
  1911 				switch (elm.nodeName) {
       
  1912 					case 'TABLE':
       
  1913 						cls = settings.visual_table_class || 'mce-item-table';
       
  1914 						value = dom.getAttrib(elm, 'border');
       
  1915 
       
  1916 						if ((!value || value == '0') && self.hasVisual) {
       
  1917 							dom.addClass(elm, cls);
       
  1918 						} else {
       
  1919 							dom.removeClass(elm, cls);
       
  1920 						}
       
  1921 
       
  1922 						return;
       
  1923 
       
  1924 					case 'A':
       
  1925 						if (!dom.getAttrib(elm, 'href', false)) {
       
  1926 							value = dom.getAttrib(elm, 'name') || elm.id;
       
  1927 							cls = settings.visual_anchor_class || 'mce-item-anchor';
       
  1928 
       
  1929 							if (value && self.hasVisual) {
       
  1930 								dom.addClass(elm, cls);
       
  1931 							} else {
       
  1932 								dom.removeClass(elm, cls);
       
  1933 							}
       
  1934 						}
       
  1935 
       
  1936 						return;
       
  1937 				}
       
  1938 			});
       
  1939 
       
  1940 			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
       
  1941 		},
       
  1942 
       
  1943 		/**
       
  1944 		 * Removes the editor from the dom and tinymce collection.
       
  1945 		 *
       
  1946 		 * @method remove
       
  1947 		 */
       
  1948 		remove: function() {
       
  1949 			var self = this;
       
  1950 
       
  1951 			if (!self.removed) {
       
  1952 				self.save();
       
  1953 				self.removed = 1;
       
  1954 				self.unbindAllNativeEvents();
       
  1955 
       
  1956 				// Remove any hidden input
       
  1957 				if (self.hasHiddenInput) {
       
  1958 					DOM.remove(self.getElement().nextSibling);
       
  1959 				}
       
  1960 
       
  1961 				if (!self.inline) {
       
  1962 					// IE 9 has a bug where the selection stops working if you place the
       
  1963 					// caret inside the editor then remove the iframe
       
  1964 					if (ie && ie < 10) {
       
  1965 						self.getDoc().execCommand('SelectAll', false, null);
       
  1966 					}
       
  1967 
       
  1968 					DOM.setStyle(self.id, 'display', self.orgDisplay);
       
  1969 					self.getBody().onload = null; // Prevent #6816
       
  1970 				}
       
  1971 
       
  1972 				self.fire('remove');
       
  1973 
       
  1974 				self.editorManager.remove(self);
       
  1975 				DOM.remove(self.getContainer());
       
  1976 				self.destroy();
       
  1977 			}
       
  1978 		},
       
  1979 
       
  1980 		/**
       
  1981 		 * Destroys the editor instance by removing all events, element references or other resources
       
  1982 		 * that could leak memory. This method will be called automatically when the page is unloaded
       
  1983 		 * but you can also call it directly if you know what you are doing.
       
  1984 		 *
       
  1985 		 * @method destroy
       
  1986 		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
       
  1987 		 */
       
  1988 		destroy: function(automatic) {
       
  1989 			var self = this, form;
       
  1990 
       
  1991 			// One time is enough
       
  1992 			if (self.destroyed) {
       
  1993 				return;
       
  1994 			}
       
  1995 
       
  1996 			// If user manually calls destroy and not remove
       
  1997 			// Users seems to have logic that calls destroy instead of remove
       
  1998 			if (!automatic && !self.removed) {
       
  1999 				self.remove();
       
  2000 				return;
       
  2001 			}
       
  2002 
       
  2003 			if (!automatic) {
       
  2004 				self.editorManager.off('beforeunload', self._beforeUnload);
       
  2005 
       
  2006 				// Manual destroy
       
  2007 				if (self.theme && self.theme.destroy) {
       
  2008 					self.theme.destroy();
       
  2009 				}
       
  2010 
       
  2011 				// Destroy controls, selection and dom
       
  2012 				self.selection.destroy();
       
  2013 				self.dom.destroy();
       
  2014 			}
       
  2015 
       
  2016 			form = self.formElement;
       
  2017 			if (form) {
       
  2018 				if (form._mceOldSubmit) {
       
  2019 					form.submit = form._mceOldSubmit;
       
  2020 					form._mceOldSubmit = null;
       
  2021 				}
       
  2022 
       
  2023 				DOM.unbind(form, 'submit reset', self.formEventDelegate);
       
  2024 			}
       
  2025 
       
  2026 			self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null;
       
  2027 			self.bodyElement = self.contentDocument = self.contentWindow = null;
       
  2028 			self.iframeElement = self.targetElm = null;
       
  2029 
       
  2030 			if (self.selection) {
       
  2031 				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
       
  2032 			}
       
  2033 
       
  2034 			self.destroyed = 1;
       
  2035 		},
       
  2036 
       
  2037 		// Internal functions
       
  2038 
       
  2039 		_refreshContentEditable: function() {
       
  2040 			var self = this, body, parent;
       
  2041 
       
  2042 			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
       
  2043 			if (self._isHidden()) {
       
  2044 				body = self.getBody();
       
  2045 				parent = body.parentNode;
       
  2046 
       
  2047 				parent.removeChild(body);
       
  2048 				parent.appendChild(body);
       
  2049 
       
  2050 				body.focus();
       
  2051 			}
       
  2052 		},
       
  2053 
       
  2054 		_isHidden: function() {
       
  2055 			var sel;
       
  2056 
       
  2057 			if (!isGecko) {
       
  2058 				return 0;
       
  2059 			}
       
  2060 
       
  2061 			// Weird, wheres that cursor selection?
       
  2062 			sel = this.selection.getSel();
       
  2063 			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
       
  2064 		}
       
  2065 	};
       
  2066 
       
  2067 	extend(Editor.prototype, EditorObservable);
       
  2068 
       
  2069 	return Editor;
       
  2070 });