src/pyams_skin/resources/js/ext/tinymce/dev/tinymce.js
changeset 566 a1707c607eec
parent 565 318533413200
child 567 bca1726b1d85
equal deleted inserted replaced
565:318533413200 566:a1707c607eec
     1 // 4.1.10 (2015-05-05)
       
     2 
       
     3 /**
       
     4  * Compiled inline version. (Library mode)
       
     5  */
       
     6 
       
     7 /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
       
     8 /*globals $code */
       
     9 
       
    10 (function(exports, undefined) {
       
    11 	"use strict";
       
    12 
       
    13 	var modules = {};
       
    14 
       
    15 	function require(ids, callback) {
       
    16 		var module, defs = [];
       
    17 
       
    18 		for (var i = 0; i < ids.length; ++i) {
       
    19 			module = modules[ids[i]] || resolve(ids[i]);
       
    20 			if (!module) {
       
    21 				throw 'module definition dependecy not found: ' + ids[i];
       
    22 			}
       
    23 
       
    24 			defs.push(module);
       
    25 		}
       
    26 
       
    27 		callback.apply(null, defs);
       
    28 	}
       
    29 
       
    30 	function define(id, dependencies, definition) {
       
    31 		if (typeof id !== 'string') {
       
    32 			throw 'invalid module definition, module id must be defined and be a string';
       
    33 		}
       
    34 
       
    35 		if (dependencies === undefined) {
       
    36 			throw 'invalid module definition, dependencies must be specified';
       
    37 		}
       
    38 
       
    39 		if (definition === undefined) {
       
    40 			throw 'invalid module definition, definition function must be specified';
       
    41 		}
       
    42 
       
    43 		require(dependencies, function() {
       
    44 			modules[id] = definition.apply(null, arguments);
       
    45 		});
       
    46 	}
       
    47 
       
    48 	function defined(id) {
       
    49 		return !!modules[id];
       
    50 	}
       
    51 
       
    52 	function resolve(id) {
       
    53 		var target = exports;
       
    54 		var fragments = id.split(/[.\/]/);
       
    55 
       
    56 		for (var fi = 0; fi < fragments.length; ++fi) {
       
    57 			if (!target[fragments[fi]]) {
       
    58 				return;
       
    59 			}
       
    60 
       
    61 			target = target[fragments[fi]];
       
    62 		}
       
    63 
       
    64 		return target;
       
    65 	}
       
    66 
       
    67 	function expose(ids) {
       
    68 		for (var i = 0; i < ids.length; i++) {
       
    69 			var target = exports;
       
    70 			var id = ids[i];
       
    71 			var fragments = id.split(/[.\/]/);
       
    72 
       
    73 			for (var fi = 0; fi < fragments.length - 1; ++fi) {
       
    74 				if (target[fragments[fi]] === undefined) {
       
    75 					target[fragments[fi]] = {};
       
    76 				}
       
    77 
       
    78 				target = target[fragments[fi]];
       
    79 			}
       
    80 
       
    81 			target[fragments[fragments.length - 1]] = modules[id];
       
    82 		}
       
    83 	}
       
    84 
       
    85 // Included from: js/tinymce/classes/dom/EventUtils.js
       
    86 
       
    87 /**
       
    88  * EventUtils.js
       
    89  *
       
    90  * Copyright, Moxiecode Systems AB
       
    91  * Released under LGPL License.
       
    92  *
       
    93  * License: http://www.tinymce.com/license
       
    94  * Contributing: http://www.tinymce.com/contributing
       
    95  */
       
    96 
       
    97 /*jshint loopfunc:true*/
       
    98 /*eslint no-loop-func:0 */
       
    99 
       
   100 /**
       
   101  * This class wraps the browsers native event logic with more convenient methods.
       
   102  *
       
   103  * @class tinymce.dom.EventUtils
       
   104  */
       
   105 define("tinymce/dom/EventUtils", [], function() {
       
   106 	"use strict";
       
   107 
       
   108 	var eventExpandoPrefix = "mce-data-";
       
   109 	var mouseEventRe = /^(?:mouse|contextmenu)|click/;
       
   110 	var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1};
       
   111 
       
   112 	/**
       
   113 	 * Binds a native event to a callback on the speified target.
       
   114 	 */
       
   115 	function addEvent(target, name, callback, capture) {
       
   116 		if (target.addEventListener) {
       
   117 			target.addEventListener(name, callback, capture || false);
       
   118 		} else if (target.attachEvent) {
       
   119 			target.attachEvent('on' + name, callback);
       
   120 		}
       
   121 	}
       
   122 
       
   123 	/**
       
   124 	 * Unbinds a native event callback on the specified target.
       
   125 	 */
       
   126 	function removeEvent(target, name, callback, capture) {
       
   127 		if (target.removeEventListener) {
       
   128 			target.removeEventListener(name, callback, capture || false);
       
   129 		} else if (target.detachEvent) {
       
   130 			target.detachEvent('on' + name, callback);
       
   131 		}
       
   132 	}
       
   133 
       
   134 	/**
       
   135 	 * Normalizes a native event object or just adds the event specific methods on a custom event.
       
   136 	 */
       
   137 	function fix(originalEvent, data) {
       
   138 		var name, event = data || {}, undef;
       
   139 
       
   140 		// Dummy function that gets replaced on the delegation state functions
       
   141 		function returnFalse() {
       
   142 			return false;
       
   143 		}
       
   144 
       
   145 		// Dummy function that gets replaced on the delegation state functions
       
   146 		function returnTrue() {
       
   147 			return true;
       
   148 		}
       
   149 
       
   150 		// Copy all properties from the original event
       
   151 		for (name in originalEvent) {
       
   152 			// layerX/layerY is deprecated in Chrome and produces a warning
       
   153 			if (!deprecated[name]) {
       
   154 				event[name] = originalEvent[name];
       
   155 			}
       
   156 		}
       
   157 
       
   158 		// Normalize target IE uses srcElement
       
   159 		if (!event.target) {
       
   160 			event.target = event.srcElement || document;
       
   161 		}
       
   162 
       
   163 		// Calculate pageX/Y if missing and clientX/Y available
       
   164 		if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
       
   165 			var eventDoc = event.target.ownerDocument || document;
       
   166 			var doc = eventDoc.documentElement;
       
   167 			var body = eventDoc.body;
       
   168 
       
   169 			event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
       
   170 				(doc && doc.clientLeft || body && body.clientLeft || 0);
       
   171 
       
   172 			event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) -
       
   173 				(doc && doc.clientTop || body && body.clientTop || 0);
       
   174 		}
       
   175 
       
   176 		// Add preventDefault method
       
   177 		event.preventDefault = function() {
       
   178 			event.isDefaultPrevented = returnTrue;
       
   179 
       
   180 			// Execute preventDefault on the original event object
       
   181 			if (originalEvent) {
       
   182 				if (originalEvent.preventDefault) {
       
   183 					originalEvent.preventDefault();
       
   184 				} else {
       
   185 					originalEvent.returnValue = false; // IE
       
   186 				}
       
   187 			}
       
   188 		};
       
   189 
       
   190 		// Add stopPropagation
       
   191 		event.stopPropagation = function() {
       
   192 			event.isPropagationStopped = returnTrue;
       
   193 
       
   194 			// Execute stopPropagation on the original event object
       
   195 			if (originalEvent) {
       
   196 				if (originalEvent.stopPropagation) {
       
   197 					originalEvent.stopPropagation();
       
   198 				} else {
       
   199 					originalEvent.cancelBubble = true; // IE
       
   200 				}
       
   201 			}
       
   202 		};
       
   203 
       
   204 		// Add stopImmediatePropagation
       
   205 		event.stopImmediatePropagation = function() {
       
   206 			event.isImmediatePropagationStopped = returnTrue;
       
   207 			event.stopPropagation();
       
   208 		};
       
   209 
       
   210 		// Add event delegation states
       
   211 		if (!event.isDefaultPrevented) {
       
   212 			event.isDefaultPrevented = returnFalse;
       
   213 			event.isPropagationStopped = returnFalse;
       
   214 			event.isImmediatePropagationStopped = returnFalse;
       
   215 		}
       
   216 
       
   217 		return event;
       
   218 	}
       
   219 
       
   220 	/**
       
   221 	 * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
       
   222 	 * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
       
   223 	 */
       
   224 	function bindOnReady(win, callback, eventUtils) {
       
   225 		var doc = win.document, event = {type: 'ready'};
       
   226 
       
   227 		if (eventUtils.domLoaded) {
       
   228 			callback(event);
       
   229 			return;
       
   230 		}
       
   231 
       
   232 		// Gets called when the DOM is ready
       
   233 		function readyHandler() {
       
   234 			if (!eventUtils.domLoaded) {
       
   235 				eventUtils.domLoaded = true;
       
   236 				callback(event);
       
   237 			}
       
   238 		}
       
   239 
       
   240 		function waitForDomLoaded() {
       
   241 			// Check complete or interactive state if there is a body
       
   242 			// element on some iframes IE 8 will produce a null body
       
   243 			if (doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body)) {
       
   244 				removeEvent(doc, "readystatechange", waitForDomLoaded);
       
   245 				readyHandler();
       
   246 			}
       
   247 		}
       
   248 
       
   249 		function tryScroll() {
       
   250 			try {
       
   251 				// If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
       
   252 				// http://javascript.nwbox.com/IEContentLoaded/
       
   253 				doc.documentElement.doScroll("left");
       
   254 			} catch (ex) {
       
   255 				setTimeout(tryScroll, 0);
       
   256 				return;
       
   257 			}
       
   258 
       
   259 			readyHandler();
       
   260 		}
       
   261 
       
   262 		// Use W3C method
       
   263 		if (doc.addEventListener) {
       
   264 			if (doc.readyState === "complete") {
       
   265 				readyHandler();
       
   266 			} else {
       
   267 				addEvent(win, 'DOMContentLoaded', readyHandler);
       
   268 			}
       
   269 		} else {
       
   270 			// Use IE method
       
   271 			addEvent(doc, "readystatechange", waitForDomLoaded);
       
   272 
       
   273 			// Wait until we can scroll, when we can the DOM is initialized
       
   274 			if (doc.documentElement.doScroll && win.self === win.top) {
       
   275 				tryScroll();
       
   276 			}
       
   277 		}
       
   278 
       
   279 		// Fallback if any of the above methods should fail for some odd reason
       
   280 		addEvent(win, 'load', readyHandler);
       
   281 	}
       
   282 
       
   283 	/**
       
   284 	 * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
       
   285 	 */
       
   286 	function EventUtils() {
       
   287 		var self = this, events = {}, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
       
   288 
       
   289 		expando = eventExpandoPrefix + (+new Date()).toString(32);
       
   290 		hasMouseEnterLeave = "onmouseenter" in document.documentElement;
       
   291 		hasFocusIn = "onfocusin" in document.documentElement;
       
   292 		mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
       
   293 		count = 1;
       
   294 
       
   295 		// State if the DOMContentLoaded was executed or not
       
   296 		self.domLoaded = false;
       
   297 		self.events = events;
       
   298 
       
   299 		/**
       
   300 		 * Executes all event handler callbacks for a specific event.
       
   301 		 *
       
   302 		 * @private
       
   303 		 * @param {Event} evt Event object.
       
   304 		 * @param {String} id Expando id value to look for.
       
   305 		 */
       
   306 		function executeHandlers(evt, id) {
       
   307 			var callbackList, i, l, callback, container = events[id];
       
   308 
       
   309 			callbackList = container && container[evt.type];
       
   310 			if (callbackList) {
       
   311 				for (i = 0, l = callbackList.length; i < l; i++) {
       
   312 					callback = callbackList[i];
       
   313 
       
   314 					// Check if callback exists might be removed if a unbind is called inside the callback
       
   315 					if (callback && callback.func.call(callback.scope, evt) === false) {
       
   316 						evt.preventDefault();
       
   317 					}
       
   318 
       
   319 					// Should we stop propagation to immediate listeners
       
   320 					if (evt.isImmediatePropagationStopped()) {
       
   321 						return;
       
   322 					}
       
   323 				}
       
   324 			}
       
   325 		}
       
   326 
       
   327 		/**
       
   328 		 * Binds a callback to an event on the specified target.
       
   329 		 *
       
   330 		 * @method bind
       
   331 		 * @param {Object} target Target node/window or custom object.
       
   332 		 * @param {String} names Name of the event to bind.
       
   333 		 * @param {function} callback Callback function to execute when the event occurs.
       
   334 		 * @param {Object} scope Scope to call the callback function on, defaults to target.
       
   335 		 * @return {function} Callback function that got bound.
       
   336 		 */
       
   337 		self.bind = function(target, names, callback, scope) {
       
   338 			var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
       
   339 
       
   340 			// Native event handler function patches the event and executes the callbacks for the expando
       
   341 			function defaultNativeHandler(evt) {
       
   342 				executeHandlers(fix(evt || win.event), id);
       
   343 			}
       
   344 
       
   345 			// Don't bind to text nodes or comments
       
   346 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
       
   347 				return;
       
   348 			}
       
   349 
       
   350 			// Create or get events id for the target
       
   351 			if (!target[expando]) {
       
   352 				id = count++;
       
   353 				target[expando] = id;
       
   354 				events[id] = {};
       
   355 			} else {
       
   356 				id = target[expando];
       
   357 			}
       
   358 
       
   359 			// Setup the specified scope or use the target as a default
       
   360 			scope = scope || target;
       
   361 
       
   362 			// Split names and bind each event, enables you to bind multiple events with one call
       
   363 			names = names.split(' ');
       
   364 			i = names.length;
       
   365 			while (i--) {
       
   366 				name = names[i];
       
   367 				nativeHandler = defaultNativeHandler;
       
   368 				fakeName = capture = false;
       
   369 
       
   370 				// Use ready instead of DOMContentLoaded
       
   371 				if (name === "DOMContentLoaded") {
       
   372 					name = "ready";
       
   373 				}
       
   374 
       
   375 				// DOM is already ready
       
   376 				if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
       
   377 					callback.call(scope, fix({type: name}));
       
   378 					continue;
       
   379 				}
       
   380 
       
   381 				// Handle mouseenter/mouseleaver
       
   382 				if (!hasMouseEnterLeave) {
       
   383 					fakeName = mouseEnterLeave[name];
       
   384 
       
   385 					if (fakeName) {
       
   386 						nativeHandler = function(evt) {
       
   387 							var current, related;
       
   388 
       
   389 							current = evt.currentTarget;
       
   390 							related = evt.relatedTarget;
       
   391 
       
   392 							// Check if related is inside the current target if it's not then the event should
       
   393 							// be ignored since it's a mouseover/mouseout inside the element
       
   394 							if (related && current.contains) {
       
   395 								// Use contains for performance
       
   396 								related = current.contains(related);
       
   397 							} else {
       
   398 								while (related && related !== current) {
       
   399 									related = related.parentNode;
       
   400 								}
       
   401 							}
       
   402 
       
   403 							// Fire fake event
       
   404 							if (!related) {
       
   405 								evt = fix(evt || win.event);
       
   406 								evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
       
   407 								evt.target = current;
       
   408 								executeHandlers(evt, id);
       
   409 							}
       
   410 						};
       
   411 					}
       
   412 				}
       
   413 
       
   414 				// Fake bubbeling of focusin/focusout
       
   415 				if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
       
   416 					capture = true;
       
   417 					fakeName = name === "focusin" ? "focus" : "blur";
       
   418 					nativeHandler = function(evt) {
       
   419 						evt = fix(evt || win.event);
       
   420 						evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
       
   421 						executeHandlers(evt, id);
       
   422 					};
       
   423 				}
       
   424 
       
   425 				// Setup callback list and bind native event
       
   426 				callbackList = events[id][name];
       
   427 				if (!callbackList) {
       
   428 					events[id][name] = callbackList = [{func: callback, scope: scope}];
       
   429 					callbackList.fakeName = fakeName;
       
   430 					callbackList.capture = capture;
       
   431 					//callbackList.callback = callback;
       
   432 
       
   433 					// Add the nativeHandler to the callback list so that we can later unbind it
       
   434 					callbackList.nativeHandler = nativeHandler;
       
   435 
       
   436 					// Check if the target has native events support
       
   437 
       
   438 					if (name === "ready") {
       
   439 						bindOnReady(target, nativeHandler, self);
       
   440 					} else {
       
   441 						addEvent(target, fakeName || name, nativeHandler, capture);
       
   442 					}
       
   443 				} else {
       
   444 					if (name === "ready" && self.domLoaded) {
       
   445 						callback({type: name});
       
   446 					} else {
       
   447 						// If it already has an native handler then just push the callback
       
   448 						callbackList.push({func: callback, scope: scope});
       
   449 					}
       
   450 				}
       
   451 			}
       
   452 
       
   453 			target = callbackList = 0; // Clean memory for IE
       
   454 
       
   455 			return callback;
       
   456 		};
       
   457 
       
   458 		/**
       
   459 		 * Unbinds the specified event by name, name and callback or all events on the target.
       
   460 		 *
       
   461 		 * @method unbind
       
   462 		 * @param {Object} target Target node/window or custom object.
       
   463 		 * @param {String} names Optional event name to unbind.
       
   464 		 * @param {function} callback Optional callback function to unbind.
       
   465 		 * @return {EventUtils} Event utils instance.
       
   466 		 */
       
   467 		self.unbind = function(target, names, callback) {
       
   468 			var id, callbackList, i, ci, name, eventMap;
       
   469 
       
   470 			// Don't bind to text nodes or comments
       
   471 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
       
   472 				return self;
       
   473 			}
       
   474 
       
   475 			// Unbind event or events if the target has the expando
       
   476 			id = target[expando];
       
   477 			if (id) {
       
   478 				eventMap = events[id];
       
   479 
       
   480 				// Specific callback
       
   481 				if (names) {
       
   482 					names = names.split(' ');
       
   483 					i = names.length;
       
   484 					while (i--) {
       
   485 						name = names[i];
       
   486 						callbackList = eventMap[name];
       
   487 
       
   488 						// Unbind the event if it exists in the map
       
   489 						if (callbackList) {
       
   490 							// Remove specified callback
       
   491 							if (callback) {
       
   492 								ci = callbackList.length;
       
   493 								while (ci--) {
       
   494 									if (callbackList[ci].func === callback) {
       
   495 										var nativeHandler = callbackList.nativeHandler;
       
   496 										var fakeName = callbackList.fakeName, capture = callbackList.capture;
       
   497 
       
   498 										// Clone callbackList since unbind inside a callback would otherwise break the handlers loop
       
   499 										callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
       
   500 										callbackList.nativeHandler = nativeHandler;
       
   501 										callbackList.fakeName = fakeName;
       
   502 										callbackList.capture = capture;
       
   503 
       
   504 										eventMap[name] = callbackList;
       
   505 									}
       
   506 								}
       
   507 							}
       
   508 
       
   509 							// Remove all callbacks if there isn't a specified callback or there is no callbacks left
       
   510 							if (!callback || callbackList.length === 0) {
       
   511 								delete eventMap[name];
       
   512 								removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
       
   513 							}
       
   514 						}
       
   515 					}
       
   516 				} else {
       
   517 					// All events for a specific element
       
   518 					for (name in eventMap) {
       
   519 						callbackList = eventMap[name];
       
   520 						removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
       
   521 					}
       
   522 
       
   523 					eventMap = {};
       
   524 				}
       
   525 
       
   526 				// Check if object is empty, if it isn't then we won't remove the expando map
       
   527 				for (name in eventMap) {
       
   528 					return self;
       
   529 				}
       
   530 
       
   531 				// Delete event object
       
   532 				delete events[id];
       
   533 
       
   534 				// Remove expando from target
       
   535 				try {
       
   536 					// IE will fail here since it can't delete properties from window
       
   537 					delete target[expando];
       
   538 				} catch (ex) {
       
   539 					// IE will set it to null
       
   540 					target[expando] = null;
       
   541 				}
       
   542 			}
       
   543 
       
   544 			return self;
       
   545 		};
       
   546 
       
   547 		/**
       
   548 		 * Fires the specified event on the specified target.
       
   549 		 *
       
   550 		 * @method fire
       
   551 		 * @param {Object} target Target node/window or custom object.
       
   552 		 * @param {String} name Event name to fire.
       
   553 		 * @param {Object} args Optional arguments to send to the observers.
       
   554 		 * @return {EventUtils} Event utils instance.
       
   555 		 */
       
   556 		self.fire = function(target, name, args) {
       
   557 			var id;
       
   558 
       
   559 			// Don't bind to text nodes or comments
       
   560 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
       
   561 				return self;
       
   562 			}
       
   563 
       
   564 			// Build event object by patching the args
       
   565 			args = fix(null, args);
       
   566 			args.type = name;
       
   567 			args.target = target;
       
   568 
       
   569 			do {
       
   570 				// Found an expando that means there is listeners to execute
       
   571 				id = target[expando];
       
   572 				if (id) {
       
   573 					executeHandlers(args, id);
       
   574 				}
       
   575 
       
   576 				// Walk up the DOM
       
   577 				target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
       
   578 			} while (target && !args.isPropagationStopped());
       
   579 
       
   580 			return self;
       
   581 		};
       
   582 
       
   583 		/**
       
   584 		 * Removes all bound event listeners for the specified target. This will also remove any bound
       
   585 		 * listeners to child nodes within that target.
       
   586 		 *
       
   587 		 * @method clean
       
   588 		 * @param {Object} target Target node/window object.
       
   589 		 * @return {EventUtils} Event utils instance.
       
   590 		 */
       
   591 		self.clean = function(target) {
       
   592 			var i, children, unbind = self.unbind;
       
   593 
       
   594 			// Don't bind to text nodes or comments
       
   595 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
       
   596 				return self;
       
   597 			}
       
   598 
       
   599 			// Unbind any element on the specificed target
       
   600 			if (target[expando]) {
       
   601 				unbind(target);
       
   602 			}
       
   603 
       
   604 			// Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
       
   605 			if (!target.getElementsByTagName) {
       
   606 				target = target.document;
       
   607 			}
       
   608 
       
   609 			// Remove events from each child element
       
   610 			if (target && target.getElementsByTagName) {
       
   611 				unbind(target);
       
   612 
       
   613 				children = target.getElementsByTagName('*');
       
   614 				i = children.length;
       
   615 				while (i--) {
       
   616 					target = children[i];
       
   617 
       
   618 					if (target[expando]) {
       
   619 						unbind(target);
       
   620 					}
       
   621 				}
       
   622 			}
       
   623 
       
   624 			return self;
       
   625 		};
       
   626 
       
   627 		/**
       
   628 		 * Destroys the event object. Call this on IE to remove memory leaks.
       
   629 		 */
       
   630 		self.destroy = function() {
       
   631 			events = {};
       
   632 		};
       
   633 
       
   634 		// Legacy function for canceling events
       
   635 		self.cancel = function(e) {
       
   636 			if (e) {
       
   637 				e.preventDefault();
       
   638 				e.stopImmediatePropagation();
       
   639 			}
       
   640 
       
   641 			return false;
       
   642 		};
       
   643 	}
       
   644 
       
   645 	EventUtils.Event = new EventUtils();
       
   646 	EventUtils.Event.bind(window, 'ready', function() {});
       
   647 
       
   648 	return EventUtils;
       
   649 });
       
   650 
       
   651 // Included from: js/tinymce/classes/dom/Sizzle.js
       
   652 
       
   653 /**
       
   654  * Sizzle.js
       
   655  *
       
   656  * Copyright, Moxiecode Systems AB
       
   657  * Released under LGPL License.
       
   658  *
       
   659  * License: http://www.tinymce.com/license
       
   660  * Contributing: http://www.tinymce.com/contributing
       
   661  *
       
   662  * @ignore-file
       
   663  */
       
   664 
       
   665 /*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */
       
   666 /*eslint dot-notation:0, no-empty:0, no-cond-assign:0, no-unused-expressions:0, new-cap:0 */
       
   667 /*eslint no-nested-ternary:0, func-style:0, no-bitwise:0, max-len:0, brace-style:0, no-return-assign:0, no-multi-spaces:0 */
       
   668 
       
   669 /**
       
   670  * Sizzle CSS Selector Engine v@VERSION
       
   671  * http://sizzlejs.com/
       
   672  *
       
   673  * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
       
   674  * Released under the MIT license
       
   675  * http://jquery.org/license
       
   676  *
       
   677  * Date: @DATE
       
   678  */
       
   679 define("tinymce/dom/Sizzle", [], function() {
       
   680 var i,
       
   681 	support,
       
   682 	Expr,
       
   683 	getText,
       
   684 	isXML,
       
   685 	tokenize,
       
   686 	compile,
       
   687 	select,
       
   688 	outermostContext,
       
   689 	sortInput,
       
   690 	hasDuplicate,
       
   691 
       
   692 	// Local document vars
       
   693 	setDocument,
       
   694 	document,
       
   695 	docElem,
       
   696 	documentIsHTML,
       
   697 	rbuggyQSA,
       
   698 	rbuggyMatches,
       
   699 	matches,
       
   700 	contains,
       
   701 
       
   702 	// Instance-specific data
       
   703 	expando = "sizzle" + -(new Date()),
       
   704 	preferredDoc = window.document,
       
   705 	dirruns = 0,
       
   706 	done = 0,
       
   707 	classCache = createCache(),
       
   708 	tokenCache = createCache(),
       
   709 	compilerCache = createCache(),
       
   710 	sortOrder = function( a, b ) {
       
   711 		if ( a === b ) {
       
   712 			hasDuplicate = true;
       
   713 		}
       
   714 		return 0;
       
   715 	},
       
   716 
       
   717 	// General-purpose constants
       
   718 	strundefined = typeof undefined,
       
   719 	MAX_NEGATIVE = 1 << 31,
       
   720 
       
   721 	// Instance methods
       
   722 	hasOwn = ({}).hasOwnProperty,
       
   723 	arr = [],
       
   724 	pop = arr.pop,
       
   725 	push_native = arr.push,
       
   726 	push = arr.push,
       
   727 	slice = arr.slice,
       
   728 	// Use a stripped-down indexOf if we can't use a native one
       
   729 	indexOf = arr.indexOf || function( elem ) {
       
   730 		var i = 0,
       
   731 			len = this.length;
       
   732 		for ( ; i < len; i++ ) {
       
   733 			if ( this[i] === elem ) {
       
   734 				return i;
       
   735 			}
       
   736 		}
       
   737 		return -1;
       
   738 	},
       
   739 
       
   740 	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
       
   741 
       
   742 	// Regular expressions
       
   743 
       
   744 	// http://www.w3.org/TR/css3-selectors/#whitespace
       
   745 	whitespace = "[\\x20\\t\\r\\n\\f]",
       
   746 
       
   747 	// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
       
   748 	identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
       
   749 
       
   750 	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
       
   751 	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
       
   752 		// Operator (capture 2)
       
   753 		"*([*^$|!~]?=)" + whitespace +
       
   754 		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
       
   755 		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
       
   756 		"*\\]",
       
   757 
       
   758 	pseudos = ":(" + identifier + ")(?:\\((" +
       
   759 		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
       
   760 		// 1. quoted (capture 3; capture 4 or capture 5)
       
   761 		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
       
   762 		// 2. simple (capture 6)
       
   763 		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
       
   764 		// 3. anything else (capture 2)
       
   765 		".*" +
       
   766 		")\\)|)",
       
   767 
       
   768 	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
       
   769 	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
       
   770 
       
   771 	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
       
   772 	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
       
   773 
       
   774 	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
       
   775 
       
   776 	rpseudo = new RegExp( pseudos ),
       
   777 	ridentifier = new RegExp( "^" + identifier + "$" ),
       
   778 
       
   779 	matchExpr = {
       
   780 		"ID": new RegExp( "^#(" + identifier + ")" ),
       
   781 		"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
       
   782 		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
       
   783 		"ATTR": new RegExp( "^" + attributes ),
       
   784 		"PSEUDO": new RegExp( "^" + pseudos ),
       
   785 		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
       
   786 			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
       
   787 			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
       
   788 		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
       
   789 		// For use in libraries implementing .is()
       
   790 		// We use this for POS matching in `select`
       
   791 		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
       
   792 			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
       
   793 	},
       
   794 
       
   795 	rinputs = /^(?:input|select|textarea|button)$/i,
       
   796 	rheader = /^h\d$/i,
       
   797 
       
   798 	rnative = /^[^{]+\{\s*\[native \w/,
       
   799 
       
   800 	// Easily-parseable/retrievable ID or TAG or CLASS selectors
       
   801 	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
       
   802 
       
   803 	rsibling = /[+~]/,
       
   804 	rescape = /'|\\/g,
       
   805 
       
   806 	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
       
   807 	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
       
   808 	funescape = function( _, escaped, escapedWhitespace ) {
       
   809 		var high = "0x" + escaped - 0x10000;
       
   810 		// NaN means non-codepoint
       
   811 		// Support: Firefox<24
       
   812 		// Workaround erroneous numeric interpretation of +"0x"
       
   813 		return high !== high || escapedWhitespace ?
       
   814 			escaped :
       
   815 			high < 0 ?
       
   816 				// BMP codepoint
       
   817 				String.fromCharCode( high + 0x10000 ) :
       
   818 				// Supplemental Plane codepoint (surrogate pair)
       
   819 				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
       
   820 	};
       
   821 
       
   822 // Optimize for push.apply( _, NodeList )
       
   823 try {
       
   824 	push.apply(
       
   825 		(arr = slice.call( preferredDoc.childNodes )),
       
   826 		preferredDoc.childNodes
       
   827 	);
       
   828 	// Support: Android<4.0
       
   829 	// Detect silently failing push.apply
       
   830 	arr[ preferredDoc.childNodes.length ].nodeType;
       
   831 } catch ( e ) {
       
   832 	push = { apply: arr.length ?
       
   833 
       
   834 		// Leverage slice if possible
       
   835 		function( target, els ) {
       
   836 			push_native.apply( target, slice.call(els) );
       
   837 		} :
       
   838 
       
   839 		// Support: IE<9
       
   840 		// Otherwise append directly
       
   841 		function( target, els ) {
       
   842 			var j = target.length,
       
   843 				i = 0;
       
   844 			// Can't trust NodeList.length
       
   845 			while ( (target[j++] = els[i++]) ) {}
       
   846 			target.length = j - 1;
       
   847 		}
       
   848 	};
       
   849 }
       
   850 
       
   851 function Sizzle( selector, context, results, seed ) {
       
   852 	var match, elem, m, nodeType,
       
   853 		// QSA vars
       
   854 		i, groups, old, nid, newContext, newSelector;
       
   855 
       
   856 	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
       
   857 		setDocument( context );
       
   858 	}
       
   859 
       
   860 	context = context || document;
       
   861 	results = results || [];
       
   862 
       
   863 	if ( !selector || typeof selector !== "string" ) {
       
   864 		return results;
       
   865 	}
       
   866 
       
   867 	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
       
   868 		return [];
       
   869 	}
       
   870 
       
   871 	if ( documentIsHTML && !seed ) {
       
   872 
       
   873 		// Shortcuts
       
   874 		if ( (match = rquickExpr.exec( selector )) ) {
       
   875 			// Speed-up: Sizzle("#ID")
       
   876 			if ( (m = match[1]) ) {
       
   877 				if ( nodeType === 9 ) {
       
   878 					elem = context.getElementById( m );
       
   879 					// Check parentNode to catch when Blackberry 4.6 returns
       
   880 					// nodes that are no longer in the document (jQuery #6963)
       
   881 					if ( elem && elem.parentNode ) {
       
   882 						// Handle the case where IE, Opera, and Webkit return items
       
   883 						// by name instead of ID
       
   884 						if ( elem.id === m ) {
       
   885 							results.push( elem );
       
   886 							return results;
       
   887 						}
       
   888 					} else {
       
   889 						return results;
       
   890 					}
       
   891 				} else {
       
   892 					// Context is not a document
       
   893 					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
       
   894 						contains( context, elem ) && elem.id === m ) {
       
   895 						results.push( elem );
       
   896 						return results;
       
   897 					}
       
   898 				}
       
   899 
       
   900 			// Speed-up: Sizzle("TAG")
       
   901 			} else if ( match[2] ) {
       
   902 				push.apply( results, context.getElementsByTagName( selector ) );
       
   903 				return results;
       
   904 
       
   905 			// Speed-up: Sizzle(".CLASS")
       
   906 			} else if ( (m = match[3]) && support.getElementsByClassName ) {
       
   907 				push.apply( results, context.getElementsByClassName( m ) );
       
   908 				return results;
       
   909 			}
       
   910 		}
       
   911 
       
   912 		// QSA path
       
   913 		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
       
   914 			nid = old = expando;
       
   915 			newContext = context;
       
   916 			newSelector = nodeType === 9 && selector;
       
   917 
       
   918 			// qSA works strangely on Element-rooted queries
       
   919 			// We can work around this by specifying an extra ID on the root
       
   920 			// and working up from there (Thanks to Andrew Dupont for the technique)
       
   921 			// IE 8 doesn't work on object elements
       
   922 			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
       
   923 				groups = tokenize( selector );
       
   924 
       
   925 				if ( (old = context.getAttribute("id")) ) {
       
   926 					nid = old.replace( rescape, "\\$&" );
       
   927 				} else {
       
   928 					context.setAttribute( "id", nid );
       
   929 				}
       
   930 				nid = "[id='" + nid + "'] ";
       
   931 
       
   932 				i = groups.length;
       
   933 				while ( i-- ) {
       
   934 					groups[i] = nid + toSelector( groups[i] );
       
   935 				}
       
   936 				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
       
   937 				newSelector = groups.join(",");
       
   938 			}
       
   939 
       
   940 			if ( newSelector ) {
       
   941 				try {
       
   942 					push.apply( results,
       
   943 						newContext.querySelectorAll( newSelector )
       
   944 					);
       
   945 					return results;
       
   946 				} catch(qsaError) {
       
   947 				} finally {
       
   948 					if ( !old ) {
       
   949 						context.removeAttribute("id");
       
   950 					}
       
   951 				}
       
   952 			}
       
   953 		}
       
   954 	}
       
   955 
       
   956 	// All others
       
   957 	return select( selector.replace( rtrim, "$1" ), context, results, seed );
       
   958 }
       
   959 
       
   960 /**
       
   961  * Create key-value caches of limited size
       
   962  * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
       
   963  *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
       
   964  *	deleting the oldest entry
       
   965  */
       
   966 function createCache() {
       
   967 	var keys = [];
       
   968 
       
   969 	function cache( key, value ) {
       
   970 		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
       
   971 		if ( keys.push( key + " " ) > Expr.cacheLength ) {
       
   972 			// Only keep the most recent entries
       
   973 			delete cache[ keys.shift() ];
       
   974 		}
       
   975 		return (cache[ key + " " ] = value);
       
   976 	}
       
   977 	return cache;
       
   978 }
       
   979 
       
   980 /**
       
   981  * Mark a function for special use by Sizzle
       
   982  * @param {Function} fn The function to mark
       
   983  */
       
   984 function markFunction( fn ) {
       
   985 	fn[ expando ] = true;
       
   986 	return fn;
       
   987 }
       
   988 
       
   989 /**
       
   990  * Support testing using an element
       
   991  * @param {Function} fn Passed the created div and expects a boolean result
       
   992  */
       
   993 function assert( fn ) {
       
   994 	var div = document.createElement("div");
       
   995 
       
   996 	try {
       
   997 		return !!fn( div );
       
   998 	} catch (e) {
       
   999 		return false;
       
  1000 	} finally {
       
  1001 		// Remove from its parent by default
       
  1002 		if ( div.parentNode ) {
       
  1003 			div.parentNode.removeChild( div );
       
  1004 		}
       
  1005 		// release memory in IE
       
  1006 		div = null;
       
  1007 	}
       
  1008 }
       
  1009 
       
  1010 /**
       
  1011  * Adds the same handler for all of the specified attrs
       
  1012  * @param {String} attrs Pipe-separated list of attributes
       
  1013  * @param {Function} handler The method that will be applied
       
  1014  */
       
  1015 function addHandle( attrs, handler ) {
       
  1016 	var arr = attrs.split("|"),
       
  1017 		i = attrs.length;
       
  1018 
       
  1019 	while ( i-- ) {
       
  1020 		Expr.attrHandle[ arr[i] ] = handler;
       
  1021 	}
       
  1022 }
       
  1023 
       
  1024 /**
       
  1025  * Checks document order of two siblings
       
  1026  * @param {Element} a
       
  1027  * @param {Element} b
       
  1028  * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
       
  1029  */
       
  1030 function siblingCheck( a, b ) {
       
  1031 	var cur = b && a,
       
  1032 		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
       
  1033 			( ~b.sourceIndex || MAX_NEGATIVE ) -
       
  1034 			( ~a.sourceIndex || MAX_NEGATIVE );
       
  1035 
       
  1036 	// Use IE sourceIndex if available on both nodes
       
  1037 	if ( diff ) {
       
  1038 		return diff;
       
  1039 	}
       
  1040 
       
  1041 	// Check if b follows a
       
  1042 	if ( cur ) {
       
  1043 		while ( (cur = cur.nextSibling) ) {
       
  1044 			if ( cur === b ) {
       
  1045 				return -1;
       
  1046 			}
       
  1047 		}
       
  1048 	}
       
  1049 
       
  1050 	return a ? 1 : -1;
       
  1051 }
       
  1052 
       
  1053 /**
       
  1054  * Returns a function to use in pseudos for input types
       
  1055  * @param {String} type
       
  1056  */
       
  1057 function createInputPseudo( type ) {
       
  1058 	return function( elem ) {
       
  1059 		var name = elem.nodeName.toLowerCase();
       
  1060 		return name === "input" && elem.type === type;
       
  1061 	};
       
  1062 }
       
  1063 
       
  1064 /**
       
  1065  * Returns a function to use in pseudos for buttons
       
  1066  * @param {String} type
       
  1067  */
       
  1068 function createButtonPseudo( type ) {
       
  1069 	return function( elem ) {
       
  1070 		var name = elem.nodeName.toLowerCase();
       
  1071 		return (name === "input" || name === "button") && elem.type === type;
       
  1072 	};
       
  1073 }
       
  1074 
       
  1075 /**
       
  1076  * Returns a function to use in pseudos for positionals
       
  1077  * @param {Function} fn
       
  1078  */
       
  1079 function createPositionalPseudo( fn ) {
       
  1080 	return markFunction(function( argument ) {
       
  1081 		argument = +argument;
       
  1082 		return markFunction(function( seed, matches ) {
       
  1083 			var j,
       
  1084 				matchIndexes = fn( [], seed.length, argument ),
       
  1085 				i = matchIndexes.length;
       
  1086 
       
  1087 			// Match elements found at the specified indexes
       
  1088 			while ( i-- ) {
       
  1089 				if ( seed[ (j = matchIndexes[i]) ] ) {
       
  1090 					seed[j] = !(matches[j] = seed[j]);
       
  1091 				}
       
  1092 			}
       
  1093 		});
       
  1094 	});
       
  1095 }
       
  1096 
       
  1097 /**
       
  1098  * Checks a node for validity as a Sizzle context
       
  1099  * @param {Element|Object=} context
       
  1100  * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
       
  1101  */
       
  1102 function testContext( context ) {
       
  1103 	return context && typeof context.getElementsByTagName !== strundefined && context;
       
  1104 }
       
  1105 
       
  1106 // Expose support vars for convenience
       
  1107 support = Sizzle.support = {};
       
  1108 
       
  1109 /**
       
  1110  * Detects XML nodes
       
  1111  * @param {Element|Object} elem An element or a document
       
  1112  * @returns {Boolean} True iff elem is a non-HTML XML node
       
  1113  */
       
  1114 isXML = Sizzle.isXML = function( elem ) {
       
  1115 	// documentElement is verified for cases where it doesn't yet exist
       
  1116 	// (such as loading iframes in IE - #4833)
       
  1117 	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
       
  1118 	return documentElement ? documentElement.nodeName !== "HTML" : false;
       
  1119 };
       
  1120 
       
  1121 /**
       
  1122  * Sets document-related variables once based on the current document
       
  1123  * @param {Element|Object} [doc] An element or document object to use to set the document
       
  1124  * @returns {Object} Returns the current document
       
  1125  */
       
  1126 setDocument = Sizzle.setDocument = function( node ) {
       
  1127 	var hasCompare,
       
  1128 		doc = node ? node.ownerDocument || node : preferredDoc,
       
  1129 		parent = doc.defaultView;
       
  1130 
       
  1131 	// If no document and documentElement is available, return
       
  1132 	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
       
  1133 		return document;
       
  1134 	}
       
  1135 
       
  1136 	// Set our document
       
  1137 	document = doc;
       
  1138 	docElem = doc.documentElement;
       
  1139 
       
  1140 	// Support tests
       
  1141 	documentIsHTML = !isXML( doc );
       
  1142 
       
  1143 	// Support: IE>8
       
  1144 	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
       
  1145 	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
       
  1146 	// IE6-8 do not support the defaultView property so parent will be undefined
       
  1147 	if ( parent && parent !== parent.top ) {
       
  1148 		// IE11 does not have attachEvent, so all must suffer
       
  1149 		if ( parent.addEventListener ) {
       
  1150 			parent.addEventListener( "unload", function() {
       
  1151 				setDocument();
       
  1152 			}, false );
       
  1153 		} else if ( parent.attachEvent ) {
       
  1154 			parent.attachEvent( "onunload", function() {
       
  1155 				setDocument();
       
  1156 			});
       
  1157 		}
       
  1158 	}
       
  1159 
       
  1160 	/* Attributes
       
  1161 	---------------------------------------------------------------------- */
       
  1162 
       
  1163 	// Support: IE<8
       
  1164 	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
       
  1165 	support.attributes = assert(function( div ) {
       
  1166 		div.className = "i";
       
  1167 		return !div.getAttribute("className");
       
  1168 	});
       
  1169 
       
  1170 	/* getElement(s)By*
       
  1171 	---------------------------------------------------------------------- */
       
  1172 
       
  1173 	// Check if getElementsByTagName("*") returns only elements
       
  1174 	support.getElementsByTagName = assert(function( div ) {
       
  1175 		div.appendChild( doc.createComment("") );
       
  1176 		return !div.getElementsByTagName("*").length;
       
  1177 	});
       
  1178 
       
  1179 	// Support: IE<9
       
  1180 	support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
       
  1181 
       
  1182 	// Support: IE<10
       
  1183 	// Check if getElementById returns elements by name
       
  1184 	// The broken getElementById methods don't pick up programatically-set names,
       
  1185 	// so use a roundabout getElementsByName test
       
  1186 	support.getById = assert(function( div ) {
       
  1187 		docElem.appendChild( div ).id = expando;
       
  1188 		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
       
  1189 	});
       
  1190 
       
  1191 	// ID find and filter
       
  1192 	if ( support.getById ) {
       
  1193 		Expr.find["ID"] = function( id, context ) {
       
  1194 			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
       
  1195 				var m = context.getElementById( id );
       
  1196 				// Check parentNode to catch when Blackberry 4.6 returns
       
  1197 				// nodes that are no longer in the document #6963
       
  1198 				return m && m.parentNode ? [ m ] : [];
       
  1199 			}
       
  1200 		};
       
  1201 		Expr.filter["ID"] = function( id ) {
       
  1202 			var attrId = id.replace( runescape, funescape );
       
  1203 			return function( elem ) {
       
  1204 				return elem.getAttribute("id") === attrId;
       
  1205 			};
       
  1206 		};
       
  1207 	} else {
       
  1208 		// Support: IE6/7
       
  1209 		// getElementById is not reliable as a find shortcut
       
  1210 		delete Expr.find["ID"];
       
  1211 
       
  1212 		Expr.filter["ID"] =  function( id ) {
       
  1213 			var attrId = id.replace( runescape, funescape );
       
  1214 			return function( elem ) {
       
  1215 				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
       
  1216 				return node && node.value === attrId;
       
  1217 			};
       
  1218 		};
       
  1219 	}
       
  1220 
       
  1221 	// Tag
       
  1222 	Expr.find["TAG"] = support.getElementsByTagName ?
       
  1223 		function( tag, context ) {
       
  1224 			if ( typeof context.getElementsByTagName !== strundefined ) {
       
  1225 				return context.getElementsByTagName( tag );
       
  1226 			}
       
  1227 		} :
       
  1228 		function( tag, context ) {
       
  1229 			var elem,
       
  1230 				tmp = [],
       
  1231 				i = 0,
       
  1232 				results = context.getElementsByTagName( tag );
       
  1233 
       
  1234 			// Filter out possible comments
       
  1235 			if ( tag === "*" ) {
       
  1236 				while ( (elem = results[i++]) ) {
       
  1237 					if ( elem.nodeType === 1 ) {
       
  1238 						tmp.push( elem );
       
  1239 					}
       
  1240 				}
       
  1241 
       
  1242 				return tmp;
       
  1243 			}
       
  1244 			return results;
       
  1245 		};
       
  1246 
       
  1247 	// Class
       
  1248 	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
       
  1249 		if ( documentIsHTML ) {
       
  1250 			return context.getElementsByClassName( className );
       
  1251 		}
       
  1252 	};
       
  1253 
       
  1254 	/* QSA/matchesSelector
       
  1255 	---------------------------------------------------------------------- */
       
  1256 
       
  1257 	// QSA and matchesSelector support
       
  1258 
       
  1259 	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
       
  1260 	rbuggyMatches = [];
       
  1261 
       
  1262 	// qSa(:focus) reports false when true (Chrome 21)
       
  1263 	// We allow this because of a bug in IE8/9 that throws an error
       
  1264 	// whenever `document.activeElement` is accessed on an iframe
       
  1265 	// So, we allow :focus to pass through QSA all the time to avoid the IE error
       
  1266 	// See http://bugs.jquery.com/ticket/13378
       
  1267 	rbuggyQSA = [];
       
  1268 
       
  1269 	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
       
  1270 		// Build QSA regex
       
  1271 		// Regex strategy adopted from Diego Perini
       
  1272 		assert(function( div ) {
       
  1273 			// Select is set to empty string on purpose
       
  1274 			// This is to test IE's treatment of not explicitly
       
  1275 			// setting a boolean content attribute,
       
  1276 			// since its presence should be enough
       
  1277 			// http://bugs.jquery.com/ticket/12359
       
  1278 			div.innerHTML = "<select msallowcapture=''><option selected=''></option></select>";
       
  1279 
       
  1280 			// Support: IE8, Opera 11-12.16
       
  1281 			// Nothing should be selected when empty strings follow ^= or $= or *=
       
  1282 			// The test attribute must be unknown in Opera but "safe" for WinRT
       
  1283 			// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
       
  1284 			if ( div.querySelectorAll("[msallowcapture^='']").length ) {
       
  1285 				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
       
  1286 			}
       
  1287 
       
  1288 			// Support: IE8
       
  1289 			// Boolean attributes and "value" are not treated correctly
       
  1290 			if ( !div.querySelectorAll("[selected]").length ) {
       
  1291 				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
       
  1292 			}
       
  1293 
       
  1294 			// Webkit/Opera - :checked should return selected option elements
       
  1295 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
       
  1296 			// IE8 throws error here and will not see later tests
       
  1297 			if ( !div.querySelectorAll(":checked").length ) {
       
  1298 				rbuggyQSA.push(":checked");
       
  1299 			}
       
  1300 		});
       
  1301 
       
  1302 		assert(function( div ) {
       
  1303 			// Support: Windows 8 Native Apps
       
  1304 			// The type and name attributes are restricted during .innerHTML assignment
       
  1305 			var input = doc.createElement("input");
       
  1306 			input.setAttribute( "type", "hidden" );
       
  1307 			div.appendChild( input ).setAttribute( "name", "D" );
       
  1308 
       
  1309 			// Support: IE8
       
  1310 			// Enforce case-sensitivity of name attribute
       
  1311 			if ( div.querySelectorAll("[name=d]").length ) {
       
  1312 				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
       
  1313 			}
       
  1314 
       
  1315 			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
       
  1316 			// IE8 throws error here and will not see later tests
       
  1317 			if ( !div.querySelectorAll(":enabled").length ) {
       
  1318 				rbuggyQSA.push( ":enabled", ":disabled" );
       
  1319 			}
       
  1320 
       
  1321 			// Opera 10-11 does not throw on post-comma invalid pseudos
       
  1322 			div.querySelectorAll("*,:x");
       
  1323 			rbuggyQSA.push(",.*:");
       
  1324 		});
       
  1325 	}
       
  1326 
       
  1327 	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
       
  1328 		docElem.webkitMatchesSelector ||
       
  1329 		docElem.mozMatchesSelector ||
       
  1330 		docElem.oMatchesSelector ||
       
  1331 		docElem.msMatchesSelector) )) ) {
       
  1332 
       
  1333 		assert(function( div ) {
       
  1334 			// Check to see if it's possible to do matchesSelector
       
  1335 			// on a disconnected node (IE 9)
       
  1336 			support.disconnectedMatch = matches.call( div, "div" );
       
  1337 
       
  1338 			// This should fail with an exception
       
  1339 			// Gecko does not error, returns false instead
       
  1340 			matches.call( div, "[s!='']:x" );
       
  1341 			rbuggyMatches.push( "!=", pseudos );
       
  1342 		});
       
  1343 	}
       
  1344 
       
  1345 	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
       
  1346 	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
       
  1347 
       
  1348 	/* Contains
       
  1349 	---------------------------------------------------------------------- */
       
  1350 	hasCompare = rnative.test( docElem.compareDocumentPosition );
       
  1351 
       
  1352 	// Element contains another
       
  1353 	// Purposefully does not implement inclusive descendent
       
  1354 	// As in, an element does not contain itself
       
  1355 	contains = hasCompare || rnative.test( docElem.contains ) ?
       
  1356 		function( a, b ) {
       
  1357 			var adown = a.nodeType === 9 ? a.documentElement : a,
       
  1358 				bup = b && b.parentNode;
       
  1359 			return a === bup || !!( bup && bup.nodeType === 1 && (
       
  1360 				adown.contains ?
       
  1361 					adown.contains( bup ) :
       
  1362 					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
       
  1363 			));
       
  1364 		} :
       
  1365 		function( a, b ) {
       
  1366 			if ( b ) {
       
  1367 				while ( (b = b.parentNode) ) {
       
  1368 					if ( b === a ) {
       
  1369 						return true;
       
  1370 					}
       
  1371 				}
       
  1372 			}
       
  1373 			return false;
       
  1374 		};
       
  1375 
       
  1376 	/* Sorting
       
  1377 	---------------------------------------------------------------------- */
       
  1378 
       
  1379 	// Document order sorting
       
  1380 	sortOrder = hasCompare ?
       
  1381 	function( a, b ) {
       
  1382 
       
  1383 		// Flag for duplicate removal
       
  1384 		if ( a === b ) {
       
  1385 			hasDuplicate = true;
       
  1386 			return 0;
       
  1387 		}
       
  1388 
       
  1389 		// Sort on method existence if only one input has compareDocumentPosition
       
  1390 		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
       
  1391 		if ( compare ) {
       
  1392 			return compare;
       
  1393 		}
       
  1394 
       
  1395 		// Calculate position if both inputs belong to the same document
       
  1396 		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
       
  1397 			a.compareDocumentPosition( b ) :
       
  1398 
       
  1399 			// Otherwise we know they are disconnected
       
  1400 			1;
       
  1401 
       
  1402 		// Disconnected nodes
       
  1403 		if ( compare & 1 ||
       
  1404 			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
       
  1405 
       
  1406 			// Choose the first element that is related to our preferred document
       
  1407 			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
       
  1408 				return -1;
       
  1409 			}
       
  1410 			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
       
  1411 				return 1;
       
  1412 			}
       
  1413 
       
  1414 			// Maintain original order
       
  1415 			return sortInput ?
       
  1416 				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
       
  1417 				0;
       
  1418 		}
       
  1419 
       
  1420 		return compare & 4 ? -1 : 1;
       
  1421 	} :
       
  1422 	function( a, b ) {
       
  1423 		// Exit early if the nodes are identical
       
  1424 		if ( a === b ) {
       
  1425 			hasDuplicate = true;
       
  1426 			return 0;
       
  1427 		}
       
  1428 
       
  1429 		var cur,
       
  1430 			i = 0,
       
  1431 			aup = a.parentNode,
       
  1432 			bup = b.parentNode,
       
  1433 			ap = [ a ],
       
  1434 			bp = [ b ];
       
  1435 
       
  1436 		// Parentless nodes are either documents or disconnected
       
  1437 		if ( !aup || !bup ) {
       
  1438 			return a === doc ? -1 :
       
  1439 				b === doc ? 1 :
       
  1440 				aup ? -1 :
       
  1441 				bup ? 1 :
       
  1442 				sortInput ?
       
  1443 				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
       
  1444 				0;
       
  1445 
       
  1446 		// If the nodes are siblings, we can do a quick check
       
  1447 		} else if ( aup === bup ) {
       
  1448 			return siblingCheck( a, b );
       
  1449 		}
       
  1450 
       
  1451 		// Otherwise we need full lists of their ancestors for comparison
       
  1452 		cur = a;
       
  1453 		while ( (cur = cur.parentNode) ) {
       
  1454 			ap.unshift( cur );
       
  1455 		}
       
  1456 		cur = b;
       
  1457 		while ( (cur = cur.parentNode) ) {
       
  1458 			bp.unshift( cur );
       
  1459 		}
       
  1460 
       
  1461 		// Walk down the tree looking for a discrepancy
       
  1462 		while ( ap[i] === bp[i] ) {
       
  1463 			i++;
       
  1464 		}
       
  1465 
       
  1466 		return i ?
       
  1467 			// Do a sibling check if the nodes have a common ancestor
       
  1468 			siblingCheck( ap[i], bp[i] ) :
       
  1469 
       
  1470 			// Otherwise nodes in our document sort first
       
  1471 			ap[i] === preferredDoc ? -1 :
       
  1472 			bp[i] === preferredDoc ? 1 :
       
  1473 			0;
       
  1474 	};
       
  1475 
       
  1476 	return doc;
       
  1477 };
       
  1478 
       
  1479 Sizzle.matches = function( expr, elements ) {
       
  1480 	return Sizzle( expr, null, null, elements );
       
  1481 };
       
  1482 
       
  1483 Sizzle.matchesSelector = function( elem, expr ) {
       
  1484 	// Set document vars if needed
       
  1485 	if ( ( elem.ownerDocument || elem ) !== document ) {
       
  1486 		setDocument( elem );
       
  1487 	}
       
  1488 
       
  1489 	// Make sure that attribute selectors are quoted
       
  1490 	expr = expr.replace( rattributeQuotes, "='$1']" );
       
  1491 
       
  1492 	if ( support.matchesSelector && documentIsHTML &&
       
  1493 		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
       
  1494 		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
       
  1495 
       
  1496 		try {
       
  1497 			var ret = matches.call( elem, expr );
       
  1498 
       
  1499 			// IE 9's matchesSelector returns false on disconnected nodes
       
  1500 			if ( ret || support.disconnectedMatch ||
       
  1501 					// As well, disconnected nodes are said to be in a document
       
  1502 					// fragment in IE 9
       
  1503 					elem.document && elem.document.nodeType !== 11 ) {
       
  1504 				return ret;
       
  1505 			}
       
  1506 		} catch(e) {}
       
  1507 	}
       
  1508 
       
  1509 	return Sizzle( expr, document, null, [ elem ] ).length > 0;
       
  1510 };
       
  1511 
       
  1512 Sizzle.contains = function( context, elem ) {
       
  1513 	// Set document vars if needed
       
  1514 	if ( ( context.ownerDocument || context ) !== document ) {
       
  1515 		setDocument( context );
       
  1516 	}
       
  1517 	return contains( context, elem );
       
  1518 };
       
  1519 
       
  1520 Sizzle.attr = function( elem, name ) {
       
  1521 	// Set document vars if needed
       
  1522 	if ( ( elem.ownerDocument || elem ) !== document ) {
       
  1523 		setDocument( elem );
       
  1524 	}
       
  1525 
       
  1526 	var fn = Expr.attrHandle[ name.toLowerCase() ],
       
  1527 		// Don't get fooled by Object.prototype properties (jQuery #13807)
       
  1528 		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
       
  1529 			fn( elem, name, !documentIsHTML ) :
       
  1530 			undefined;
       
  1531 
       
  1532 	return val !== undefined ?
       
  1533 		val :
       
  1534 		support.attributes || !documentIsHTML ?
       
  1535 			elem.getAttribute( name ) :
       
  1536 			(val = elem.getAttributeNode(name)) && val.specified ?
       
  1537 				val.value :
       
  1538 				null;
       
  1539 };
       
  1540 
       
  1541 Sizzle.error = function( msg ) {
       
  1542 	throw new Error( "Syntax error, unrecognized expression: " + msg );
       
  1543 };
       
  1544 
       
  1545 /**
       
  1546  * Document sorting and removing duplicates
       
  1547  * @param {ArrayLike} results
       
  1548  */
       
  1549 Sizzle.uniqueSort = function( results ) {
       
  1550 	var elem,
       
  1551 		duplicates = [],
       
  1552 		j = 0,
       
  1553 		i = 0;
       
  1554 
       
  1555 	// Unless we *know* we can detect duplicates, assume their presence
       
  1556 	hasDuplicate = !support.detectDuplicates;
       
  1557 	sortInput = !support.sortStable && results.slice( 0 );
       
  1558 	results.sort( sortOrder );
       
  1559 
       
  1560 	if ( hasDuplicate ) {
       
  1561 		while ( (elem = results[i++]) ) {
       
  1562 			if ( elem === results[ i ] ) {
       
  1563 				j = duplicates.push( i );
       
  1564 			}
       
  1565 		}
       
  1566 		while ( j-- ) {
       
  1567 			results.splice( duplicates[ j ], 1 );
       
  1568 		}
       
  1569 	}
       
  1570 
       
  1571 	// Clear input after sorting to release objects
       
  1572 	// See https://github.com/jquery/sizzle/pull/225
       
  1573 	sortInput = null;
       
  1574 
       
  1575 	return results;
       
  1576 };
       
  1577 
       
  1578 /**
       
  1579  * Utility function for retrieving the text value of an array of DOM nodes
       
  1580  * @param {Array|Element} elem
       
  1581  */
       
  1582 getText = Sizzle.getText = function( elem ) {
       
  1583 	var node,
       
  1584 		ret = "",
       
  1585 		i = 0,
       
  1586 		nodeType = elem.nodeType;
       
  1587 
       
  1588 	if ( !nodeType ) {
       
  1589 		// If no nodeType, this is expected to be an array
       
  1590 		while ( (node = elem[i++]) ) {
       
  1591 			// Do not traverse comment nodes
       
  1592 			ret += getText( node );
       
  1593 		}
       
  1594 	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
       
  1595 		// Use textContent for elements
       
  1596 		// innerText usage removed for consistency of new lines (jQuery #11153)
       
  1597 		if ( typeof elem.textContent === "string" ) {
       
  1598 			return elem.textContent;
       
  1599 		} else {
       
  1600 			// Traverse its children
       
  1601 			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
       
  1602 				ret += getText( elem );
       
  1603 			}
       
  1604 		}
       
  1605 	} else if ( nodeType === 3 || nodeType === 4 ) {
       
  1606 		return elem.nodeValue;
       
  1607 	}
       
  1608 	// Do not include comment or processing instruction nodes
       
  1609 
       
  1610 	return ret;
       
  1611 };
       
  1612 
       
  1613 Expr = Sizzle.selectors = {
       
  1614 
       
  1615 	// Can be adjusted by the user
       
  1616 	cacheLength: 50,
       
  1617 
       
  1618 	createPseudo: markFunction,
       
  1619 
       
  1620 	match: matchExpr,
       
  1621 
       
  1622 	attrHandle: {},
       
  1623 
       
  1624 	find: {},
       
  1625 
       
  1626 	relative: {
       
  1627 		">": { dir: "parentNode", first: true },
       
  1628 		" ": { dir: "parentNode" },
       
  1629 		"+": { dir: "previousSibling", first: true },
       
  1630 		"~": { dir: "previousSibling" }
       
  1631 	},
       
  1632 
       
  1633 	preFilter: {
       
  1634 		"ATTR": function( match ) {
       
  1635 			match[1] = match[1].replace( runescape, funescape );
       
  1636 
       
  1637 			// Move the given value to match[3] whether quoted or unquoted
       
  1638 			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
       
  1639 
       
  1640 			if ( match[2] === "~=" ) {
       
  1641 				match[3] = " " + match[3] + " ";
       
  1642 			}
       
  1643 
       
  1644 			return match.slice( 0, 4 );
       
  1645 		},
       
  1646 
       
  1647 		"CHILD": function( match ) {
       
  1648 			/* matches from matchExpr["CHILD"]
       
  1649 				1 type (only|nth|...)
       
  1650 				2 what (child|of-type)
       
  1651 				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
       
  1652 				4 xn-component of xn+y argument ([+-]?\d*n|)
       
  1653 				5 sign of xn-component
       
  1654 				6 x of xn-component
       
  1655 				7 sign of y-component
       
  1656 				8 y of y-component
       
  1657 			*/
       
  1658 			match[1] = match[1].toLowerCase();
       
  1659 
       
  1660 			if ( match[1].slice( 0, 3 ) === "nth" ) {
       
  1661 				// nth-* requires argument
       
  1662 				if ( !match[3] ) {
       
  1663 					Sizzle.error( match[0] );
       
  1664 				}
       
  1665 
       
  1666 				// numeric x and y parameters for Expr.filter.CHILD
       
  1667 				// remember that false/true cast respectively to 0/1
       
  1668 				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
       
  1669 				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
       
  1670 
       
  1671 			// other types prohibit arguments
       
  1672 			} else if ( match[3] ) {
       
  1673 				Sizzle.error( match[0] );
       
  1674 			}
       
  1675 
       
  1676 			return match;
       
  1677 		},
       
  1678 
       
  1679 		"PSEUDO": function( match ) {
       
  1680 			var excess,
       
  1681 				unquoted = !match[6] && match[2];
       
  1682 
       
  1683 			if ( matchExpr["CHILD"].test( match[0] ) ) {
       
  1684 				return null;
       
  1685 			}
       
  1686 
       
  1687 			// Accept quoted arguments as-is
       
  1688 			if ( match[3] ) {
       
  1689 				match[2] = match[4] || match[5] || "";
       
  1690 
       
  1691 			// Strip excess characters from unquoted arguments
       
  1692 			} else if ( unquoted && rpseudo.test( unquoted ) &&
       
  1693 				// Get excess from tokenize (recursively)
       
  1694 				(excess = tokenize( unquoted, true )) &&
       
  1695 				// advance to the next closing parenthesis
       
  1696 				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
       
  1697 
       
  1698 				// excess is a negative index
       
  1699 				match[0] = match[0].slice( 0, excess );
       
  1700 				match[2] = unquoted.slice( 0, excess );
       
  1701 			}
       
  1702 
       
  1703 			// Return only captures needed by the pseudo filter method (type and argument)
       
  1704 			return match.slice( 0, 3 );
       
  1705 		}
       
  1706 	},
       
  1707 
       
  1708 	filter: {
       
  1709 
       
  1710 		"TAG": function( nodeNameSelector ) {
       
  1711 			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
       
  1712 			return nodeNameSelector === "*" ?
       
  1713 				function() { return true; } :
       
  1714 				function( elem ) {
       
  1715 					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
       
  1716 				};
       
  1717 		},
       
  1718 
       
  1719 		"CLASS": function( className ) {
       
  1720 			var pattern = classCache[ className + " " ];
       
  1721 
       
  1722 			return pattern ||
       
  1723 				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
       
  1724 				classCache( className, function( elem ) {
       
  1725 					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
       
  1726 				});
       
  1727 		},
       
  1728 
       
  1729 		"ATTR": function( name, operator, check ) {
       
  1730 			return function( elem ) {
       
  1731 				var result = Sizzle.attr( elem, name );
       
  1732 
       
  1733 				if ( result == null ) {
       
  1734 					return operator === "!=";
       
  1735 				}
       
  1736 				if ( !operator ) {
       
  1737 					return true;
       
  1738 				}
       
  1739 
       
  1740 				result += "";
       
  1741 
       
  1742 				return operator === "=" ? result === check :
       
  1743 					operator === "!=" ? result !== check :
       
  1744 					operator === "^=" ? check && result.indexOf( check ) === 0 :
       
  1745 					operator === "*=" ? check && result.indexOf( check ) > -1 :
       
  1746 					operator === "$=" ? check && result.slice( -check.length ) === check :
       
  1747 					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
       
  1748 					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
       
  1749 					false;
       
  1750 			};
       
  1751 		},
       
  1752 
       
  1753 		"CHILD": function( type, what, argument, first, last ) {
       
  1754 			var simple = type.slice( 0, 3 ) !== "nth",
       
  1755 				forward = type.slice( -4 ) !== "last",
       
  1756 				ofType = what === "of-type";
       
  1757 
       
  1758 			return first === 1 && last === 0 ?
       
  1759 
       
  1760 				// Shortcut for :nth-*(n)
       
  1761 				function( elem ) {
       
  1762 					return !!elem.parentNode;
       
  1763 				} :
       
  1764 
       
  1765 				function( elem, context, xml ) {
       
  1766 					var cache, outerCache, node, diff, nodeIndex, start,
       
  1767 						dir = simple !== forward ? "nextSibling" : "previousSibling",
       
  1768 						parent = elem.parentNode,
       
  1769 						name = ofType && elem.nodeName.toLowerCase(),
       
  1770 						useCache = !xml && !ofType;
       
  1771 
       
  1772 					if ( parent ) {
       
  1773 
       
  1774 						// :(first|last|only)-(child|of-type)
       
  1775 						if ( simple ) {
       
  1776 							while ( dir ) {
       
  1777 								node = elem;
       
  1778 								while ( (node = node[ dir ]) ) {
       
  1779 									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
       
  1780 										return false;
       
  1781 									}
       
  1782 								}
       
  1783 								// Reverse direction for :only-* (if we haven't yet done so)
       
  1784 								start = dir = type === "only" && !start && "nextSibling";
       
  1785 							}
       
  1786 							return true;
       
  1787 						}
       
  1788 
       
  1789 						start = [ forward ? parent.firstChild : parent.lastChild ];
       
  1790 
       
  1791 						// non-xml :nth-child(...) stores cache data on `parent`
       
  1792 						if ( forward && useCache ) {
       
  1793 							// Seek `elem` from a previously-cached index
       
  1794 							outerCache = parent[ expando ] || (parent[ expando ] = {});
       
  1795 							cache = outerCache[ type ] || [];
       
  1796 							nodeIndex = cache[0] === dirruns && cache[1];
       
  1797 							diff = cache[0] === dirruns && cache[2];
       
  1798 							node = nodeIndex && parent.childNodes[ nodeIndex ];
       
  1799 
       
  1800 							while ( (node = ++nodeIndex && node && node[ dir ] ||
       
  1801 
       
  1802 								// Fallback to seeking `elem` from the start
       
  1803 								(diff = nodeIndex = 0) || start.pop()) ) {
       
  1804 
       
  1805 								// When found, cache indexes on `parent` and break
       
  1806 								if ( node.nodeType === 1 && ++diff && node === elem ) {
       
  1807 									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
       
  1808 									break;
       
  1809 								}
       
  1810 							}
       
  1811 
       
  1812 						// Use previously-cached element index if available
       
  1813 						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
       
  1814 							diff = cache[1];
       
  1815 
       
  1816 						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
       
  1817 						} else {
       
  1818 							// Use the same loop as above to seek `elem` from the start
       
  1819 							while ( (node = ++nodeIndex && node && node[ dir ] ||
       
  1820 								(diff = nodeIndex = 0) || start.pop()) ) {
       
  1821 
       
  1822 								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
       
  1823 									// Cache the index of each encountered element
       
  1824 									if ( useCache ) {
       
  1825 										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
       
  1826 									}
       
  1827 
       
  1828 									if ( node === elem ) {
       
  1829 										break;
       
  1830 									}
       
  1831 								}
       
  1832 							}
       
  1833 						}
       
  1834 
       
  1835 						// Incorporate the offset, then check against cycle size
       
  1836 						diff -= last;
       
  1837 						return diff === first || ( diff % first === 0 && diff / first >= 0 );
       
  1838 					}
       
  1839 				};
       
  1840 		},
       
  1841 
       
  1842 		"PSEUDO": function( pseudo, argument ) {
       
  1843 			// pseudo-class names are case-insensitive
       
  1844 			// http://www.w3.org/TR/selectors/#pseudo-classes
       
  1845 			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
       
  1846 			// Remember that setFilters inherits from pseudos
       
  1847 			var args,
       
  1848 				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
       
  1849 					Sizzle.error( "unsupported pseudo: " + pseudo );
       
  1850 
       
  1851 			// The user may use createPseudo to indicate that
       
  1852 			// arguments are needed to create the filter function
       
  1853 			// just as Sizzle does
       
  1854 			if ( fn[ expando ] ) {
       
  1855 				return fn( argument );
       
  1856 			}
       
  1857 
       
  1858 			// But maintain support for old signatures
       
  1859 			if ( fn.length > 1 ) {
       
  1860 				args = [ pseudo, pseudo, "", argument ];
       
  1861 				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
       
  1862 					markFunction(function( seed, matches ) {
       
  1863 						var idx,
       
  1864 							matched = fn( seed, argument ),
       
  1865 							i = matched.length;
       
  1866 						while ( i-- ) {
       
  1867 							idx = indexOf.call( seed, matched[i] );
       
  1868 							seed[ idx ] = !( matches[ idx ] = matched[i] );
       
  1869 						}
       
  1870 					}) :
       
  1871 					function( elem ) {
       
  1872 						return fn( elem, 0, args );
       
  1873 					};
       
  1874 			}
       
  1875 
       
  1876 			return fn;
       
  1877 		}
       
  1878 	},
       
  1879 
       
  1880 	pseudos: {
       
  1881 		// Potentially complex pseudos
       
  1882 		"not": markFunction(function( selector ) {
       
  1883 			// Trim the selector passed to compile
       
  1884 			// to avoid treating leading and trailing
       
  1885 			// spaces as combinators
       
  1886 			var input = [],
       
  1887 				results = [],
       
  1888 				matcher = compile( selector.replace( rtrim, "$1" ) );
       
  1889 
       
  1890 			return matcher[ expando ] ?
       
  1891 				markFunction(function( seed, matches, context, xml ) {
       
  1892 					var elem,
       
  1893 						unmatched = matcher( seed, null, xml, [] ),
       
  1894 						i = seed.length;
       
  1895 
       
  1896 					// Match elements unmatched by `matcher`
       
  1897 					while ( i-- ) {
       
  1898 						if ( (elem = unmatched[i]) ) {
       
  1899 							seed[i] = !(matches[i] = elem);
       
  1900 						}
       
  1901 					}
       
  1902 				}) :
       
  1903 				function( elem, context, xml ) {
       
  1904 					input[0] = elem;
       
  1905 					matcher( input, null, xml, results );
       
  1906 					return !results.pop();
       
  1907 				};
       
  1908 		}),
       
  1909 
       
  1910 		"has": markFunction(function( selector ) {
       
  1911 			return function( elem ) {
       
  1912 				return Sizzle( selector, elem ).length > 0;
       
  1913 			};
       
  1914 		}),
       
  1915 
       
  1916 		"contains": markFunction(function( text ) {
       
  1917 			text = text.replace( runescape, funescape );
       
  1918 			return function( elem ) {
       
  1919 				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
       
  1920 			};
       
  1921 		}),
       
  1922 
       
  1923 		// "Whether an element is represented by a :lang() selector
       
  1924 		// is based solely on the element's language value
       
  1925 		// being equal to the identifier C,
       
  1926 		// or beginning with the identifier C immediately followed by "-".
       
  1927 		// The matching of C against the element's language value is performed case-insensitively.
       
  1928 		// The identifier C does not have to be a valid language name."
       
  1929 		// http://www.w3.org/TR/selectors/#lang-pseudo
       
  1930 		"lang": markFunction( function( lang ) {
       
  1931 			// lang value must be a valid identifier
       
  1932 			if ( !ridentifier.test(lang || "") ) {
       
  1933 				Sizzle.error( "unsupported lang: " + lang );
       
  1934 			}
       
  1935 			lang = lang.replace( runescape, funescape ).toLowerCase();
       
  1936 			return function( elem ) {
       
  1937 				var elemLang;
       
  1938 				do {
       
  1939 					if ( (elemLang = documentIsHTML ?
       
  1940 						elem.lang :
       
  1941 						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
       
  1942 
       
  1943 						elemLang = elemLang.toLowerCase();
       
  1944 						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
       
  1945 					}
       
  1946 				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
       
  1947 				return false;
       
  1948 			};
       
  1949 		}),
       
  1950 
       
  1951 		// Miscellaneous
       
  1952 		"target": function( elem ) {
       
  1953 			var hash = window.location && window.location.hash;
       
  1954 			return hash && hash.slice( 1 ) === elem.id;
       
  1955 		},
       
  1956 
       
  1957 		"root": function( elem ) {
       
  1958 			return elem === docElem;
       
  1959 		},
       
  1960 
       
  1961 		"focus": function( elem ) {
       
  1962 			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
       
  1963 		},
       
  1964 
       
  1965 		// Boolean properties
       
  1966 		"enabled": function( elem ) {
       
  1967 			return elem.disabled === false;
       
  1968 		},
       
  1969 
       
  1970 		"disabled": function( elem ) {
       
  1971 			return elem.disabled === true;
       
  1972 		},
       
  1973 
       
  1974 		"checked": function( elem ) {
       
  1975 			// In CSS3, :checked should return both checked and selected elements
       
  1976 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
       
  1977 			var nodeName = elem.nodeName.toLowerCase();
       
  1978 			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
       
  1979 		},
       
  1980 
       
  1981 		"selected": function( elem ) {
       
  1982 			// Accessing this property makes selected-by-default
       
  1983 			// options in Safari work properly
       
  1984 			if ( elem.parentNode ) {
       
  1985 				elem.parentNode.selectedIndex;
       
  1986 			}
       
  1987 
       
  1988 			return elem.selected === true;
       
  1989 		},
       
  1990 
       
  1991 		// Contents
       
  1992 		"empty": function( elem ) {
       
  1993 			// http://www.w3.org/TR/selectors/#empty-pseudo
       
  1994 			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
       
  1995 			//   but not by others (comment: 8; processing instruction: 7; etc.)
       
  1996 			// nodeType < 6 works because attributes (2) do not appear as children
       
  1997 			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
       
  1998 				if ( elem.nodeType < 6 ) {
       
  1999 					return false;
       
  2000 				}
       
  2001 			}
       
  2002 			return true;
       
  2003 		},
       
  2004 
       
  2005 		"parent": function( elem ) {
       
  2006 			return !Expr.pseudos["empty"]( elem );
       
  2007 		},
       
  2008 
       
  2009 		// Element/input types
       
  2010 		"header": function( elem ) {
       
  2011 			return rheader.test( elem.nodeName );
       
  2012 		},
       
  2013 
       
  2014 		"input": function( elem ) {
       
  2015 			return rinputs.test( elem.nodeName );
       
  2016 		},
       
  2017 
       
  2018 		"button": function( elem ) {
       
  2019 			var name = elem.nodeName.toLowerCase();
       
  2020 			return name === "input" && elem.type === "button" || name === "button";
       
  2021 		},
       
  2022 
       
  2023 		"text": function( elem ) {
       
  2024 			var attr;
       
  2025 			return elem.nodeName.toLowerCase() === "input" &&
       
  2026 				elem.type === "text" &&
       
  2027 
       
  2028 				// Support: IE<8
       
  2029 				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
       
  2030 				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
       
  2031 		},
       
  2032 
       
  2033 		// Position-in-collection
       
  2034 		"first": createPositionalPseudo(function() {
       
  2035 			return [ 0 ];
       
  2036 		}),
       
  2037 
       
  2038 		"last": createPositionalPseudo(function( matchIndexes, length ) {
       
  2039 			return [ length - 1 ];
       
  2040 		}),
       
  2041 
       
  2042 		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
       
  2043 			return [ argument < 0 ? argument + length : argument ];
       
  2044 		}),
       
  2045 
       
  2046 		"even": createPositionalPseudo(function( matchIndexes, length ) {
       
  2047 			var i = 0;
       
  2048 			for ( ; i < length; i += 2 ) {
       
  2049 				matchIndexes.push( i );
       
  2050 			}
       
  2051 			return matchIndexes;
       
  2052 		}),
       
  2053 
       
  2054 		"odd": createPositionalPseudo(function( matchIndexes, length ) {
       
  2055 			var i = 1;
       
  2056 			for ( ; i < length; i += 2 ) {
       
  2057 				matchIndexes.push( i );
       
  2058 			}
       
  2059 			return matchIndexes;
       
  2060 		}),
       
  2061 
       
  2062 		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
       
  2063 			var i = argument < 0 ? argument + length : argument;
       
  2064 			for ( ; --i >= 0; ) {
       
  2065 				matchIndexes.push( i );
       
  2066 			}
       
  2067 			return matchIndexes;
       
  2068 		}),
       
  2069 
       
  2070 		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
       
  2071 			var i = argument < 0 ? argument + length : argument;
       
  2072 			for ( ; ++i < length; ) {
       
  2073 				matchIndexes.push( i );
       
  2074 			}
       
  2075 			return matchIndexes;
       
  2076 		})
       
  2077 	}
       
  2078 };
       
  2079 
       
  2080 Expr.pseudos["nth"] = Expr.pseudos["eq"];
       
  2081 
       
  2082 // Add button/input type pseudos
       
  2083 for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
       
  2084 	Expr.pseudos[ i ] = createInputPseudo( i );
       
  2085 }
       
  2086 for ( i in { submit: true, reset: true } ) {
       
  2087 	Expr.pseudos[ i ] = createButtonPseudo( i );
       
  2088 }
       
  2089 
       
  2090 // Easy API for creating new setFilters
       
  2091 function setFilters() {}
       
  2092 setFilters.prototype = Expr.filters = Expr.pseudos;
       
  2093 Expr.setFilters = new setFilters();
       
  2094 
       
  2095 tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
       
  2096 	var matched, match, tokens, type,
       
  2097 		soFar, groups, preFilters,
       
  2098 		cached = tokenCache[ selector + " " ];
       
  2099 
       
  2100 	if ( cached ) {
       
  2101 		return parseOnly ? 0 : cached.slice( 0 );
       
  2102 	}
       
  2103 
       
  2104 	soFar = selector;
       
  2105 	groups = [];
       
  2106 	preFilters = Expr.preFilter;
       
  2107 
       
  2108 	while ( soFar ) {
       
  2109 
       
  2110 		// Comma and first run
       
  2111 		if ( !matched || (match = rcomma.exec( soFar )) ) {
       
  2112 			if ( match ) {
       
  2113 				// Don't consume trailing commas as valid
       
  2114 				soFar = soFar.slice( match[0].length ) || soFar;
       
  2115 			}
       
  2116 			groups.push( (tokens = []) );
       
  2117 		}
       
  2118 
       
  2119 		matched = false;
       
  2120 
       
  2121 		// Combinators
       
  2122 		if ( (match = rcombinators.exec( soFar )) ) {
       
  2123 			matched = match.shift();
       
  2124 			tokens.push({
       
  2125 				value: matched,
       
  2126 				// Cast descendant combinators to space
       
  2127 				type: match[0].replace( rtrim, " " )
       
  2128 			});
       
  2129 			soFar = soFar.slice( matched.length );
       
  2130 		}
       
  2131 
       
  2132 		// Filters
       
  2133 		for ( type in Expr.filter ) {
       
  2134 			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
       
  2135 				(match = preFilters[ type ]( match ))) ) {
       
  2136 				matched = match.shift();
       
  2137 				tokens.push({
       
  2138 					value: matched,
       
  2139 					type: type,
       
  2140 					matches: match
       
  2141 				});
       
  2142 				soFar = soFar.slice( matched.length );
       
  2143 			}
       
  2144 		}
       
  2145 
       
  2146 		if ( !matched ) {
       
  2147 			break;
       
  2148 		}
       
  2149 	}
       
  2150 
       
  2151 	// Return the length of the invalid excess
       
  2152 	// if we're just parsing
       
  2153 	// Otherwise, throw an error or return tokens
       
  2154 	return parseOnly ?
       
  2155 		soFar.length :
       
  2156 		soFar ?
       
  2157 			Sizzle.error( selector ) :
       
  2158 			// Cache the tokens
       
  2159 			tokenCache( selector, groups ).slice( 0 );
       
  2160 };
       
  2161 
       
  2162 function toSelector( tokens ) {
       
  2163 	var i = 0,
       
  2164 		len = tokens.length,
       
  2165 		selector = "";
       
  2166 	for ( ; i < len; i++ ) {
       
  2167 		selector += tokens[i].value;
       
  2168 	}
       
  2169 	return selector;
       
  2170 }
       
  2171 
       
  2172 function addCombinator( matcher, combinator, base ) {
       
  2173 	var dir = combinator.dir,
       
  2174 		checkNonElements = base && dir === "parentNode",
       
  2175 		doneName = done++;
       
  2176 
       
  2177 	return combinator.first ?
       
  2178 		// Check against closest ancestor/preceding element
       
  2179 		function( elem, context, xml ) {
       
  2180 			while ( (elem = elem[ dir ]) ) {
       
  2181 				if ( elem.nodeType === 1 || checkNonElements ) {
       
  2182 					return matcher( elem, context, xml );
       
  2183 				}
       
  2184 			}
       
  2185 		} :
       
  2186 
       
  2187 		// Check against all ancestor/preceding elements
       
  2188 		function( elem, context, xml ) {
       
  2189 			var oldCache, outerCache,
       
  2190 				newCache = [ dirruns, doneName ];
       
  2191 
       
  2192 			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
       
  2193 			if ( xml ) {
       
  2194 				while ( (elem = elem[ dir ]) ) {
       
  2195 					if ( elem.nodeType === 1 || checkNonElements ) {
       
  2196 						if ( matcher( elem, context, xml ) ) {
       
  2197 							return true;
       
  2198 						}
       
  2199 					}
       
  2200 				}
       
  2201 			} else {
       
  2202 				while ( (elem = elem[ dir ]) ) {
       
  2203 					if ( elem.nodeType === 1 || checkNonElements ) {
       
  2204 						outerCache = elem[ expando ] || (elem[ expando ] = {});
       
  2205 						if ( (oldCache = outerCache[ dir ]) &&
       
  2206 							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
       
  2207 
       
  2208 							// Assign to newCache so results back-propagate to previous elements
       
  2209 							return (newCache[ 2 ] = oldCache[ 2 ]);
       
  2210 						} else {
       
  2211 							// Reuse newcache so results back-propagate to previous elements
       
  2212 							outerCache[ dir ] = newCache;
       
  2213 
       
  2214 							// A match means we're done; a fail means we have to keep checking
       
  2215 							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
       
  2216 								return true;
       
  2217 							}
       
  2218 						}
       
  2219 					}
       
  2220 				}
       
  2221 			}
       
  2222 		};
       
  2223 }
       
  2224 
       
  2225 function elementMatcher( matchers ) {
       
  2226 	return matchers.length > 1 ?
       
  2227 		function( elem, context, xml ) {
       
  2228 			var i = matchers.length;
       
  2229 			while ( i-- ) {
       
  2230 				if ( !matchers[i]( elem, context, xml ) ) {
       
  2231 					return false;
       
  2232 				}
       
  2233 			}
       
  2234 			return true;
       
  2235 		} :
       
  2236 		matchers[0];
       
  2237 }
       
  2238 
       
  2239 function multipleContexts( selector, contexts, results ) {
       
  2240 	var i = 0,
       
  2241 		len = contexts.length;
       
  2242 	for ( ; i < len; i++ ) {
       
  2243 		Sizzle( selector, contexts[i], results );
       
  2244 	}
       
  2245 	return results;
       
  2246 }
       
  2247 
       
  2248 function condense( unmatched, map, filter, context, xml ) {
       
  2249 	var elem,
       
  2250 		newUnmatched = [],
       
  2251 		i = 0,
       
  2252 		len = unmatched.length,
       
  2253 		mapped = map != null;
       
  2254 
       
  2255 	for ( ; i < len; i++ ) {
       
  2256 		if ( (elem = unmatched[i]) ) {
       
  2257 			if ( !filter || filter( elem, context, xml ) ) {
       
  2258 				newUnmatched.push( elem );
       
  2259 				if ( mapped ) {
       
  2260 					map.push( i );
       
  2261 				}
       
  2262 			}
       
  2263 		}
       
  2264 	}
       
  2265 
       
  2266 	return newUnmatched;
       
  2267 }
       
  2268 
       
  2269 function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
       
  2270 	if ( postFilter && !postFilter[ expando ] ) {
       
  2271 		postFilter = setMatcher( postFilter );
       
  2272 	}
       
  2273 	if ( postFinder && !postFinder[ expando ] ) {
       
  2274 		postFinder = setMatcher( postFinder, postSelector );
       
  2275 	}
       
  2276 	return markFunction(function( seed, results, context, xml ) {
       
  2277 		var temp, i, elem,
       
  2278 			preMap = [],
       
  2279 			postMap = [],
       
  2280 			preexisting = results.length,
       
  2281 
       
  2282 			// Get initial elements from seed or context
       
  2283 			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
       
  2284 
       
  2285 			// Prefilter to get matcher input, preserving a map for seed-results synchronization
       
  2286 			matcherIn = preFilter && ( seed || !selector ) ?
       
  2287 				condense( elems, preMap, preFilter, context, xml ) :
       
  2288 				elems,
       
  2289 
       
  2290 			matcherOut = matcher ?
       
  2291 				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
       
  2292 				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
       
  2293 
       
  2294 					// ...intermediate processing is necessary
       
  2295 					[] :
       
  2296 
       
  2297 					// ...otherwise use results directly
       
  2298 					results :
       
  2299 				matcherIn;
       
  2300 
       
  2301 		// Find primary matches
       
  2302 		if ( matcher ) {
       
  2303 			matcher( matcherIn, matcherOut, context, xml );
       
  2304 		}
       
  2305 
       
  2306 		// Apply postFilter
       
  2307 		if ( postFilter ) {
       
  2308 			temp = condense( matcherOut, postMap );
       
  2309 			postFilter( temp, [], context, xml );
       
  2310 
       
  2311 			// Un-match failing elements by moving them back to matcherIn
       
  2312 			i = temp.length;
       
  2313 			while ( i-- ) {
       
  2314 				if ( (elem = temp[i]) ) {
       
  2315 					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
       
  2316 				}
       
  2317 			}
       
  2318 		}
       
  2319 
       
  2320 		if ( seed ) {
       
  2321 			if ( postFinder || preFilter ) {
       
  2322 				if ( postFinder ) {
       
  2323 					// Get the final matcherOut by condensing this intermediate into postFinder contexts
       
  2324 					temp = [];
       
  2325 					i = matcherOut.length;
       
  2326 					while ( i-- ) {
       
  2327 						if ( (elem = matcherOut[i]) ) {
       
  2328 							// Restore matcherIn since elem is not yet a final match
       
  2329 							temp.push( (matcherIn[i] = elem) );
       
  2330 						}
       
  2331 					}
       
  2332 					postFinder( null, (matcherOut = []), temp, xml );
       
  2333 				}
       
  2334 
       
  2335 				// Move matched elements from seed to results to keep them synchronized
       
  2336 				i = matcherOut.length;
       
  2337 				while ( i-- ) {
       
  2338 					if ( (elem = matcherOut[i]) &&
       
  2339 						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
       
  2340 
       
  2341 						seed[temp] = !(results[temp] = elem);
       
  2342 					}
       
  2343 				}
       
  2344 			}
       
  2345 
       
  2346 		// Add elements to results, through postFinder if defined
       
  2347 		} else {
       
  2348 			matcherOut = condense(
       
  2349 				matcherOut === results ?
       
  2350 					matcherOut.splice( preexisting, matcherOut.length ) :
       
  2351 					matcherOut
       
  2352 			);
       
  2353 			if ( postFinder ) {
       
  2354 				postFinder( null, results, matcherOut, xml );
       
  2355 			} else {
       
  2356 				push.apply( results, matcherOut );
       
  2357 			}
       
  2358 		}
       
  2359 	});
       
  2360 }
       
  2361 
       
  2362 function matcherFromTokens( tokens ) {
       
  2363 	var checkContext, matcher, j,
       
  2364 		len = tokens.length,
       
  2365 		leadingRelative = Expr.relative[ tokens[0].type ],
       
  2366 		implicitRelative = leadingRelative || Expr.relative[" "],
       
  2367 		i = leadingRelative ? 1 : 0,
       
  2368 
       
  2369 		// The foundational matcher ensures that elements are reachable from top-level context(s)
       
  2370 		matchContext = addCombinator( function( elem ) {
       
  2371 			return elem === checkContext;
       
  2372 		}, implicitRelative, true ),
       
  2373 		matchAnyContext = addCombinator( function( elem ) {
       
  2374 			return indexOf.call( checkContext, elem ) > -1;
       
  2375 		}, implicitRelative, true ),
       
  2376 		matchers = [ function( elem, context, xml ) {
       
  2377 			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
       
  2378 				(checkContext = context).nodeType ?
       
  2379 					matchContext( elem, context, xml ) :
       
  2380 					matchAnyContext( elem, context, xml ) );
       
  2381 		} ];
       
  2382 
       
  2383 	for ( ; i < len; i++ ) {
       
  2384 		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
       
  2385 			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
       
  2386 		} else {
       
  2387 			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
       
  2388 
       
  2389 			// Return special upon seeing a positional matcher
       
  2390 			if ( matcher[ expando ] ) {
       
  2391 				// Find the next relative operator (if any) for proper handling
       
  2392 				j = ++i;
       
  2393 				for ( ; j < len; j++ ) {
       
  2394 					if ( Expr.relative[ tokens[j].type ] ) {
       
  2395 						break;
       
  2396 					}
       
  2397 				}
       
  2398 				return setMatcher(
       
  2399 					i > 1 && elementMatcher( matchers ),
       
  2400 					i > 1 && toSelector(
       
  2401 						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
       
  2402 						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
       
  2403 					).replace( rtrim, "$1" ),
       
  2404 					matcher,
       
  2405 					i < j && matcherFromTokens( tokens.slice( i, j ) ),
       
  2406 					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
       
  2407 					j < len && toSelector( tokens )
       
  2408 				);
       
  2409 			}
       
  2410 			matchers.push( matcher );
       
  2411 		}
       
  2412 	}
       
  2413 
       
  2414 	return elementMatcher( matchers );
       
  2415 }
       
  2416 
       
  2417 function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
       
  2418 	var bySet = setMatchers.length > 0,
       
  2419 		byElement = elementMatchers.length > 0,
       
  2420 		superMatcher = function( seed, context, xml, results, outermost ) {
       
  2421 			var elem, j, matcher,
       
  2422 				matchedCount = 0,
       
  2423 				i = "0",
       
  2424 				unmatched = seed && [],
       
  2425 				setMatched = [],
       
  2426 				contextBackup = outermostContext,
       
  2427 				// We must always have either seed elements or outermost context
       
  2428 				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
       
  2429 				// Use integer dirruns iff this is the outermost matcher
       
  2430 				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
       
  2431 				len = elems.length;
       
  2432 
       
  2433 			if ( outermost ) {
       
  2434 				outermostContext = context !== document && context;
       
  2435 			}
       
  2436 
       
  2437 			// Add elements passing elementMatchers directly to results
       
  2438 			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
       
  2439 			// Support: IE<9, Safari
       
  2440 			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
       
  2441 			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
       
  2442 				if ( byElement && elem ) {
       
  2443 					j = 0;
       
  2444 					while ( (matcher = elementMatchers[j++]) ) {
       
  2445 						if ( matcher( elem, context, xml ) ) {
       
  2446 							results.push( elem );
       
  2447 							break;
       
  2448 						}
       
  2449 					}
       
  2450 					if ( outermost ) {
       
  2451 						dirruns = dirrunsUnique;
       
  2452 					}
       
  2453 				}
       
  2454 
       
  2455 				// Track unmatched elements for set filters
       
  2456 				if ( bySet ) {
       
  2457 					// They will have gone through all possible matchers
       
  2458 					if ( (elem = !matcher && elem) ) {
       
  2459 						matchedCount--;
       
  2460 					}
       
  2461 
       
  2462 					// Lengthen the array for every element, matched or not
       
  2463 					if ( seed ) {
       
  2464 						unmatched.push( elem );
       
  2465 					}
       
  2466 				}
       
  2467 			}
       
  2468 
       
  2469 			// Apply set filters to unmatched elements
       
  2470 			matchedCount += i;
       
  2471 			if ( bySet && i !== matchedCount ) {
       
  2472 				j = 0;
       
  2473 				while ( (matcher = setMatchers[j++]) ) {
       
  2474 					matcher( unmatched, setMatched, context, xml );
       
  2475 				}
       
  2476 
       
  2477 				if ( seed ) {
       
  2478 					// Reintegrate element matches to eliminate the need for sorting
       
  2479 					if ( matchedCount > 0 ) {
       
  2480 						while ( i-- ) {
       
  2481 							if ( !(unmatched[i] || setMatched[i]) ) {
       
  2482 								setMatched[i] = pop.call( results );
       
  2483 							}
       
  2484 						}
       
  2485 					}
       
  2486 
       
  2487 					// Discard index placeholder values to get only actual matches
       
  2488 					setMatched = condense( setMatched );
       
  2489 				}
       
  2490 
       
  2491 				// Add matches to results
       
  2492 				push.apply( results, setMatched );
       
  2493 
       
  2494 				// Seedless set matches succeeding multiple successful matchers stipulate sorting
       
  2495 				if ( outermost && !seed && setMatched.length > 0 &&
       
  2496 					( matchedCount + setMatchers.length ) > 1 ) {
       
  2497 
       
  2498 					Sizzle.uniqueSort( results );
       
  2499 				}
       
  2500 			}
       
  2501 
       
  2502 			// Override manipulation of globals by nested matchers
       
  2503 			if ( outermost ) {
       
  2504 				dirruns = dirrunsUnique;
       
  2505 				outermostContext = contextBackup;
       
  2506 			}
       
  2507 
       
  2508 			return unmatched;
       
  2509 		};
       
  2510 
       
  2511 	return bySet ?
       
  2512 		markFunction( superMatcher ) :
       
  2513 		superMatcher;
       
  2514 }
       
  2515 
       
  2516 compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
       
  2517 	var i,
       
  2518 		setMatchers = [],
       
  2519 		elementMatchers = [],
       
  2520 		cached = compilerCache[ selector + " " ];
       
  2521 
       
  2522 	if ( !cached ) {
       
  2523 		// Generate a function of recursive functions that can be used to check each element
       
  2524 		if ( !match ) {
       
  2525 			match = tokenize( selector );
       
  2526 		}
       
  2527 		i = match.length;
       
  2528 		while ( i-- ) {
       
  2529 			cached = matcherFromTokens( match[i] );
       
  2530 			if ( cached[ expando ] ) {
       
  2531 				setMatchers.push( cached );
       
  2532 			} else {
       
  2533 				elementMatchers.push( cached );
       
  2534 			}
       
  2535 		}
       
  2536 
       
  2537 		// Cache the compiled function
       
  2538 		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
       
  2539 
       
  2540 		// Save selector and tokenization
       
  2541 		cached.selector = selector;
       
  2542 	}
       
  2543 	return cached;
       
  2544 };
       
  2545 
       
  2546 /**
       
  2547  * A low-level selection function that works with Sizzle's compiled
       
  2548  *  selector functions
       
  2549  * @param {String|Function} selector A selector or a pre-compiled
       
  2550  *  selector function built with Sizzle.compile
       
  2551  * @param {Element} context
       
  2552  * @param {Array} [results]
       
  2553  * @param {Array} [seed] A set of elements to match against
       
  2554  */
       
  2555 select = Sizzle.select = function( selector, context, results, seed ) {
       
  2556 	var i, tokens, token, type, find,
       
  2557 		compiled = typeof selector === "function" && selector,
       
  2558 		match = !seed && tokenize( (selector = compiled.selector || selector) );
       
  2559 
       
  2560 	results = results || [];
       
  2561 
       
  2562 	// Try to minimize operations if there is no seed and only one group
       
  2563 	if ( match.length === 1 ) {
       
  2564 
       
  2565 		// Take a shortcut and set the context if the root selector is an ID
       
  2566 		tokens = match[0] = match[0].slice( 0 );
       
  2567 		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
       
  2568 				support.getById && context.nodeType === 9 && documentIsHTML &&
       
  2569 				Expr.relative[ tokens[1].type ] ) {
       
  2570 
       
  2571 			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
       
  2572 			if ( !context ) {
       
  2573 				return results;
       
  2574 
       
  2575 			// Precompiled matchers will still verify ancestry, so step up a level
       
  2576 			} else if ( compiled ) {
       
  2577 				context = context.parentNode;
       
  2578 			}
       
  2579 
       
  2580 			selector = selector.slice( tokens.shift().value.length );
       
  2581 		}
       
  2582 
       
  2583 		// Fetch a seed set for right-to-left matching
       
  2584 		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
       
  2585 		while ( i-- ) {
       
  2586 			token = tokens[i];
       
  2587 
       
  2588 			// Abort if we hit a combinator
       
  2589 			if ( Expr.relative[ (type = token.type) ] ) {
       
  2590 				break;
       
  2591 			}
       
  2592 			if ( (find = Expr.find[ type ]) ) {
       
  2593 				// Search, expanding context for leading sibling combinators
       
  2594 				if ( (seed = find(
       
  2595 					token.matches[0].replace( runescape, funescape ),
       
  2596 					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
       
  2597 				)) ) {
       
  2598 
       
  2599 					// If seed is empty or no tokens remain, we can return early
       
  2600 					tokens.splice( i, 1 );
       
  2601 					selector = seed.length && toSelector( tokens );
       
  2602 					if ( !selector ) {
       
  2603 						push.apply( results, seed );
       
  2604 						return results;
       
  2605 					}
       
  2606 
       
  2607 					break;
       
  2608 				}
       
  2609 			}
       
  2610 		}
       
  2611 	}
       
  2612 
       
  2613 	// Compile and execute a filtering function if one is not provided
       
  2614 	// Provide `match` to avoid retokenization if we modified the selector above
       
  2615 	( compiled || compile( selector, match ) )(
       
  2616 		seed,
       
  2617 		context,
       
  2618 		!documentIsHTML,
       
  2619 		results,
       
  2620 		rsibling.test( selector ) && testContext( context.parentNode ) || context
       
  2621 	);
       
  2622 	return results;
       
  2623 };
       
  2624 
       
  2625 // One-time assignments
       
  2626 
       
  2627 // Sort stability
       
  2628 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
       
  2629 
       
  2630 // Support: Chrome 14-35+
       
  2631 // Always assume duplicates if they aren't passed to the comparison function
       
  2632 support.detectDuplicates = !!hasDuplicate;
       
  2633 
       
  2634 // Initialize against the default document
       
  2635 setDocument();
       
  2636 
       
  2637 // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
       
  2638 // Detached nodes confoundingly follow *each other*
       
  2639 support.sortDetached = assert(function( div1 ) {
       
  2640 	// Should return 1, but returns 4 (following)
       
  2641 	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
       
  2642 });
       
  2643 
       
  2644 // Support: IE<8
       
  2645 // Prevent attribute/property "interpolation"
       
  2646 // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
       
  2647 if ( !assert(function( div ) {
       
  2648 	div.innerHTML = "<a href='#'></a>";
       
  2649 	return div.firstChild.getAttribute("href") === "#" ;
       
  2650 }) ) {
       
  2651 	addHandle( "type|href|height|width", function( elem, name, isXML ) {
       
  2652 		if ( !isXML ) {
       
  2653 			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
       
  2654 		}
       
  2655 	});
       
  2656 }
       
  2657 
       
  2658 // Support: IE<9
       
  2659 // Use defaultValue in place of getAttribute("value")
       
  2660 if ( !support.attributes || !assert(function( div ) {
       
  2661 	div.innerHTML = "<input/>";
       
  2662 	div.firstChild.setAttribute( "value", "" );
       
  2663 	return div.firstChild.getAttribute( "value" ) === "";
       
  2664 }) ) {
       
  2665 	addHandle( "value", function( elem, name, isXML ) {
       
  2666 		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
       
  2667 			return elem.defaultValue;
       
  2668 		}
       
  2669 	});
       
  2670 }
       
  2671 
       
  2672 // Support: IE<9
       
  2673 // Use getAttributeNode to fetch booleans when getAttribute lies
       
  2674 if ( !assert(function( div ) {
       
  2675 	return div.getAttribute("disabled") == null;
       
  2676 }) ) {
       
  2677 	addHandle( booleans, function( elem, name, isXML ) {
       
  2678 		var val;
       
  2679 		if ( !isXML ) {
       
  2680 			return elem[ name ] === true ? name.toLowerCase() :
       
  2681 					(val = elem.getAttributeNode( name )) && val.specified ?
       
  2682 					val.value :
       
  2683 				null;
       
  2684 		}
       
  2685 	});
       
  2686 }
       
  2687 
       
  2688 // EXPOSE
       
  2689 return Sizzle;
       
  2690 });
       
  2691 
       
  2692 // Included from: js/tinymce/classes/Env.js
       
  2693 
       
  2694 /**
       
  2695  * Env.js
       
  2696  *
       
  2697  * Copyright, Moxiecode Systems AB
       
  2698  * Released under LGPL License.
       
  2699  *
       
  2700  * License: http://www.tinymce.com/license
       
  2701  * Contributing: http://www.tinymce.com/contributing
       
  2702  */
       
  2703 
       
  2704 /**
       
  2705  * This class contains various environment constants like browser versions etc.
       
  2706  * Normally you don't want to sniff specific browser versions but sometimes you have
       
  2707  * to when it's impossible to feature detect. So use this with care.
       
  2708  *
       
  2709  * @class tinymce.Env
       
  2710  * @static
       
  2711  */
       
  2712 define("tinymce/Env", [], function() {
       
  2713 	var nav = navigator, userAgent = nav.userAgent;
       
  2714 	var opera, webkit, ie, ie11, ie12, gecko, mac, iDevice, android;
       
  2715 
       
  2716 	opera = window.opera && window.opera.buildNumber;
       
  2717 	android = /Android/.test(userAgent);
       
  2718 	webkit = /WebKit/.test(userAgent);
       
  2719 	ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
       
  2720 	ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
       
  2721 	ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
       
  2722 	ie12 = (document.msElementsFromPoint && !ie && !ie11) ? 12 : false;
       
  2723 	ie = ie || ie11 || ie12;
       
  2724 	gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
       
  2725 	mac = userAgent.indexOf('Mac') != -1;
       
  2726 	iDevice = /(iPad|iPhone)/.test(userAgent);
       
  2727 
       
  2728 	if (ie12) {
       
  2729 		webkit = false;
       
  2730 	}
       
  2731 
       
  2732 	// Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
       
  2733 	// says it has contentEditable support but there is no visible caret.
       
  2734 	var contentEditable = !iDevice || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
       
  2735 
       
  2736 	return {
       
  2737 		/**
       
  2738 		 * Constant that is true if the browser is Opera.
       
  2739 		 *
       
  2740 		 * @property opera
       
  2741 		 * @type Boolean
       
  2742 		 * @final
       
  2743 		 */
       
  2744 		opera: opera,
       
  2745 
       
  2746 		/**
       
  2747 		 * Constant that is true if the browser is WebKit (Safari/Chrome).
       
  2748 		 *
       
  2749 		 * @property webKit
       
  2750 		 * @type Boolean
       
  2751 		 * @final
       
  2752 		 */
       
  2753 		webkit: webkit,
       
  2754 
       
  2755 		/**
       
  2756 		 * Constant that is more than zero if the browser is IE.
       
  2757 		 *
       
  2758 		 * @property ie
       
  2759 		 * @type Boolean
       
  2760 		 * @final
       
  2761 		 */
       
  2762 		ie: ie,
       
  2763 
       
  2764 		/**
       
  2765 		 * Constant that is true if the browser is Gecko.
       
  2766 		 *
       
  2767 		 * @property gecko
       
  2768 		 * @type Boolean
       
  2769 		 * @final
       
  2770 		 */
       
  2771 		gecko: gecko,
       
  2772 
       
  2773 		/**
       
  2774 		 * Constant that is true if the os is Mac OS.
       
  2775 		 *
       
  2776 		 * @property mac
       
  2777 		 * @type Boolean
       
  2778 		 * @final
       
  2779 		 */
       
  2780 		mac: mac,
       
  2781 
       
  2782 		/**
       
  2783 		 * Constant that is true if the os is iOS.
       
  2784 		 *
       
  2785 		 * @property iOS
       
  2786 		 * @type Boolean
       
  2787 		 * @final
       
  2788 		 */
       
  2789 		iOS: iDevice,
       
  2790 
       
  2791 		/**
       
  2792 		 * Constant that is true if the os is android.
       
  2793 		 *
       
  2794 		 * @property android
       
  2795 		 * @type Boolean
       
  2796 		 * @final
       
  2797 		 */
       
  2798 		android: android,
       
  2799 
       
  2800 		/**
       
  2801 		 * Constant that is true if the browser supports editing.
       
  2802 		 *
       
  2803 		 * @property contentEditable
       
  2804 		 * @type Boolean
       
  2805 		 * @final
       
  2806 		 */
       
  2807 		contentEditable: contentEditable,
       
  2808 
       
  2809 		/**
       
  2810 		 * Transparent image data url.
       
  2811 		 *
       
  2812 		 * @property transparentSrc
       
  2813 		 * @type Boolean
       
  2814 		 * @final
       
  2815 		 */
       
  2816 		transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
       
  2817 
       
  2818 		/**
       
  2819 		 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
       
  2820 		 *
       
  2821 		 * @property noCaretAfter
       
  2822 		 * @type Boolean
       
  2823 		 * @final
       
  2824 		 */
       
  2825 		caretAfter: ie != 8,
       
  2826 
       
  2827 		/**
       
  2828 		 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
       
  2829 		 *
       
  2830 		 * @property range
       
  2831 		 * @type Boolean
       
  2832 		 */
       
  2833 		range: window.getSelection && "Range" in window,
       
  2834 
       
  2835 		/**
       
  2836 		 * Returns the IE document mode for non IE browsers this will fake IE 10.
       
  2837 		 *
       
  2838 		 * @property documentMode
       
  2839 		 * @type Number
       
  2840 		 */
       
  2841 		documentMode: ie && !ie12 ? (document.documentMode || 7) : 10
       
  2842 	};
       
  2843 });
       
  2844 
       
  2845 // Included from: js/tinymce/classes/util/Tools.js
       
  2846 
       
  2847 /**
       
  2848  * Tools.js
       
  2849  *
       
  2850  * Copyright, Moxiecode Systems AB
       
  2851  * Released under LGPL License.
       
  2852  *
       
  2853  * License: http://www.tinymce.com/license
       
  2854  * Contributing: http://www.tinymce.com/contributing
       
  2855  */
       
  2856 
       
  2857 /**
       
  2858  * This class contains various utlity functions. These are also exposed
       
  2859  * directly on the tinymce namespace.
       
  2860  *
       
  2861  * @class tinymce.util.Tools
       
  2862  */
       
  2863 define("tinymce/util/Tools", [
       
  2864 	"tinymce/Env"
       
  2865 ], function(Env) {
       
  2866 	/**
       
  2867 	 * Removes whitespace from the beginning and end of a string.
       
  2868 	 *
       
  2869 	 * @method trim
       
  2870 	 * @param {String} s String to remove whitespace from.
       
  2871 	 * @return {String} New string with removed whitespace.
       
  2872 	 */
       
  2873 	var whiteSpaceRegExp = /^\s*|\s*$/g;
       
  2874 
       
  2875 	function trim(str) {
       
  2876 		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
       
  2877 	}
       
  2878 
       
  2879 	/**
       
  2880 	 * Returns true/false if the object is an array or not.
       
  2881 	 *
       
  2882 	 * @method isArray
       
  2883 	 * @param {Object} obj Object to check.
       
  2884 	 * @return {boolean} true/false state if the object is an array or not.
       
  2885 	 */
       
  2886 	var isArray = Array.isArray || function(obj) {
       
  2887 		return Object.prototype.toString.call(obj) === "[object Array]";
       
  2888 	};
       
  2889 
       
  2890 	/**
       
  2891 	 * Checks if a object is of a specific type for example an array.
       
  2892 	 *
       
  2893 	 * @method is
       
  2894 	 * @param {Object} obj Object to check type of.
       
  2895 	 * @param {string} type Optional type to check for.
       
  2896 	 * @return {Boolean} true/false if the object is of the specified type.
       
  2897 	 */
       
  2898 	function is(obj, type) {
       
  2899 		if (!type) {
       
  2900 			return obj !== undefined;
       
  2901 		}
       
  2902 
       
  2903 		if (type == 'array' && isArray(obj)) {
       
  2904 			return true;
       
  2905 		}
       
  2906 
       
  2907 		return typeof obj == type;
       
  2908 	}
       
  2909 
       
  2910 	/**
       
  2911 	 * Converts the specified object into a real JavaScript array.
       
  2912 	 *
       
  2913 	 * @method toArray
       
  2914 	 * @param {Object} obj Object to convert into array.
       
  2915 	 * @return {Array} Array object based in input.
       
  2916 	 */
       
  2917 	function toArray(obj) {
       
  2918 		var array = obj, i, l;
       
  2919 
       
  2920 		if (!isArray(obj)) {
       
  2921 			array = [];
       
  2922 			for (i = 0, l = obj.length; i < l; i++) {
       
  2923 				array[i] = obj[i];
       
  2924 			}
       
  2925 		}
       
  2926 
       
  2927 		return array;
       
  2928 	}
       
  2929 
       
  2930 	/**
       
  2931 	 * Makes a name/object map out of an array with names.
       
  2932 	 *
       
  2933 	 * @method makeMap
       
  2934 	 * @param {Array/String} items Items to make map out of.
       
  2935 	 * @param {String} delim Optional delimiter to split string by.
       
  2936 	 * @param {Object} map Optional map to add items to.
       
  2937 	 * @return {Object} Name/value map of items.
       
  2938 	 */
       
  2939 	function makeMap(items, delim, map) {
       
  2940 		var i;
       
  2941 
       
  2942 		items = items || [];
       
  2943 		delim = delim || ',';
       
  2944 
       
  2945 		if (typeof items == "string") {
       
  2946 			items = items.split(delim);
       
  2947 		}
       
  2948 
       
  2949 		map = map || {};
       
  2950 
       
  2951 		i = items.length;
       
  2952 		while (i--) {
       
  2953 			map[items[i]] = {};
       
  2954 		}
       
  2955 
       
  2956 		return map;
       
  2957 	}
       
  2958 
       
  2959 	/**
       
  2960 	 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
       
  2961 	 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
       
  2962 	 * The callback has the following format: cb(value, key_or_index).
       
  2963 	 *
       
  2964 	 * @method each
       
  2965 	 * @param {Object} o Collection to iterate.
       
  2966 	 * @param {function} cb Callback function to execute for each item.
       
  2967 	 * @param {Object} s Optional scope to execute the callback in.
       
  2968 	 * @example
       
  2969 	 * // Iterate an array
       
  2970 	 * tinymce.each([1,2,3], function(v, i) {
       
  2971 	 *     console.debug("Value: " + v + ", Index: " + i);
       
  2972 	 * });
       
  2973 	 *
       
  2974 	 * // Iterate an object
       
  2975 	 * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
       
  2976 	 *     console.debug("Value: " + v + ", Key: " + k);
       
  2977 	 * });
       
  2978 	 */
       
  2979 	function each(o, cb, s) {
       
  2980 		var n, l;
       
  2981 
       
  2982 		if (!o) {
       
  2983 			return 0;
       
  2984 		}
       
  2985 
       
  2986 		s = s || o;
       
  2987 
       
  2988 		if (o.length !== undefined) {
       
  2989 			// Indexed arrays, needed for Safari
       
  2990 			for (n = 0, l = o.length; n < l; n++) {
       
  2991 				if (cb.call(s, o[n], n, o) === false) {
       
  2992 					return 0;
       
  2993 				}
       
  2994 			}
       
  2995 		} else {
       
  2996 			// Hashtables
       
  2997 			for (n in o) {
       
  2998 				if (o.hasOwnProperty(n)) {
       
  2999 					if (cb.call(s, o[n], n, o) === false) {
       
  3000 						return 0;
       
  3001 					}
       
  3002 				}
       
  3003 			}
       
  3004 		}
       
  3005 
       
  3006 		return 1;
       
  3007 	}
       
  3008 
       
  3009 	/**
       
  3010 	 * Creates a new array by the return value of each iteration function call. This enables you to convert
       
  3011 	 * one array list into another.
       
  3012 	 *
       
  3013 	 * @method map
       
  3014 	 * @param {Array} array Array of items to iterate.
       
  3015 	 * @param {function} callback Function to call for each item. It's return value will be the new value.
       
  3016 	 * @return {Array} Array with new values based on function return values.
       
  3017 	 */
       
  3018 	function map(array, callback) {
       
  3019 		var out = [];
       
  3020 
       
  3021 		each(array, function(item) {
       
  3022 			out.push(callback(item));
       
  3023 		});
       
  3024 
       
  3025 		return out;
       
  3026 	}
       
  3027 
       
  3028 	/**
       
  3029 	 * Filters out items from the input array by calling the specified function for each item.
       
  3030 	 * If the function returns false the item will be excluded if it returns true it will be included.
       
  3031 	 *
       
  3032 	 * @method grep
       
  3033 	 * @param {Array} a Array of items to loop though.
       
  3034 	 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
       
  3035 	 * @return {Array} New array with values imported and filtered based in input.
       
  3036 	 * @example
       
  3037 	 * // Filter out some items, this will return an array with 4 and 5
       
  3038 	 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
       
  3039 	 */
       
  3040 	function grep(a, f) {
       
  3041 		var o = [];
       
  3042 
       
  3043 		each(a, function(v) {
       
  3044 			if (!f || f(v)) {
       
  3045 				o.push(v);
       
  3046 			}
       
  3047 		});
       
  3048 
       
  3049 		return o;
       
  3050 	}
       
  3051 
       
  3052 	/**
       
  3053 	 * Creates a class, subclass or static singleton.
       
  3054 	 * More details on this method can be found in the Wiki.
       
  3055 	 *
       
  3056 	 * @method create
       
  3057 	 * @param {String} s Class name, inheritage and prefix.
       
  3058 	 * @param {Object} p Collection of methods to add to the class.
       
  3059 	 * @param {Object} root Optional root object defaults to the global window object.
       
  3060 	 * @example
       
  3061 	 * // Creates a basic class
       
  3062 	 * tinymce.create('tinymce.somepackage.SomeClass', {
       
  3063 	 *     SomeClass: function() {
       
  3064 	 *         // Class constructor
       
  3065 	 *     },
       
  3066 	 *
       
  3067 	 *     method: function() {
       
  3068 	 *         // Some method
       
  3069 	 *     }
       
  3070 	 * });
       
  3071 	 *
       
  3072 	 * // Creates a basic subclass class
       
  3073 	 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
       
  3074 	 *     SomeSubClass: function() {
       
  3075 	 *         // Class constructor
       
  3076 	 *         this.parent(); // Call parent constructor
       
  3077 	 *     },
       
  3078 	 *
       
  3079 	 *     method: function() {
       
  3080 	 *         // Some method
       
  3081 	 *         this.parent(); // Call parent method
       
  3082 	 *     },
       
  3083 	 *
       
  3084 	 *     'static': {
       
  3085 	 *         staticMethod: function() {
       
  3086 	 *             // Static method
       
  3087 	 *         }
       
  3088 	 *     }
       
  3089 	 * });
       
  3090 	 *
       
  3091 	 * // Creates a singleton/static class
       
  3092 	 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
       
  3093 	 *     method: function() {
       
  3094 	 *         // Some method
       
  3095 	 *     }
       
  3096 	 * });
       
  3097 	 */
       
  3098 	function create(s, p, root) {
       
  3099 		var self = this, sp, ns, cn, scn, c, de = 0;
       
  3100 
       
  3101 		// Parse : <prefix> <class>:<super class>
       
  3102 		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
       
  3103 		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
       
  3104 
       
  3105 		// Create namespace for new class
       
  3106 		ns = self.createNS(s[3].replace(/\.\w+$/, ''), root);
       
  3107 
       
  3108 		// Class already exists
       
  3109 		if (ns[cn]) {
       
  3110 			return;
       
  3111 		}
       
  3112 
       
  3113 		// Make pure static class
       
  3114 		if (s[2] == 'static') {
       
  3115 			ns[cn] = p;
       
  3116 
       
  3117 			if (this.onCreate) {
       
  3118 				this.onCreate(s[2], s[3], ns[cn]);
       
  3119 			}
       
  3120 
       
  3121 			return;
       
  3122 		}
       
  3123 
       
  3124 		// Create default constructor
       
  3125 		if (!p[cn]) {
       
  3126 			p[cn] = function() {};
       
  3127 			de = 1;
       
  3128 		}
       
  3129 
       
  3130 		// Add constructor and methods
       
  3131 		ns[cn] = p[cn];
       
  3132 		self.extend(ns[cn].prototype, p);
       
  3133 
       
  3134 		// Extend
       
  3135 		if (s[5]) {
       
  3136 			sp = self.resolve(s[5]).prototype;
       
  3137 			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
       
  3138 
       
  3139 			// Extend constructor
       
  3140 			c = ns[cn];
       
  3141 			if (de) {
       
  3142 				// Add passthrough constructor
       
  3143 				ns[cn] = function() {
       
  3144 					return sp[scn].apply(this, arguments);
       
  3145 				};
       
  3146 			} else {
       
  3147 				// Add inherit constructor
       
  3148 				ns[cn] = function() {
       
  3149 					this.parent = sp[scn];
       
  3150 					return c.apply(this, arguments);
       
  3151 				};
       
  3152 			}
       
  3153 			ns[cn].prototype[cn] = ns[cn];
       
  3154 
       
  3155 			// Add super methods
       
  3156 			self.each(sp, function(f, n) {
       
  3157 				ns[cn].prototype[n] = sp[n];
       
  3158 			});
       
  3159 
       
  3160 			// Add overridden methods
       
  3161 			self.each(p, function(f, n) {
       
  3162 				// Extend methods if needed
       
  3163 				if (sp[n]) {
       
  3164 					ns[cn].prototype[n] = function() {
       
  3165 						this.parent = sp[n];
       
  3166 						return f.apply(this, arguments);
       
  3167 					};
       
  3168 				} else {
       
  3169 					if (n != cn) {
       
  3170 						ns[cn].prototype[n] = f;
       
  3171 					}
       
  3172 				}
       
  3173 			});
       
  3174 		}
       
  3175 
       
  3176 		// Add static methods
       
  3177 		/*jshint sub:true*/
       
  3178 		/*eslint dot-notation:0*/
       
  3179 		self.each(p['static'], function(f, n) {
       
  3180 			ns[cn][n] = f;
       
  3181 		});
       
  3182 	}
       
  3183 
       
  3184 	/**
       
  3185 	 * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
       
  3186 	 *
       
  3187 	 * @method inArray
       
  3188 	 * @param {Array} a Array/Object to search for value in.
       
  3189 	 * @param {Object} v Value to check for inside the array.
       
  3190 	 * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
       
  3191 	 * @example
       
  3192 	 * // Get index of value in array this will alert 1 since 2 is at that index
       
  3193 	 * alert(tinymce.inArray([1,2,3], 2));
       
  3194 	 */
       
  3195 	function inArray(a, v) {
       
  3196 		var i, l;
       
  3197 
       
  3198 		if (a) {
       
  3199 			for (i = 0, l = a.length; i < l; i++) {
       
  3200 				if (a[i] === v) {
       
  3201 					return i;
       
  3202 				}
       
  3203 			}
       
  3204 		}
       
  3205 
       
  3206 		return -1;
       
  3207 	}
       
  3208 
       
  3209 	function extend(obj, ext) {
       
  3210 		var i, l, name, args = arguments, value;
       
  3211 
       
  3212 		for (i = 1, l = args.length; i < l; i++) {
       
  3213 			ext = args[i];
       
  3214 			for (name in ext) {
       
  3215 				if (ext.hasOwnProperty(name)) {
       
  3216 					value = ext[name];
       
  3217 
       
  3218 					if (value !== undefined) {
       
  3219 						obj[name] = value;
       
  3220 					}
       
  3221 				}
       
  3222 			}
       
  3223 		}
       
  3224 
       
  3225 		return obj;
       
  3226 	}
       
  3227 
       
  3228 	/**
       
  3229 	 * Executed the specified function for each item in a object tree.
       
  3230 	 *
       
  3231 	 * @method walk
       
  3232 	 * @param {Object} o Object tree to walk though.
       
  3233 	 * @param {function} f Function to call for each item.
       
  3234 	 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
       
  3235 	 * @param {String} s Optional scope to execute the function in.
       
  3236 	 */
       
  3237 	function walk(o, f, n, s) {
       
  3238 		s = s || this;
       
  3239 
       
  3240 		if (o) {
       
  3241 			if (n) {
       
  3242 				o = o[n];
       
  3243 			}
       
  3244 
       
  3245 			each(o, function(o, i) {
       
  3246 				if (f.call(s, o, i, n) === false) {
       
  3247 					return false;
       
  3248 				}
       
  3249 
       
  3250 				walk(o, f, n, s);
       
  3251 			});
       
  3252 		}
       
  3253 	}
       
  3254 
       
  3255 	/**
       
  3256 	 * Creates a namespace on a specific object.
       
  3257 	 *
       
  3258 	 * @method createNS
       
  3259 	 * @param {String} n Namespace to create for example a.b.c.d.
       
  3260 	 * @param {Object} o Optional object to add namespace to, defaults to window.
       
  3261 	 * @return {Object} New namespace object the last item in path.
       
  3262 	 * @example
       
  3263 	 * // Create some namespace
       
  3264 	 * tinymce.createNS('tinymce.somepackage.subpackage');
       
  3265 	 *
       
  3266 	 * // Add a singleton
       
  3267 	 * var tinymce.somepackage.subpackage.SomeSingleton = {
       
  3268 	 *     method: function() {
       
  3269 	 *         // Some method
       
  3270 	 *     }
       
  3271 	 * };
       
  3272 	 */
       
  3273 	function createNS(n, o) {
       
  3274 		var i, v;
       
  3275 
       
  3276 		o = o || window;
       
  3277 
       
  3278 		n = n.split('.');
       
  3279 		for (i = 0; i < n.length; i++) {
       
  3280 			v = n[i];
       
  3281 
       
  3282 			if (!o[v]) {
       
  3283 				o[v] = {};
       
  3284 			}
       
  3285 
       
  3286 			o = o[v];
       
  3287 		}
       
  3288 
       
  3289 		return o;
       
  3290 	}
       
  3291 
       
  3292 	/**
       
  3293 	 * Resolves a string and returns the object from a specific structure.
       
  3294 	 *
       
  3295 	 * @method resolve
       
  3296 	 * @param {String} n Path to resolve for example a.b.c.d.
       
  3297 	 * @param {Object} o Optional object to search though, defaults to window.
       
  3298 	 * @return {Object} Last object in path or null if it couldn't be resolved.
       
  3299 	 * @example
       
  3300 	 * // Resolve a path into an object reference
       
  3301 	 * var obj = tinymce.resolve('a.b.c.d');
       
  3302 	 */
       
  3303 	function resolve(n, o) {
       
  3304 		var i, l;
       
  3305 
       
  3306 		o = o || window;
       
  3307 
       
  3308 		n = n.split('.');
       
  3309 		for (i = 0, l = n.length; i < l; i++) {
       
  3310 			o = o[n[i]];
       
  3311 
       
  3312 			if (!o) {
       
  3313 				break;
       
  3314 			}
       
  3315 		}
       
  3316 
       
  3317 		return o;
       
  3318 	}
       
  3319 
       
  3320 	/**
       
  3321 	 * Splits a string but removes the whitespace before and after each value.
       
  3322 	 *
       
  3323 	 * @method explode
       
  3324 	 * @param {string} s String to split.
       
  3325 	 * @param {string} d Delimiter to split by.
       
  3326 	 * @example
       
  3327 	 * // Split a string into an array with a,b,c
       
  3328 	 * var arr = tinymce.explode('a, b,   c');
       
  3329 	 */
       
  3330 	function explode(s, d) {
       
  3331 		if (!s || is(s, 'array')) {
       
  3332 			return s;
       
  3333 		}
       
  3334 
       
  3335 		return map(s.split(d || ','), trim);
       
  3336 	}
       
  3337 
       
  3338 	function _addCacheSuffix(url) {
       
  3339 		var cacheSuffix = Env.cacheSuffix;
       
  3340 
       
  3341 		if (cacheSuffix) {
       
  3342 			url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix;
       
  3343 		}
       
  3344 
       
  3345 		return url;
       
  3346 	}
       
  3347 
       
  3348 	return {
       
  3349 		trim: trim,
       
  3350 		isArray: isArray,
       
  3351 		is: is,
       
  3352 		toArray: toArray,
       
  3353 		makeMap: makeMap,
       
  3354 		each: each,
       
  3355 		map: map,
       
  3356 		grep: grep,
       
  3357 		inArray: inArray,
       
  3358 		extend: extend,
       
  3359 		create: create,
       
  3360 		walk: walk,
       
  3361 		createNS: createNS,
       
  3362 		resolve: resolve,
       
  3363 		explode: explode,
       
  3364 		_addCacheSuffix: _addCacheSuffix
       
  3365 	};
       
  3366 });
       
  3367 
       
  3368 // Included from: js/tinymce/classes/dom/DomQuery.js
       
  3369 
       
  3370 /**
       
  3371  * DomQuery.js
       
  3372  *
       
  3373  * Copyright, Moxiecode Systems AB
       
  3374  * Released under LGPL License.
       
  3375  *
       
  3376  * License: http://www.tinymce.com/license
       
  3377  * Contributing: http://www.tinymce.com/contributing
       
  3378  */
       
  3379 
       
  3380 /**
       
  3381  * This class mimics most of the jQuery API:
       
  3382  *
       
  3383  * This is whats currently implemented:
       
  3384  * - Utility functions
       
  3385  * - DOM traversial
       
  3386  * - DOM manipulation
       
  3387  * - Event binding
       
  3388  *
       
  3389  * This is not currently implemented:
       
  3390  * - Dimension
       
  3391  * - Ajax
       
  3392  * - Animation
       
  3393  * - Advanced chaining
       
  3394  *
       
  3395  * @example
       
  3396  * var $ = tinymce.dom.DomQuery;
       
  3397  * $('p').attr('attr', 'value').addClass('class');
       
  3398  *
       
  3399  * @class tinymce.dom.DomQuery
       
  3400  */
       
  3401 define("tinymce/dom/DomQuery", [
       
  3402 	"tinymce/dom/EventUtils",
       
  3403 	"tinymce/dom/Sizzle",
       
  3404 	"tinymce/util/Tools",
       
  3405 	"tinymce/Env"
       
  3406 ], function(EventUtils, Sizzle, Tools, Env) {
       
  3407 	var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
       
  3408 	var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
       
  3409 	var Event = EventUtils.Event, undef;
       
  3410 
       
  3411 	function isDefined(obj) {
       
  3412 		return typeof obj !== 'undefined';
       
  3413 	}
       
  3414 
       
  3415 	function isString(obj) {
       
  3416 		return typeof obj === 'string';
       
  3417 	}
       
  3418 
       
  3419 	function isWindow(obj) {
       
  3420 		return obj && obj == obj.window;
       
  3421 	}
       
  3422 
       
  3423 	function createFragment(html, fragDoc) {
       
  3424 		var frag, node, container;
       
  3425 
       
  3426 		fragDoc = fragDoc || doc;
       
  3427 		container = fragDoc.createElement('div');
       
  3428 		frag = fragDoc.createDocumentFragment();
       
  3429 		container.innerHTML = html;
       
  3430 
       
  3431 		while ((node = container.firstChild)) {
       
  3432 			frag.appendChild(node);
       
  3433 		}
       
  3434 
       
  3435 		return frag;
       
  3436 	}
       
  3437 
       
  3438 	function domManipulate(targetNodes, sourceItem, callback, reverse) {
       
  3439 		var i;
       
  3440 
       
  3441 		if (isString(sourceItem)) {
       
  3442 			sourceItem = createFragment(sourceItem, getElementDocument(targetNodes[0]));
       
  3443 		} else if (sourceItem.length && !sourceItem.nodeType) {
       
  3444 			sourceItem = DomQuery.makeArray(sourceItem);
       
  3445 
       
  3446 			if (reverse) {
       
  3447 				for (i = sourceItem.length - 1; i >= 0; i--) {
       
  3448 					domManipulate(targetNodes, sourceItem[i], callback, reverse);
       
  3449 				}
       
  3450 			} else {
       
  3451 				for (i = 0; i < sourceItem.length; i++) {
       
  3452 					domManipulate(targetNodes, sourceItem[i], callback, reverse);
       
  3453 				}
       
  3454 			}
       
  3455 
       
  3456 			return targetNodes;
       
  3457 		}
       
  3458 
       
  3459 		if (sourceItem.nodeType) {
       
  3460 			i = targetNodes.length;
       
  3461 			while (i--) {
       
  3462 				callback.call(targetNodes[i], sourceItem);
       
  3463 			}
       
  3464 		}
       
  3465 
       
  3466 		return targetNodes;
       
  3467 	}
       
  3468 
       
  3469 	function hasClass(node, className) {
       
  3470 		return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
       
  3471 	}
       
  3472 
       
  3473 	function wrap(elements, wrapper, all) {
       
  3474 		var lastParent, newWrapper;
       
  3475 
       
  3476 		wrapper = DomQuery(wrapper)[0];
       
  3477 
       
  3478 		elements.each(function() {
       
  3479 			var self = this;
       
  3480 
       
  3481 			if (!all || lastParent != self.parentNode) {
       
  3482 				lastParent = self.parentNode;
       
  3483 				newWrapper = wrapper.cloneNode(false);
       
  3484 				self.parentNode.insertBefore(newWrapper, self);
       
  3485 				newWrapper.appendChild(self);
       
  3486 			} else {
       
  3487 				newWrapper.appendChild(self);
       
  3488 			}
       
  3489 		});
       
  3490 
       
  3491 		return elements;
       
  3492 	}
       
  3493 
       
  3494 	var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
       
  3495 	var booleanMap = Tools.makeMap('checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected', ' ');
       
  3496 	var propFix = {
       
  3497 		'for': 'htmlFor',
       
  3498 		'class': 'className',
       
  3499 		'readonly': 'readOnly'
       
  3500 	};
       
  3501 	var cssFix = {
       
  3502 		'float': 'cssFloat'
       
  3503 	};
       
  3504 
       
  3505 	var attrHooks = {}, cssHooks = {};
       
  3506 
       
  3507 	function DomQuery(selector, context) {
       
  3508 		/*eslint new-cap:0 */
       
  3509 		return new DomQuery.fn.init(selector, context);
       
  3510 	}
       
  3511 
       
  3512 	function inArray(item, array) {
       
  3513 		var i;
       
  3514 
       
  3515 		if (array.indexOf) {
       
  3516 			return array.indexOf(item);
       
  3517 		}
       
  3518 
       
  3519 		i = array.length;
       
  3520 		while (i--) {
       
  3521 			if (array[i] === item) {
       
  3522 				return i;
       
  3523 			}
       
  3524 		}
       
  3525 
       
  3526 		return -1;
       
  3527 	}
       
  3528 
       
  3529 	var whiteSpaceRegExp = /^\s*|\s*$/g;
       
  3530 
       
  3531 	function trim(str) {
       
  3532 		return (str === null || str === undef) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
       
  3533 	}
       
  3534 
       
  3535 	function each(obj, callback) {
       
  3536 		var length, key, i, undef, value;
       
  3537 
       
  3538 		if (obj) {
       
  3539 			length = obj.length;
       
  3540 
       
  3541 			if (length === undef) {
       
  3542 				// Loop object items
       
  3543 				for (key in obj) {
       
  3544 					if (obj.hasOwnProperty(key)) {
       
  3545 						value = obj[key];
       
  3546 						if (callback.call(value, key, value) === false) {
       
  3547 							break;
       
  3548 						}
       
  3549 					}
       
  3550 				}
       
  3551 			} else {
       
  3552 				// Loop array items
       
  3553 				for (i = 0; i < length; i++) {
       
  3554 					value = obj[i];
       
  3555 					if (callback.call(value, i, value) === false) {
       
  3556 						break;
       
  3557 					}
       
  3558 				}
       
  3559 			}
       
  3560 		}
       
  3561 
       
  3562 		return obj;
       
  3563 	}
       
  3564 
       
  3565 	function grep(array, callback) {
       
  3566 		var out = [];
       
  3567 
       
  3568 		each(array, function(i, item) {
       
  3569 			if (callback(item, i)) {
       
  3570 				out.push(item);
       
  3571 			}
       
  3572 		});
       
  3573 
       
  3574 		return out;
       
  3575 	}
       
  3576 
       
  3577 	function getElementDocument(element) {
       
  3578 		if (!element) {
       
  3579 			return doc;
       
  3580 		}
       
  3581 
       
  3582 		if (element.nodeType == 9) {
       
  3583 			return element;
       
  3584 		}
       
  3585 
       
  3586 		return element.ownerDocument;
       
  3587 	}
       
  3588 
       
  3589 	DomQuery.fn = DomQuery.prototype = {
       
  3590 		constructor: DomQuery,
       
  3591 
       
  3592 		/**
       
  3593 		 * Selector for the current set.
       
  3594 		 *
       
  3595 		 * @property selector
       
  3596 		 * @type String
       
  3597 		 */
       
  3598 		selector: "",
       
  3599 
       
  3600 		/**
       
  3601 		 * Context used to create the set.
       
  3602 		 *
       
  3603 		 * @property context
       
  3604 		 * @type Element
       
  3605 		 */
       
  3606 		context: null,
       
  3607 
       
  3608 		/**
       
  3609 		 * Number of items in the current set.
       
  3610 		 *
       
  3611 		 * @property length
       
  3612 		 * @type Number
       
  3613 		 */
       
  3614 		length: 0,
       
  3615 
       
  3616 		/**
       
  3617 		 * Constructs a new DomQuery instance with the specified selector or context.
       
  3618 		 *
       
  3619 		 * @constructor
       
  3620 		 * @method init
       
  3621 		 * @param {String/Array/DomQuery} selector Optional CSS selector/Array or array like object or HTML string.
       
  3622 		 * @param {Document/Element} context Optional context to search in.
       
  3623 		 */
       
  3624 		init: function(selector, context) {
       
  3625 			var self = this, match, node;
       
  3626 
       
  3627 			if (!selector) {
       
  3628 				return self;
       
  3629 			}
       
  3630 
       
  3631 			if (selector.nodeType) {
       
  3632 				self.context = self[0] = selector;
       
  3633 				self.length = 1;
       
  3634 
       
  3635 				return self;
       
  3636 			}
       
  3637 
       
  3638 			if (context && context.nodeType) {
       
  3639 				self.context = context;
       
  3640 			} else {
       
  3641 				if (context) {
       
  3642 					return DomQuery(selector).attr(context);
       
  3643 				} else {
       
  3644 					self.context = context = document;
       
  3645 				}
       
  3646 			}
       
  3647 
       
  3648 			if (isString(selector)) {
       
  3649 				self.selector = selector;
       
  3650 
       
  3651 				if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
       
  3652 					match = [null, selector, null];
       
  3653 				} else {
       
  3654 					match = rquickExpr.exec(selector);
       
  3655 				}
       
  3656 
       
  3657 				if (match) {
       
  3658 					if (match[1]) {
       
  3659 						node = createFragment(selector, getElementDocument(context)).firstChild;
       
  3660 
       
  3661 						while (node) {
       
  3662 							push.call(self, node);
       
  3663 							node = node.nextSibling;
       
  3664 						}
       
  3665 					} else {
       
  3666 						node = getElementDocument(context).getElementById(match[2]);
       
  3667 
       
  3668 						if (!node) {
       
  3669 							return self;
       
  3670 						}
       
  3671 
       
  3672 						if (node.id !== match[2]) {
       
  3673 							return self.find(selector);
       
  3674 						}
       
  3675 
       
  3676 						self.length = 1;
       
  3677 						self[0] = node;
       
  3678 					}
       
  3679 				} else {
       
  3680 					return DomQuery(context).find(selector);
       
  3681 				}
       
  3682 			} else {
       
  3683 				this.add(selector, false);
       
  3684 			}
       
  3685 
       
  3686 			return self;
       
  3687 		},
       
  3688 
       
  3689 		/**
       
  3690 		 * Converts the current set to an array.
       
  3691 		 *
       
  3692 		 * @method toArray
       
  3693 		 * @param {Array} Array of all nodes in set.
       
  3694 		 */
       
  3695 		toArray: function() {
       
  3696 			return Tools.toArray(this);
       
  3697 		},
       
  3698 
       
  3699 		/**
       
  3700 		 * Adds new nodes to the set.
       
  3701 		 *
       
  3702 		 * @method add
       
  3703 		 * @param {Array/tinymce.dom.DomQuery} items Array of all nodes to add to set.
       
  3704 		 * @return {tinymce.dom.DomQuery} New instance with nodes added.
       
  3705 		 */
       
  3706 		add: function(items, sort) {
       
  3707 			var self = this, nodes, i;
       
  3708 
       
  3709 			if (isString(items)) {
       
  3710 				return self.add(DomQuery(items));
       
  3711 			}
       
  3712 
       
  3713 			if (sort !== false) {
       
  3714 				nodes = DomQuery.unique(self.toArray().concat(DomQuery.makeArray(items)));
       
  3715 				self.length = nodes.length;
       
  3716 				for (i = 0; i < nodes.length; i++) {
       
  3717 					self[i] = nodes[i];
       
  3718 				}
       
  3719 			} else {
       
  3720 				push.apply(self, DomQuery.makeArray(items));
       
  3721 			}
       
  3722 
       
  3723 			return self;
       
  3724 		},
       
  3725 
       
  3726 		/**
       
  3727 		 * Sets/gets attributes on the elements in the current set.
       
  3728 		 *
       
  3729 		 * @method attr
       
  3730 		 * @param {String/Object} name Name of attribute to get or an object with attributes to set.
       
  3731 		 * @param {String} value Optional value to set.
       
  3732 		 * @return {tinymce.dom.DomQuery/String} Current set or the specified attribute when only the name is specified.
       
  3733 		 */
       
  3734 		attr: function(name, value) {
       
  3735 			var self = this, hook;
       
  3736 
       
  3737 			if (typeof name === "object") {
       
  3738 				each(name, function(name, value) {
       
  3739 					self.attr(name, value);
       
  3740 				});
       
  3741 			} else if (isDefined(value)) {
       
  3742 				this.each(function() {
       
  3743 					var hook;
       
  3744 
       
  3745 					if (this.nodeType === 1) {
       
  3746 						hook = attrHooks[name];
       
  3747 						if (hook && hook.set) {
       
  3748 							hook.set(this, value);
       
  3749 							return;
       
  3750 						}
       
  3751 
       
  3752 						if (value === null) {
       
  3753 							this.removeAttribute(name, 2);
       
  3754 						} else {
       
  3755 							this.setAttribute(name, value, 2);
       
  3756 						}
       
  3757 					}
       
  3758 				});
       
  3759 			} else {
       
  3760 				if (self[0] && self[0].nodeType === 1) {
       
  3761 					hook = attrHooks[name];
       
  3762 					if (hook && hook.get) {
       
  3763 						return hook.get(self[0], name);
       
  3764 					}
       
  3765 
       
  3766 					if (booleanMap[name]) {
       
  3767 						return self.prop(name) ? name : undef;
       
  3768 					}
       
  3769 
       
  3770 					value = self[0].getAttribute(name, 2);
       
  3771 
       
  3772 					if (value === null) {
       
  3773 						value = undef;
       
  3774 					}
       
  3775 				}
       
  3776 
       
  3777 				return value;
       
  3778 			}
       
  3779 
       
  3780 			return self;
       
  3781 		},
       
  3782 
       
  3783 		/**
       
  3784 		 * Removes attributse on the elements in the current set.
       
  3785 		 *
       
  3786 		 * @method removeAttr
       
  3787 		 * @param {String/Object} name Name of attribute to remove.
       
  3788 		 * @return {tinymce.dom.DomQuery/String} Current set.
       
  3789 		 */
       
  3790 		removeAttr: function(name) {
       
  3791 			return this.attr(name, null);
       
  3792 		},
       
  3793 
       
  3794 		/**
       
  3795 		 * Sets/gets properties on the elements in the current set.
       
  3796 		 *
       
  3797 		 * @method attr
       
  3798 		 * @param {String/Object} name Name of property to get or an object with properties to set.
       
  3799 		 * @param {String} value Optional value to set.
       
  3800 		 * @return {tinymce.dom.DomQuery/String} Current set or the specified property when only the name is specified.
       
  3801 		 */
       
  3802 		prop: function(name, value) {
       
  3803 			var self = this;
       
  3804 
       
  3805 			name = propFix[name] || name;
       
  3806 
       
  3807 			if (typeof name === "object") {
       
  3808 				each(name, function(name, value) {
       
  3809 					self.prop(name, value);
       
  3810 				});
       
  3811 			} else if (isDefined(value)) {
       
  3812 				this.each(function() {
       
  3813 					if (this.nodeType == 1) {
       
  3814 						this[name] = value;
       
  3815 					}
       
  3816 				});
       
  3817 			} else {
       
  3818 				if (self[0] && self[0].nodeType && name in self[0]) {
       
  3819 					return self[0][name];
       
  3820 				}
       
  3821 
       
  3822 				return value;
       
  3823 			}
       
  3824 
       
  3825 			return self;
       
  3826 		},
       
  3827 
       
  3828 		/**
       
  3829 		 * Sets/gets styles on the elements in the current set.
       
  3830 		 *
       
  3831 		 * @method css
       
  3832 		 * @param {String/Object} name Name of style to get or an object with styles to set.
       
  3833 		 * @param {String} value Optional value to set.
       
  3834 		 * @return {tinymce.dom.DomQuery/String} Current set or the specified style when only the name is specified.
       
  3835 		 */
       
  3836 		css: function(name, value) {
       
  3837 			var self = this, elm, hook;
       
  3838 
       
  3839 			function camel(name) {
       
  3840 				return name.replace(/-(\D)/g, function(a, b) {
       
  3841 					return b.toUpperCase();
       
  3842 				});
       
  3843 			}
       
  3844 
       
  3845 			function dashed(name) {
       
  3846 				return name.replace(/[A-Z]/g, function(a) {
       
  3847 					return '-' + a;
       
  3848 				});
       
  3849 			}
       
  3850 
       
  3851 			if (typeof name === "object") {
       
  3852 				each(name, function(name, value) {
       
  3853 					self.css(name, value);
       
  3854 				});
       
  3855 			} else {
       
  3856 				if (isDefined(value)) {
       
  3857 					name = camel(name);
       
  3858 
       
  3859 					// Default px suffix on these
       
  3860 					if (typeof value === 'number' && !numericCssMap[name]) {
       
  3861 						value += 'px';
       
  3862 					}
       
  3863 
       
  3864 					self.each(function() {
       
  3865 						var style = this.style;
       
  3866 
       
  3867 						hook = cssHooks[name];
       
  3868 						if (hook && hook.set) {
       
  3869 							hook.set(this, value);
       
  3870 							return;
       
  3871 						}
       
  3872 
       
  3873 						try {
       
  3874 							this.style[cssFix[name] || name] = value;
       
  3875 						} catch (ex) {
       
  3876 							// Ignore
       
  3877 						}
       
  3878 
       
  3879 						if (value === null || value === '') {
       
  3880 							if (style.removeProperty) {
       
  3881 								style.removeProperty(dashed(name));
       
  3882 							} else {
       
  3883 								style.removeAttribute(name);
       
  3884 							}
       
  3885 						}
       
  3886 					});
       
  3887 				} else {
       
  3888 					elm = self[0];
       
  3889 
       
  3890 					hook = cssHooks[name];
       
  3891 					if (hook && hook.get) {
       
  3892 						return hook.get(elm);
       
  3893 					}
       
  3894 
       
  3895 					if (elm.ownerDocument.defaultView) {
       
  3896 						try {
       
  3897 							return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(dashed(name));
       
  3898 						} catch (ex) {
       
  3899 							return undef;
       
  3900 						}
       
  3901 					} else if (elm.currentStyle) {
       
  3902 						return elm.currentStyle[camel(name)];
       
  3903 					}
       
  3904 				}
       
  3905 			}
       
  3906 
       
  3907 			return self;
       
  3908 		},
       
  3909 
       
  3910 		/**
       
  3911 		 * Removes all nodes in set from the document.
       
  3912 		 *
       
  3913 		 * @method remove
       
  3914 		 * @return {tinymce.dom.DomQuery} Current set with the removed nodes.
       
  3915 		 */
       
  3916 		remove: function() {
       
  3917 			var self = this, node, i = this.length;
       
  3918 
       
  3919 			while (i--) {
       
  3920 				node = self[i];
       
  3921 				Event.clean(node);
       
  3922 
       
  3923 				if (node.parentNode) {
       
  3924 					node.parentNode.removeChild(node);
       
  3925 				}
       
  3926 			}
       
  3927 
       
  3928 			return this;
       
  3929 		},
       
  3930 
       
  3931 		/**
       
  3932 		 * Empties all elements in set.
       
  3933 		 *
       
  3934 		 * @method empty
       
  3935 		 * @return {tinymce.dom.DomQuery} Current set with the empty nodes.
       
  3936 		 */
       
  3937 		empty: function() {
       
  3938 			var self = this, node, i = this.length;
       
  3939 
       
  3940 			while (i--) {
       
  3941 				node = self[i];
       
  3942 				while (node.firstChild) {
       
  3943 					node.removeChild(node.firstChild);
       
  3944 				}
       
  3945 			}
       
  3946 
       
  3947 			return this;
       
  3948 		},
       
  3949 
       
  3950 		/**
       
  3951 		 * Sets or gets the HTML of the current set or first set node.
       
  3952 		 *
       
  3953 		 * @method html
       
  3954 		 * @param {String} value Optional innerHTML value to set on each element.
       
  3955 		 * @return {tinymce.dom.DomQuery/String} Current set or the innerHTML of the first element.
       
  3956 		 */
       
  3957 		html: function(value) {
       
  3958 			var self = this, i;
       
  3959 
       
  3960 			if (isDefined(value)) {
       
  3961 				i = self.length;
       
  3962 
       
  3963 				try {
       
  3964 					while (i--) {
       
  3965 						self[i].innerHTML = value;
       
  3966 					}
       
  3967 				} catch (ex) {
       
  3968 					// Workaround for "Unknown runtime error" when DIV is added to P on IE
       
  3969 					DomQuery(self[i]).empty().append(value);
       
  3970 				}
       
  3971 
       
  3972 				return self;
       
  3973 			}
       
  3974 
       
  3975 			return self[0] ? self[0].innerHTML : '';
       
  3976 		},
       
  3977 
       
  3978 		/**
       
  3979 		 * Sets or gets the text of the current set or first set node.
       
  3980 		 *
       
  3981 		 * @method text
       
  3982 		 * @param {String} value Optional innerText value to set on each element.
       
  3983 		 * @return {tinymce.dom.DomQuery/String} Current set or the innerText of the first element.
       
  3984 		 */
       
  3985 		text: function(value) {
       
  3986 			var self = this, i;
       
  3987 
       
  3988 			if (isDefined(value)) {
       
  3989 				i = self.length;
       
  3990 				while (i--) {
       
  3991 					if ("innerText" in self[i]) {
       
  3992 						self[i].innerText = value;
       
  3993 					} else {
       
  3994 						self[0].textContent = value;
       
  3995 					}
       
  3996 				}
       
  3997 
       
  3998 				return self;
       
  3999 			}
       
  4000 
       
  4001 			return self[0] ? (self[0].innerText || self[0].textContent) : '';
       
  4002 		},
       
  4003 
       
  4004 		/**
       
  4005 		 * Appends the specified node/html or node set to the current set nodes.
       
  4006 		 *
       
  4007 		 * @method append
       
  4008 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to append to each element in set.
       
  4009 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4010 		 */
       
  4011 		append: function() {
       
  4012 			return domManipulate(this, arguments, function(node) {
       
  4013 				if (this.nodeType === 1) {
       
  4014 					this.appendChild(node);
       
  4015 				}
       
  4016 			});
       
  4017 		},
       
  4018 
       
  4019 		/**
       
  4020 		 * Prepends the specified node/html or node set to the current set nodes.
       
  4021 		 *
       
  4022 		 * @method prepend
       
  4023 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to prepend to each element in set.
       
  4024 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4025 		 */
       
  4026 		prepend: function() {
       
  4027 			return domManipulate(this, arguments, function(node) {
       
  4028 				if (this.nodeType === 1) {
       
  4029 					this.insertBefore(node, this.firstChild);
       
  4030 				}
       
  4031 			}, true);
       
  4032 		},
       
  4033 
       
  4034 		/**
       
  4035 		 * Adds the specified elements before current set nodes.
       
  4036 		 *
       
  4037 		 * @method before
       
  4038 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add before to each element in set.
       
  4039 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4040 		 */
       
  4041 		before: function() {
       
  4042 			var self = this;
       
  4043 
       
  4044 			if (self[0] && self[0].parentNode) {
       
  4045 				return domManipulate(self, arguments, function(node) {
       
  4046 					this.parentNode.insertBefore(node, this);
       
  4047 				});
       
  4048 			}
       
  4049 
       
  4050 			return self;
       
  4051 		},
       
  4052 
       
  4053 		/**
       
  4054 		 * Adds the specified elements after current set nodes.
       
  4055 		 *
       
  4056 		 * @method after
       
  4057 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add after to each element in set.
       
  4058 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4059 		 */
       
  4060 		after: function() {
       
  4061 			var self = this;
       
  4062 
       
  4063 			if (self[0] && self[0].parentNode) {
       
  4064 				return domManipulate(self, arguments, function(node) {
       
  4065 					this.parentNode.insertBefore(node, this.nextSibling);
       
  4066 				}, true);
       
  4067 			}
       
  4068 
       
  4069 			return self;
       
  4070 		},
       
  4071 
       
  4072 		/**
       
  4073 		 * Appends the specified set nodes to the specified selector/instance.
       
  4074 		 *
       
  4075 		 * @method appendTo
       
  4076 		 * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to append the current set to.
       
  4077 		 * @return {tinymce.dom.DomQuery} Current set with the appended nodes.
       
  4078 		 */
       
  4079 		appendTo: function(val) {
       
  4080 			DomQuery(val).append(this);
       
  4081 
       
  4082 			return this;
       
  4083 		},
       
  4084 
       
  4085 		/**
       
  4086 		 * Prepends the specified set nodes to the specified selector/instance.
       
  4087 		 *
       
  4088 		 * @method prependTo
       
  4089 		 * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to prepend the current set to.
       
  4090 		 * @return {tinymce.dom.DomQuery} Current set with the prepended nodes.
       
  4091 		 */
       
  4092 		prependTo: function(val) {
       
  4093 			DomQuery(val).prepend(this);
       
  4094 
       
  4095 			return this;
       
  4096 		},
       
  4097 
       
  4098 		/**
       
  4099 		 * Replaces the nodes in set with the specified content.
       
  4100 		 *
       
  4101 		 * @method replaceWith
       
  4102 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to replace nodes with.
       
  4103 		 * @return {tinymce.dom.DomQuery} Set with replaced nodes.
       
  4104 		 */
       
  4105 		replaceWith: function(content) {
       
  4106 			return this.before(content).remove();
       
  4107 		},
       
  4108 
       
  4109 		/**
       
  4110 		 * Wraps all elements in set with the specified wrapper.
       
  4111 		 *
       
  4112 		 * @method wrap
       
  4113 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
       
  4114 		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
       
  4115 		 */
       
  4116 		wrap: function(wrapper) {
       
  4117 			return wrap(this, wrapper);
       
  4118 		},
       
  4119 
       
  4120 		/**
       
  4121 		 * Wraps all nodes in set with the specified wrapper. If the nodes are siblings all of them
       
  4122 		 * will be wrapped in the same wrapper.
       
  4123 		 *
       
  4124 		 * @method wrapAll
       
  4125 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
       
  4126 		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
       
  4127 		 */
       
  4128 		wrapAll: function(wrapper) {
       
  4129 			return wrap(this, wrapper, true);
       
  4130 		},
       
  4131 
       
  4132 		/**
       
  4133 		 * Wraps all elements inner contents in set with the specified wrapper.
       
  4134 		 *
       
  4135 		 * @method wrapInner
       
  4136 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
       
  4137 		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
       
  4138 		 */
       
  4139 		wrapInner: function(wrapper) {
       
  4140 			this.each(function() {
       
  4141 				DomQuery(this).contents().wrapAll(wrapper);
       
  4142 			});
       
  4143 
       
  4144 			return this;
       
  4145 		},
       
  4146 
       
  4147 		/**
       
  4148 		 * Unwraps all elements by removing the parent element of each item in set.
       
  4149 		 *
       
  4150 		 * @method unwrap
       
  4151 		 * @return {tinymce.dom.DomQuery} Set with unwrapped nodes.
       
  4152 		 */
       
  4153 		unwrap: function() {
       
  4154 			return this.parent().each(function() {
       
  4155 				DomQuery(this).replaceWith(this.childNodes);
       
  4156 			});
       
  4157 		},
       
  4158 
       
  4159 		/**
       
  4160 		 * Clones all nodes in set.
       
  4161 		 *
       
  4162 		 * @method clone
       
  4163 		 * @return {tinymce.dom.DomQuery} Set with cloned nodes.
       
  4164 		 */
       
  4165 		clone: function() {
       
  4166 			var result = [];
       
  4167 
       
  4168 			this.each(function() {
       
  4169 				result.push(this.cloneNode(true));
       
  4170 			});
       
  4171 
       
  4172 			return DomQuery(result);
       
  4173 		},
       
  4174 
       
  4175 		/**
       
  4176 		 * Adds the specified class name to the current set elements.
       
  4177 		 *
       
  4178 		 * @method addClass
       
  4179 		 * @param {String} className Class name to add.
       
  4180 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4181 		 */
       
  4182 		addClass: function(className) {
       
  4183 			return this.toggleClass(className, true);
       
  4184 		},
       
  4185 
       
  4186 		/**
       
  4187 		 * Removes the specified class name to the current set elements.
       
  4188 		 *
       
  4189 		 * @method removeClass
       
  4190 		 * @param {String} className Class name to remove.
       
  4191 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4192 		 */
       
  4193 		removeClass: function(className) {
       
  4194 			return this.toggleClass(className, false);
       
  4195 		},
       
  4196 
       
  4197 		/**
       
  4198 		 * Toggles the specified class name on the current set elements.
       
  4199 		 *
       
  4200 		 * @method toggleClass
       
  4201 		 * @param {String} className Class name to add/remove.
       
  4202 		 * @param {Boolean} state Optional state to toggle on/off.
       
  4203 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4204 		 */
       
  4205 		toggleClass: function(className, state) {
       
  4206 			var self = this;
       
  4207 
       
  4208 			// Functions are not supported
       
  4209 			if (typeof className != 'string') {
       
  4210 				return self;
       
  4211 			}
       
  4212 
       
  4213 			if (className.indexOf(' ') !== -1) {
       
  4214 				each(className.split(' '), function() {
       
  4215 					self.toggleClass(this, state);
       
  4216 				});
       
  4217 			} else {
       
  4218 				self.each(function(index, node) {
       
  4219 					var existingClassName, classState;
       
  4220 
       
  4221 					classState = hasClass(node, className);
       
  4222 					if (classState !== state) {
       
  4223 						existingClassName = node.className;
       
  4224 
       
  4225 						if (classState) {
       
  4226 							node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
       
  4227 						} else {
       
  4228 							node.className += existingClassName ? ' ' + className : className;
       
  4229 						}
       
  4230 					}
       
  4231 				});
       
  4232 			}
       
  4233 
       
  4234 			return self;
       
  4235 		},
       
  4236 
       
  4237 		/**
       
  4238 		 * Returns true/false if the first item in set has the specified class.
       
  4239 		 *
       
  4240 		 * @method hasClass
       
  4241 		 * @param {String} className Class name to check for.
       
  4242 		 * @return {Boolean} True/false if the set has the specified class.
       
  4243 		 */
       
  4244 		hasClass: function(className) {
       
  4245 			return hasClass(this[0], className);
       
  4246 		},
       
  4247 
       
  4248 		/**
       
  4249 		 * Executes the callback function for each item DomQuery collection. If you return false in the
       
  4250 		 * callback it will break the loop.
       
  4251 		 *
       
  4252 		 * @method each
       
  4253 		 * @param {function} callback Callback function to execute for each item.
       
  4254 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4255 		 */
       
  4256 		each: function(callback) {
       
  4257 			return each(this, callback);
       
  4258 		},
       
  4259 
       
  4260 		/**
       
  4261 		 * Binds an event with callback function to the elements in set.
       
  4262 		 *
       
  4263 		 * @method on
       
  4264 		 * @param {String} name Name of the event to bind.
       
  4265 		 * @param {function} callback Callback function to execute when the event occurs.
       
  4266 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4267 		 */
       
  4268 		on: function(name, callback) {
       
  4269 			return this.each(function() {
       
  4270 				Event.bind(this, name, callback);
       
  4271 			});
       
  4272 		},
       
  4273 
       
  4274 		/**
       
  4275 		 * Unbinds an event with callback function to the elements in set.
       
  4276 		 *
       
  4277 		 * @method off
       
  4278 		 * @param {String} name Optional name of the event to bind.
       
  4279 		 * @param {function} callback Optional callback function to execute when the event occurs.
       
  4280 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4281 		 */
       
  4282 		off: function(name, callback) {
       
  4283 			return this.each(function() {
       
  4284 				Event.unbind(this, name, callback);
       
  4285 			});
       
  4286 		},
       
  4287 
       
  4288 		/**
       
  4289 		 * Triggers the specified event by name or event object.
       
  4290 		 *
       
  4291 		 * @method trigger
       
  4292 		 * @param {String/Object} name Name of the event to trigger or event object.
       
  4293 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4294 		 */
       
  4295 		trigger: function(name) {
       
  4296 			return this.each(function() {
       
  4297 				if (typeof name == 'object') {
       
  4298 					Event.fire(this, name.type, name);
       
  4299 				} else {
       
  4300 					Event.fire(this, name);
       
  4301 				}
       
  4302 			});
       
  4303 		},
       
  4304 
       
  4305 		/**
       
  4306 		 * Shows all elements in set.
       
  4307 		 *
       
  4308 		 * @method show
       
  4309 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4310 		 */
       
  4311 		show: function() {
       
  4312 			return this.css('display', '');
       
  4313 		},
       
  4314 
       
  4315 		/**
       
  4316 		 * Hides all elements in set.
       
  4317 		 *
       
  4318 		 * @method hide
       
  4319 		 * @return {tinymce.dom.DomQuery} Current set.
       
  4320 		 */
       
  4321 		hide: function() {
       
  4322 			return this.css('display', 'none');
       
  4323 		},
       
  4324 
       
  4325 		/**
       
  4326 		 * Slices the current set.
       
  4327 		 *
       
  4328 		 * @method slice
       
  4329 		 * @param {Number} start Start index to slice at.
       
  4330 		 * @param {Number} end Optional ened index to end slice at.
       
  4331 		 * @return {tinymce.dom.DomQuery} Sliced set.
       
  4332 		 */
       
  4333 		slice: function() {
       
  4334 			return new DomQuery(slice.apply(this, arguments));
       
  4335 		},
       
  4336 
       
  4337 		/**
       
  4338 		 * Makes the set equal to the specified index.
       
  4339 		 *
       
  4340 		 * @method eq
       
  4341 		 * @param {Number} index Index to set it equal to.
       
  4342 		 * @return {tinymce.dom.DomQuery} Single item set.
       
  4343 		 */
       
  4344 		eq: function(index) {
       
  4345 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
       
  4346 		},
       
  4347 
       
  4348 		/**
       
  4349 		 * Makes the set equal to first element in set.
       
  4350 		 *
       
  4351 		 * @method first
       
  4352 		 * @return {tinymce.dom.DomQuery} Single item set.
       
  4353 		 */
       
  4354 		first: function() {
       
  4355 			return this.eq(0);
       
  4356 		},
       
  4357 
       
  4358 		/**
       
  4359 		 * Makes the set equal to last element in set.
       
  4360 		 *
       
  4361 		 * @method last
       
  4362 		 * @return {tinymce.dom.DomQuery} Single item set.
       
  4363 		 */
       
  4364 		last: function() {
       
  4365 			return this.eq(-1);
       
  4366 		},
       
  4367 
       
  4368 		/**
       
  4369 		 * Finds elements by the specified selector for each element in set.
       
  4370 		 *
       
  4371 		 * @method find
       
  4372 		 * @param {String} selector Selector to find elements by.
       
  4373 		 * @return {tinymce.dom.DomQuery} Set with matches elements.
       
  4374 		 */
       
  4375 		find: function(selector) {
       
  4376 			var i, l, ret = [];
       
  4377 
       
  4378 			for (i = 0, l = this.length; i < l; i++) {
       
  4379 				DomQuery.find(selector, this[i], ret);
       
  4380 			}
       
  4381 
       
  4382 			return DomQuery(ret);
       
  4383 		},
       
  4384 
       
  4385 		/**
       
  4386 		 * Filters the current set with the specified selector.
       
  4387 		 *
       
  4388 		 * @method filter
       
  4389 		 * @param {String/function} selector Selector to filter elements by.
       
  4390 		 * @return {tinymce.dom.DomQuery} Set with filtered elements.
       
  4391 		 */
       
  4392 		filter: function(selector) {
       
  4393 			if (typeof selector == 'function') {
       
  4394 				return DomQuery(grep(this.toArray(), function(item, i) {
       
  4395 					return selector(i, item);
       
  4396 				}));
       
  4397 			}
       
  4398 
       
  4399 			return DomQuery(DomQuery.filter(selector, this.toArray()));
       
  4400 		},
       
  4401 
       
  4402 		/**
       
  4403 		 * Gets the current node or any partent matching the specified selector.
       
  4404 		 *
       
  4405 		 * @method closest
       
  4406 		 * @param {String/Element/tinymce.dom.DomQuery} selector Selector or element to find.
       
  4407 		 * @return {tinymce.dom.DomQuery} Set with closest elements.
       
  4408 		 */
       
  4409 		closest: function(selector) {
       
  4410 			var result = [];
       
  4411 
       
  4412 			if (selector instanceof DomQuery) {
       
  4413 				selector = selector[0];
       
  4414 			}
       
  4415 
       
  4416 			this.each(function(i, node) {
       
  4417 				while (node) {
       
  4418 					if (typeof selector == 'string' && DomQuery(node).is(selector)) {
       
  4419 						result.push(node);
       
  4420 						break;
       
  4421 					} else if (node == selector) {
       
  4422 						result.push(node);
       
  4423 						break;
       
  4424 					}
       
  4425 
       
  4426 					node = node.parentNode;
       
  4427 				}
       
  4428 			});
       
  4429 
       
  4430 			return DomQuery(result);
       
  4431 		},
       
  4432 
       
  4433 		/**
       
  4434 		 * Returns the offset of the first element in set or sets the top/left css properties of all elements in set.
       
  4435 		 *
       
  4436 		 * @method offset
       
  4437 		 * @param {Object} offset Optional offset object to set on each item.
       
  4438 		 * @return {Object/tinymce.dom.DomQuery} Returns the first element offset or the current set if you specified an offset.
       
  4439 		 */
       
  4440 		offset: function(offset) {
       
  4441 			var elm, doc, docElm;
       
  4442 			var x = 0, y = 0, pos;
       
  4443 
       
  4444 			if (!offset) {
       
  4445 				elm = this[0];
       
  4446 
       
  4447 				if (elm) {
       
  4448 					doc = elm.ownerDocument;
       
  4449 					docElm = doc.documentElement;
       
  4450 
       
  4451 					if (elm.getBoundingClientRect) {
       
  4452 						pos = elm.getBoundingClientRect();
       
  4453 						x = pos.left + (docElm.scrollLeft || doc.body.scrollLeft) - docElm.clientLeft;
       
  4454 						y = pos.top + (docElm.scrollTop || doc.body.scrollTop) - docElm.clientTop;
       
  4455 					}
       
  4456 				}
       
  4457 
       
  4458 				return {
       
  4459 					left: x,
       
  4460 					top: y
       
  4461 				};
       
  4462 			}
       
  4463 
       
  4464 			return this.css(offset);
       
  4465 		},
       
  4466 
       
  4467 		push: push,
       
  4468 		sort: [].sort,
       
  4469 		splice: [].splice
       
  4470 	};
       
  4471 
       
  4472 	// Static members
       
  4473 	Tools.extend(DomQuery, {
       
  4474 		/**
       
  4475 		 * Extends the specified object with one or more objects.
       
  4476 		 *
       
  4477 		 * @static
       
  4478 		 * @method extend
       
  4479 		 * @param {Object} target Target object to extend with new items.
       
  4480 		 * @param {Object..} object Object to extend the target with.
       
  4481 		 * @return {Object} Extended input object.
       
  4482 		 */
       
  4483 		extend: Tools.extend,
       
  4484 
       
  4485 		/**
       
  4486 		 * Creates an array out of an array like object.
       
  4487 		 *
       
  4488 		 * @static
       
  4489 		 * @method makeArray
       
  4490 		 * @param {Object} object Object to convert to array.
       
  4491 		 * @return {Arrau} Array produced from object.
       
  4492 		 */
       
  4493 		makeArray: function(array) {
       
  4494 			if (isWindow(array) || array.nodeType) {
       
  4495 				return [array];
       
  4496 			}
       
  4497 
       
  4498 			return Tools.toArray(array);
       
  4499 		},
       
  4500 
       
  4501 		/**
       
  4502 		 * Returns the index of the specified item inside the array.
       
  4503 		 *
       
  4504 		 * @static
       
  4505 		 * @method inArray
       
  4506 		 * @param {Object} item Item to look for.
       
  4507 		 * @param {Array} array Array to look for item in.
       
  4508 		 * @return {Number} Index of the item or -1.
       
  4509 		 */
       
  4510 		inArray: inArray,
       
  4511 
       
  4512 		/**
       
  4513 		 * Returns true/false if the specified object is an array or not.
       
  4514 		 *
       
  4515 		 * @static
       
  4516 		 * @method isArray
       
  4517 		 * @param {Object} array Object to check if it's an array or not.
       
  4518 		 * @return {Boolean} True/false if the object is an array.
       
  4519 		 */
       
  4520 		isArray: Tools.isArray,
       
  4521 
       
  4522 		/**
       
  4523 		 * Executes the callback function for each item in array/object. If you return false in the
       
  4524 		 * callback it will break the loop.
       
  4525 		 *
       
  4526 		 * @static
       
  4527 		 * @method each
       
  4528 		 * @param {Object} obj Object to iterate.
       
  4529 		 * @param {function} callback Callback function to execute for each item.
       
  4530 		 */
       
  4531 		each: each,
       
  4532 
       
  4533 		/**
       
  4534 		 * Removes whitespace from the beginning and end of a string.
       
  4535 		 *
       
  4536 		 * @static
       
  4537 		 * @method trim
       
  4538 		 * @param {String} str String to remove whitespace from.
       
  4539 		 * @return {String} New string with removed whitespace.
       
  4540 		 */
       
  4541 		trim: trim,
       
  4542 
       
  4543 		/**
       
  4544 		 * Filters out items from the input array by calling the specified function for each item.
       
  4545 		 * If the function returns false the item will be excluded if it returns true it will be included.
       
  4546 		 *
       
  4547 		 * @static
       
  4548 		 * @method grep
       
  4549 		 * @param {Array} array Array of items to loop though.
       
  4550 		 * @param {function} callback Function to call for each item. Include/exclude depends on it's return value.
       
  4551 		 * @return {Array} New array with values imported and filtered based in input.
       
  4552 		 * @example
       
  4553 		 * // Filter out some items, this will return an array with 4 and 5
       
  4554 		 * var items = DomQuery.grep([1, 2, 3, 4, 5], function(v) {return v > 3;});
       
  4555 		 */
       
  4556 		grep: grep,
       
  4557 
       
  4558 		// Sizzle
       
  4559 		find: Sizzle,
       
  4560 		expr: Sizzle.selectors,
       
  4561 		unique: Sizzle.uniqueSort,
       
  4562 		text: Sizzle.getText,
       
  4563 		contains: Sizzle.contains,
       
  4564 		filter: function(expr, elems, not) {
       
  4565 			var i = elems.length;
       
  4566 
       
  4567 			if (not) {
       
  4568 				expr = ":not(" + expr + ")";
       
  4569 			}
       
  4570 
       
  4571 			while (i--) {
       
  4572 				if (elems[i].nodeType != 1) {
       
  4573 					elems.splice(i, 1);
       
  4574 				}
       
  4575 			}
       
  4576 
       
  4577 			if (elems.length === 1) {
       
  4578 				elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [];
       
  4579 			} else {
       
  4580 				elems = DomQuery.find.matches(expr, elems);
       
  4581 			}
       
  4582 
       
  4583 			return elems;
       
  4584 		}
       
  4585 	});
       
  4586 
       
  4587 	function dir(el, prop, until) {
       
  4588 		var matched = [], cur = el[prop];
       
  4589 
       
  4590 		if (typeof until != 'string' && until instanceof DomQuery) {
       
  4591 			until = until[0];
       
  4592 		}
       
  4593 
       
  4594 		while (cur && cur.nodeType !== 9) {
       
  4595 			if (until !== undefined) {
       
  4596 				if (cur === until) {
       
  4597 					break;
       
  4598 				}
       
  4599 
       
  4600 				if (typeof until == 'string' && DomQuery(cur).is(until)) {
       
  4601 					break;
       
  4602 				}
       
  4603 			}
       
  4604 
       
  4605 			if (cur.nodeType === 1) {
       
  4606 				matched.push(cur);
       
  4607 			}
       
  4608 
       
  4609 			cur = cur[prop];
       
  4610 		}
       
  4611 
       
  4612 		return matched;
       
  4613 	}
       
  4614 
       
  4615 	function sibling(node, siblingName, nodeType, until) {
       
  4616 		var result = [];
       
  4617 
       
  4618 		if (until instanceof DomQuery) {
       
  4619 			until = until[0];
       
  4620 		}
       
  4621 
       
  4622 		for (; node; node = node[siblingName]) {
       
  4623 			if (nodeType && node.nodeType !== nodeType) {
       
  4624 				continue;
       
  4625 			}
       
  4626 
       
  4627 			if (until !== undefined) {
       
  4628 				if (node === until) {
       
  4629 					break;
       
  4630 				}
       
  4631 
       
  4632 				if (typeof until == 'string' && DomQuery(node).is(until)) {
       
  4633 					break;
       
  4634 				}
       
  4635 			}
       
  4636 
       
  4637 			result.push(node);
       
  4638 		}
       
  4639 
       
  4640 		return result;
       
  4641 	}
       
  4642 
       
  4643 	function firstSibling(node, siblingName, nodeType) {
       
  4644 		for (node = node[siblingName]; node; node = node[siblingName]) {
       
  4645 			if (node.nodeType == nodeType) {
       
  4646 				return node;
       
  4647 			}
       
  4648 		}
       
  4649 
       
  4650 		return null;
       
  4651 	}
       
  4652 
       
  4653 	each({
       
  4654 		/**
       
  4655 		 * Returns a new collection with the parent of each item in current collection matching the optional selector.
       
  4656 		 *
       
  4657 		 * @method parent
       
  4658 		 * @param {String} selector Selector to match parents agains.
       
  4659 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
       
  4660 		 */
       
  4661 		parent: function(node) {
       
  4662 			var parent = node.parentNode;
       
  4663 
       
  4664 			return parent && parent.nodeType !== 11 ? parent : null;
       
  4665 		},
       
  4666 
       
  4667 		/**
       
  4668 		 * Returns a new collection with the all the parents of each item in current collection matching the optional selector.
       
  4669 		 *
       
  4670 		 * @method parents
       
  4671 		 * @param {String} selector Selector to match parents agains.
       
  4672 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
       
  4673 		 */
       
  4674 		parents: function(node) {
       
  4675 			return dir(node, "parentNode");
       
  4676 		},
       
  4677 
       
  4678 		/**
       
  4679 		 * Returns a new collection with next sibling of each item in current collection matching the optional selector.
       
  4680 		 *
       
  4681 		 * @method next
       
  4682 		 * @param {String} selector Selector to match the next element against.
       
  4683 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  4684 		 */
       
  4685 		next: function(node) {
       
  4686 			return firstSibling(node, 'nextSibling', 1);
       
  4687 		},
       
  4688 
       
  4689 		/**
       
  4690 		 * Returns a new collection with previous sibling of each item in current collection matching the optional selector.
       
  4691 		 *
       
  4692 		 * @method prev
       
  4693 		 * @param {String} selector Selector to match the previous element against.
       
  4694 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  4695 		 */
       
  4696 		prev: function(node) {
       
  4697 			return firstSibling(node, 'previousSibling', 1);
       
  4698 		},
       
  4699 
       
  4700 		/**
       
  4701 		 * Returns all child elements matching the optional selector.
       
  4702 		 *
       
  4703 		 * @method children
       
  4704 		 * @param {String} selector Selector to match the elements against.
       
  4705 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  4706 		 */
       
  4707 		children: function(node) {
       
  4708 			return sibling(node.firstChild, 'nextSibling', 1);
       
  4709 		},
       
  4710 
       
  4711 		/**
       
  4712 		 * Returns all child nodes matching the optional selector.
       
  4713 		 *
       
  4714 		 * @method contents
       
  4715 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  4716 		 */
       
  4717 		contents: function(node) {
       
  4718 			return Tools.toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
       
  4719 		}
       
  4720 	}, function(name, fn) {
       
  4721 		DomQuery.fn[name] = function(selector) {
       
  4722 			var self = this, result = [];
       
  4723 
       
  4724 			self.each(function() {
       
  4725 				var nodes = fn.call(result, this, selector, result);
       
  4726 
       
  4727 				if (nodes) {
       
  4728 					if (DomQuery.isArray(nodes)) {
       
  4729 						result.push.apply(result, nodes);
       
  4730 					} else {
       
  4731 						result.push(nodes);
       
  4732 					}
       
  4733 				}
       
  4734 			});
       
  4735 
       
  4736 			// If traversing on multiple elements we might get the same elements twice
       
  4737 			if (this.length > 1) {
       
  4738 				result = DomQuery.unique(result);
       
  4739 
       
  4740 				if (name.indexOf('parents') === 0) {
       
  4741 					result = result.reverse();
       
  4742 				}
       
  4743 			}
       
  4744 
       
  4745 			result = DomQuery(result);
       
  4746 
       
  4747 			if (selector) {
       
  4748 				return result.filter(selector);
       
  4749 			}
       
  4750 
       
  4751 			return result;
       
  4752 		};
       
  4753 	});
       
  4754 
       
  4755 	each({
       
  4756 		/**
       
  4757 		 * Returns a new collection with the all the parents until the matching selector/element
       
  4758 		 * of each item in current collection matching the optional selector.
       
  4759 		 *
       
  4760 		 * @method parentsUntil
       
  4761 		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
       
  4762 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
       
  4763 		 */
       
  4764 		parentsUntil: function(node, until) {
       
  4765 			return dir(node, "parentNode", until);
       
  4766 		},
       
  4767 
       
  4768 		/**
       
  4769 		 * Returns a new collection with all next siblings of each item in current collection matching the optional selector.
       
  4770 		 *
       
  4771 		 * @method nextUntil
       
  4772 		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
       
  4773 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  4774 		 */
       
  4775 		nextUntil: function(node, until) {
       
  4776 			return sibling(node, 'nextSibling', 1, until).slice(1);
       
  4777 		},
       
  4778 
       
  4779 		/**
       
  4780 		 * Returns a new collection with all previous siblings of each item in current collection matching the optional selector.
       
  4781 		 *
       
  4782 		 * @method prevUntil
       
  4783 		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
       
  4784 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  4785 		 */
       
  4786 		prevUntil: function(node, until) {
       
  4787 			return sibling(node, 'previousSibling', 1, until).slice(1);
       
  4788 		}
       
  4789 	}, function(name, fn) {
       
  4790 		DomQuery.fn[name] = function(selector, filter) {
       
  4791 			var self = this, result = [];
       
  4792 
       
  4793 			self.each(function() {
       
  4794 				var nodes = fn.call(result, this, selector, result);
       
  4795 
       
  4796 				if (nodes) {
       
  4797 					if (DomQuery.isArray(nodes)) {
       
  4798 						result.push.apply(result, nodes);
       
  4799 					} else {
       
  4800 						result.push(nodes);
       
  4801 					}
       
  4802 				}
       
  4803 			});
       
  4804 
       
  4805 			// If traversing on multiple elements we might get the same elements twice
       
  4806 			if (this.length > 1) {
       
  4807 				result = DomQuery.unique(result);
       
  4808 
       
  4809 				if (name.indexOf('parents') === 0 || name === 'prevUntil') {
       
  4810 					result = result.reverse();
       
  4811 				}
       
  4812 			}
       
  4813 
       
  4814 			result = DomQuery(result);
       
  4815 
       
  4816 			if (filter) {
       
  4817 				return result.filter(filter);
       
  4818 			}
       
  4819 
       
  4820 			return result;
       
  4821 		};
       
  4822 	});
       
  4823 
       
  4824 	/**
       
  4825 	 * Returns true/false if the current set items matches the selector.
       
  4826 	 *
       
  4827 	 * @method is
       
  4828 	 * @param {String} selector Selector to match the elements against.
       
  4829 	 * @return {Boolean} True/false if the current set matches the selector.
       
  4830 	 */
       
  4831 	DomQuery.fn.is = function(selector) {
       
  4832 		return !!selector && this.filter(selector).length > 0;
       
  4833 	};
       
  4834 
       
  4835 	DomQuery.fn.init.prototype = DomQuery.fn;
       
  4836 
       
  4837 	DomQuery.overrideDefaults = function(callback) {
       
  4838 		var defaults;
       
  4839 
       
  4840 		function sub(selector, context) {
       
  4841 			defaults = defaults || callback();
       
  4842 
       
  4843 			if (arguments.length === 0) {
       
  4844 				selector = defaults.element;
       
  4845 			}
       
  4846 
       
  4847 			if (!context) {
       
  4848 				context = defaults.context;
       
  4849 			}
       
  4850 
       
  4851 			return new sub.fn.init(selector, context);
       
  4852 		}
       
  4853 
       
  4854 		DomQuery.extend(sub, this);
       
  4855 
       
  4856 		return sub;
       
  4857 	};
       
  4858 
       
  4859 	function appendHooks(targetHooks, prop, hooks) {
       
  4860 		each(hooks, function(name, func) {
       
  4861 			targetHooks[name] = targetHooks[name] || {};
       
  4862 			targetHooks[name][prop] = func;
       
  4863 		});
       
  4864 	}
       
  4865 
       
  4866 	if (Env.ie && Env.ie < 8) {
       
  4867 		appendHooks(attrHooks, 'get', {
       
  4868 			maxlength: function(elm) {
       
  4869 				var value = elm.maxLength;
       
  4870 
       
  4871 				if (value === 0x7fffffff) {
       
  4872 					return undef;
       
  4873 				}
       
  4874 
       
  4875 				return value;
       
  4876 			},
       
  4877 
       
  4878 			size: function(elm) {
       
  4879 				var value = elm.size;
       
  4880 
       
  4881 				if (value === 20) {
       
  4882 					return undef;
       
  4883 				}
       
  4884 
       
  4885 				return value;
       
  4886 			},
       
  4887 
       
  4888 			'class': function(elm) {
       
  4889 				return elm.className;
       
  4890 			},
       
  4891 
       
  4892 			style: function(elm) {
       
  4893 				var value = elm.style.cssText;
       
  4894 
       
  4895 				if (value.length === 0) {
       
  4896 					return undef;
       
  4897 				}
       
  4898 
       
  4899 				return value;
       
  4900 			}
       
  4901 		});
       
  4902 
       
  4903 		appendHooks(attrHooks, 'set', {
       
  4904 			'class': function(elm, value) {
       
  4905 				elm.className = value;
       
  4906 			},
       
  4907 
       
  4908 			style: function(elm, value) {
       
  4909 				elm.style.cssText = value;
       
  4910 			}
       
  4911 		});
       
  4912 	}
       
  4913 
       
  4914 	if (Env.ie && Env.ie < 9) {
       
  4915 		/*jshint sub:true */
       
  4916 		/*eslint dot-notation: 0*/
       
  4917 		cssFix['float'] = 'styleFloat';
       
  4918 
       
  4919 		appendHooks(cssHooks, 'set', {
       
  4920 			opacity: function(elm, value) {
       
  4921 				var style = elm.style;
       
  4922 
       
  4923 				if (value === null || value === '') {
       
  4924 					style.removeAttribute('filter');
       
  4925 				} else {
       
  4926 					style.zoom = 1;
       
  4927 					style.filter = 'alpha(opacity=' + (value * 100) + ')';
       
  4928 				}
       
  4929 			}
       
  4930 		});
       
  4931 	}
       
  4932 
       
  4933 	DomQuery.attrHooks = attrHooks;
       
  4934 	DomQuery.cssHooks = cssHooks;
       
  4935 
       
  4936 	return DomQuery;
       
  4937 });
       
  4938 
       
  4939 // Included from: js/tinymce/classes/html/Styles.js
       
  4940 
       
  4941 /**
       
  4942  * Styles.js
       
  4943  *
       
  4944  * Copyright, Moxiecode Systems AB
       
  4945  * Released under LGPL License.
       
  4946  *
       
  4947  * License: http://www.tinymce.com/license
       
  4948  * Contributing: http://www.tinymce.com/contributing
       
  4949  */
       
  4950 
       
  4951 /**
       
  4952  * This class is used to parse CSS styles it also compresses styles to reduce the output size.
       
  4953  *
       
  4954  * @example
       
  4955  * var Styles = new tinymce.html.Styles({
       
  4956  *    url_converter: function(url) {
       
  4957  *       return url;
       
  4958  *    }
       
  4959  * });
       
  4960  *
       
  4961  * styles = Styles.parse('border: 1px solid red');
       
  4962  * styles.color = 'red';
       
  4963  *
       
  4964  * console.log(new tinymce.html.StyleSerializer().serialize(styles));
       
  4965  *
       
  4966  * @class tinymce.html.Styles
       
  4967  * @version 3.4
       
  4968  */
       
  4969 define("tinymce/html/Styles", [], function() {
       
  4970 	return function(settings, schema) {
       
  4971 		/*jshint maxlen:255 */
       
  4972 		/*eslint max-len:0 */
       
  4973 		var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
       
  4974 			urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
       
  4975 			styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
       
  4976 			trimRightRegExp = /\s+$/,
       
  4977 			undef, i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
       
  4978 
       
  4979 		settings = settings || {};
       
  4980 
       
  4981 		if (schema) {
       
  4982 			validStyles = schema.getValidStyles();
       
  4983 			invalidStyles = schema.getInvalidStyles();
       
  4984 		}
       
  4985 
       
  4986 		encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
       
  4987 		for (i = 0; i < encodingItems.length; i++) {
       
  4988 			encodingLookup[encodingItems[i]] = invisibleChar + i;
       
  4989 			encodingLookup[invisibleChar + i] = encodingItems[i];
       
  4990 		}
       
  4991 
       
  4992 		function toHex(match, r, g, b) {
       
  4993 			function hex(val) {
       
  4994 				val = parseInt(val, 10).toString(16);
       
  4995 
       
  4996 				return val.length > 1 ? val : '0' + val; // 0 -> 00
       
  4997 			}
       
  4998 
       
  4999 			return '#' + hex(r) + hex(g) + hex(b);
       
  5000 		}
       
  5001 
       
  5002 		return {
       
  5003 			/**
       
  5004 			 * Parses the specified RGB color value and returns a hex version of that color.
       
  5005 			 *
       
  5006 			 * @method toHex
       
  5007 			 * @param {String} color RGB string value like rgb(1,2,3)
       
  5008 			 * @return {String} Hex version of that RGB value like #FF00FF.
       
  5009 			 */
       
  5010 			toHex: function(color) {
       
  5011 				return color.replace(rgbRegExp, toHex);
       
  5012 			},
       
  5013 
       
  5014 			/**
       
  5015 			 * Parses the specified style value into an object collection. This parser will also
       
  5016 			 * merge and remove any redundant items that browsers might have added. It will also convert non hex
       
  5017 			 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
       
  5018 			 *
       
  5019 			 * @method parse
       
  5020 			 * @param {String} css Style value to parse for example: border:1px solid red;.
       
  5021 			 * @return {Object} Object representation of that style like {border: '1px solid red'}
       
  5022 			 */
       
  5023 			parse: function(css) {
       
  5024 				var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
       
  5025 				var urlConverterScope = settings.url_converter_scope || this;
       
  5026 
       
  5027 				function compress(prefix, suffix, noJoin) {
       
  5028 					var top, right, bottom, left;
       
  5029 
       
  5030 					top = styles[prefix + '-top' + suffix];
       
  5031 					if (!top) {
       
  5032 						return;
       
  5033 					}
       
  5034 
       
  5035 					right = styles[prefix + '-right' + suffix];
       
  5036 					if (!right) {
       
  5037 						return;
       
  5038 					}
       
  5039 
       
  5040 					bottom = styles[prefix + '-bottom' + suffix];
       
  5041 					if (!bottom) {
       
  5042 						return;
       
  5043 					}
       
  5044 
       
  5045 					left = styles[prefix + '-left' + suffix];
       
  5046 					if (!left) {
       
  5047 						return;
       
  5048 					}
       
  5049 
       
  5050 					var box = [top, right, bottom, left];
       
  5051 					i = box.length - 1;
       
  5052 					while (i--) {
       
  5053 						if (box[i] !== box[i + 1]) {
       
  5054 							break;
       
  5055 						}
       
  5056 					}
       
  5057 
       
  5058 					if (i > -1 && noJoin) {
       
  5059 						return;
       
  5060 					}
       
  5061 
       
  5062 					styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
       
  5063 					delete styles[prefix + '-top' + suffix];
       
  5064 					delete styles[prefix + '-right' + suffix];
       
  5065 					delete styles[prefix + '-bottom' + suffix];
       
  5066 					delete styles[prefix + '-left' + suffix];
       
  5067 				}
       
  5068 
       
  5069 				/**
       
  5070 				 * Checks if the specific style can be compressed in other words if all border-width are equal.
       
  5071 				 */
       
  5072 				function canCompress(key) {
       
  5073 					var value = styles[key], i;
       
  5074 
       
  5075 					if (!value) {
       
  5076 						return;
       
  5077 					}
       
  5078 
       
  5079 					value = value.split(' ');
       
  5080 					i = value.length;
       
  5081 					while (i--) {
       
  5082 						if (value[i] !== value[0]) {
       
  5083 							return false;
       
  5084 						}
       
  5085 					}
       
  5086 
       
  5087 					styles[key] = value[0];
       
  5088 
       
  5089 					return true;
       
  5090 				}
       
  5091 
       
  5092 				/**
       
  5093 				 * Compresses multiple styles into one style.
       
  5094 				 */
       
  5095 				function compress2(target, a, b, c) {
       
  5096 					if (!canCompress(a)) {
       
  5097 						return;
       
  5098 					}
       
  5099 
       
  5100 					if (!canCompress(b)) {
       
  5101 						return;
       
  5102 					}
       
  5103 
       
  5104 					if (!canCompress(c)) {
       
  5105 						return;
       
  5106 					}
       
  5107 
       
  5108 					// Compress
       
  5109 					styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
       
  5110 					delete styles[a];
       
  5111 					delete styles[b];
       
  5112 					delete styles[c];
       
  5113 				}
       
  5114 
       
  5115 				// Encodes the specified string by replacing all \" \' ; : with _<num>
       
  5116 				function encode(str) {
       
  5117 					isEncoded = true;
       
  5118 
       
  5119 					return encodingLookup[str];
       
  5120 				}
       
  5121 
       
  5122 				// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
       
  5123 				// It will also decode the \" \' if keep_slashes is set to fale or omitted
       
  5124 				function decode(str, keep_slashes) {
       
  5125 					if (isEncoded) {
       
  5126 						str = str.replace(/\uFEFF[0-9]/g, function(str) {
       
  5127 							return encodingLookup[str];
       
  5128 						});
       
  5129 					}
       
  5130 
       
  5131 					if (!keep_slashes) {
       
  5132 						str = str.replace(/\\([\'\";:])/g, "$1");
       
  5133 					}
       
  5134 
       
  5135 					return str;
       
  5136 				}
       
  5137 
       
  5138 				function processUrl(match, url, url2, url3, str, str2) {
       
  5139 					str = str || str2;
       
  5140 
       
  5141 					if (str) {
       
  5142 						str = decode(str);
       
  5143 
       
  5144 						// Force strings into single quote format
       
  5145 						return "'" + str.replace(/\'/g, "\\'") + "'";
       
  5146 					}
       
  5147 
       
  5148 					url = decode(url || url2 || url3);
       
  5149 
       
  5150 					if (!settings.allow_script_urls) {
       
  5151 						var scriptUrl = url.replace(/[\s\r\n]+/, '');
       
  5152 
       
  5153 						if (/(java|vb)script:/i.test(scriptUrl)) {
       
  5154 							return "";
       
  5155 						}
       
  5156 
       
  5157 						if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
       
  5158 							return "";
       
  5159 						}
       
  5160 					}
       
  5161 
       
  5162 					// Convert the URL to relative/absolute depending on config
       
  5163 					if (urlConverter) {
       
  5164 						url = urlConverter.call(urlConverterScope, url, 'style');
       
  5165 					}
       
  5166 
       
  5167 					// Output new URL format
       
  5168 					return "url('" + url.replace(/\'/g, "\\'") + "')";
       
  5169 				}
       
  5170 
       
  5171 				if (css) {
       
  5172 					css = css.replace(/[\u0000-\u001F]/g, '');
       
  5173 
       
  5174 					// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
       
  5175 					css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
       
  5176 						return str.replace(/[;:]/g, encode);
       
  5177 					});
       
  5178 
       
  5179 					// Parse styles
       
  5180 					while ((matches = styleRegExp.exec(css))) {
       
  5181 						name = matches[1].replace(trimRightRegExp, '').toLowerCase();
       
  5182 						value = matches[2].replace(trimRightRegExp, '');
       
  5183 
       
  5184 						// Decode escaped sequences like \65 -> e
       
  5185 						/*jshint loopfunc:true*/
       
  5186 						/*eslint no-loop-func:0 */
       
  5187 						value = value.replace(/\\[0-9a-f]+/g, function(e) {
       
  5188 							return String.fromCharCode(parseInt(e.substr(1), 16));
       
  5189 						});
       
  5190 
       
  5191 						if (name && value.length > 0) {
       
  5192 							// Don't allow behavior name or expression/comments within the values
       
  5193 							if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
       
  5194 								continue;
       
  5195 							}
       
  5196 
       
  5197 							// Opera will produce 700 instead of bold in their style values
       
  5198 							if (name === 'font-weight' && value === '700') {
       
  5199 								value = 'bold';
       
  5200 							} else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
       
  5201 								value = value.toLowerCase();
       
  5202 							}
       
  5203 
       
  5204 							// Convert RGB colors to HEX
       
  5205 							value = value.replace(rgbRegExp, toHex);
       
  5206 
       
  5207 							// Convert URLs and force them into url('value') format
       
  5208 							value = value.replace(urlOrStrRegExp, processUrl);
       
  5209 							styles[name] = isEncoded ? decode(value, true) : value;
       
  5210 						}
       
  5211 
       
  5212 						styleRegExp.lastIndex = matches.index + matches[0].length;
       
  5213 					}
       
  5214 					// Compress the styles to reduce it's size for example IE will expand styles
       
  5215 					compress("border", "", true);
       
  5216 					compress("border", "-width");
       
  5217 					compress("border", "-color");
       
  5218 					compress("border", "-style");
       
  5219 					compress("padding", "");
       
  5220 					compress("margin", "");
       
  5221 					compress2('border', 'border-width', 'border-style', 'border-color');
       
  5222 
       
  5223 					// Remove pointless border, IE produces these
       
  5224 					if (styles.border === 'medium none') {
       
  5225 						delete styles.border;
       
  5226 					}
       
  5227 
       
  5228 					// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
       
  5229 					// So lets asume it shouldn't be there
       
  5230 					if (styles['border-image'] === 'none') {
       
  5231 						delete styles['border-image'];
       
  5232 					}
       
  5233 				}
       
  5234 
       
  5235 				return styles;
       
  5236 			},
       
  5237 
       
  5238 			/**
       
  5239 			 * Serializes the specified style object into a string.
       
  5240 			 *
       
  5241 			 * @method serialize
       
  5242 			 * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
       
  5243 			 * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
       
  5244 			 * @return {String} String representation of the style object for example: border: 1px solid red.
       
  5245 			 */
       
  5246 			serialize: function(styles, elementName) {
       
  5247 				var css = '', name, value;
       
  5248 
       
  5249 				function serializeStyles(name) {
       
  5250 					var styleList, i, l, value;
       
  5251 
       
  5252 					styleList = validStyles[name];
       
  5253 					if (styleList) {
       
  5254 						for (i = 0, l = styleList.length; i < l; i++) {
       
  5255 							name = styleList[i];
       
  5256 							value = styles[name];
       
  5257 
       
  5258 							if (value !== undef && value.length > 0) {
       
  5259 								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
       
  5260 							}
       
  5261 						}
       
  5262 					}
       
  5263 				}
       
  5264 
       
  5265 				function isValid(name, elementName) {
       
  5266 					var styleMap;
       
  5267 
       
  5268 					styleMap = invalidStyles['*'];
       
  5269 					if (styleMap && styleMap[name]) {
       
  5270 						return false;
       
  5271 					}
       
  5272 
       
  5273 					styleMap = invalidStyles[elementName];
       
  5274 					if (styleMap && styleMap[name]) {
       
  5275 						return false;
       
  5276 					}
       
  5277 
       
  5278 					return true;
       
  5279 				}
       
  5280 
       
  5281 				// Serialize styles according to schema
       
  5282 				if (elementName && validStyles) {
       
  5283 					// Serialize global styles and element specific styles
       
  5284 					serializeStyles('*');
       
  5285 					serializeStyles(elementName);
       
  5286 				} else {
       
  5287 					// Output the styles in the order they are inside the object
       
  5288 					for (name in styles) {
       
  5289 						value = styles[name];
       
  5290 
       
  5291 						if (value !== undef && value.length > 0) {
       
  5292 							if (!invalidStyles || isValid(name, elementName)) {
       
  5293 								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
       
  5294 							}
       
  5295 						}
       
  5296 					}
       
  5297 				}
       
  5298 
       
  5299 				return css;
       
  5300 			}
       
  5301 		};
       
  5302 	};
       
  5303 });
       
  5304 
       
  5305 // Included from: js/tinymce/classes/dom/TreeWalker.js
       
  5306 
       
  5307 /**
       
  5308  * TreeWalker.js
       
  5309  *
       
  5310  * Copyright, Moxiecode Systems AB
       
  5311  * Released under LGPL License.
       
  5312  *
       
  5313  * License: http://www.tinymce.com/license
       
  5314  * Contributing: http://www.tinymce.com/contributing
       
  5315  */
       
  5316 
       
  5317 /**
       
  5318  * TreeWalker class enables you to walk the DOM in a linear manner.
       
  5319  *
       
  5320  * @class tinymce.dom.TreeWalker
       
  5321  * @example
       
  5322  * var walker = new tinymce.dom.TreeWalker(startNode);
       
  5323  *
       
  5324  * do {
       
  5325  *     console.log(walker.current());
       
  5326  * } while (walker.next());
       
  5327  */
       
  5328 define("tinymce/dom/TreeWalker", [], function() {
       
  5329 	/**
       
  5330 	 * Constructs a new TreeWalker instance.
       
  5331 	 *
       
  5332 	 * @constructor
       
  5333 	 * @method TreeWalker
       
  5334 	 * @param {Node} startNode Node to start walking from.
       
  5335 	 * @param {node} rootNode Optional root node to never walk out of.
       
  5336 	 */
       
  5337 	return function(startNode, rootNode) {
       
  5338 		var node = startNode;
       
  5339 
       
  5340 		function findSibling(node, startName, siblingName, shallow) {
       
  5341 			var sibling, parent;
       
  5342 
       
  5343 			if (node) {
       
  5344 				// Walk into nodes if it has a start
       
  5345 				if (!shallow && node[startName]) {
       
  5346 					return node[startName];
       
  5347 				}
       
  5348 
       
  5349 				// Return the sibling if it has one
       
  5350 				if (node != rootNode) {
       
  5351 					sibling = node[siblingName];
       
  5352 					if (sibling) {
       
  5353 						return sibling;
       
  5354 					}
       
  5355 
       
  5356 					// Walk up the parents to look for siblings
       
  5357 					for (parent = node.parentNode; parent && parent != rootNode; parent = parent.parentNode) {
       
  5358 						sibling = parent[siblingName];
       
  5359 						if (sibling) {
       
  5360 							return sibling;
       
  5361 						}
       
  5362 					}
       
  5363 				}
       
  5364 			}
       
  5365 		}
       
  5366 
       
  5367 		/**
       
  5368 		 * Returns the current node.
       
  5369 		 *
       
  5370 		 * @method current
       
  5371 		 * @return {Node} Current node where the walker is.
       
  5372 		 */
       
  5373 		this.current = function() {
       
  5374 			return node;
       
  5375 		};
       
  5376 
       
  5377 		/**
       
  5378 		 * Walks to the next node in tree.
       
  5379 		 *
       
  5380 		 * @method next
       
  5381 		 * @return {Node} Current node where the walker is after moving to the next node.
       
  5382 		 */
       
  5383 		this.next = function(shallow) {
       
  5384 			node = findSibling(node, 'firstChild', 'nextSibling', shallow);
       
  5385 			return node;
       
  5386 		};
       
  5387 
       
  5388 		/**
       
  5389 		 * Walks to the previous node in tree.
       
  5390 		 *
       
  5391 		 * @method prev
       
  5392 		 * @return {Node} Current node where the walker is after moving to the previous node.
       
  5393 		 */
       
  5394 		this.prev = function(shallow) {
       
  5395 			node = findSibling(node, 'lastChild', 'previousSibling', shallow);
       
  5396 			return node;
       
  5397 		};
       
  5398 	};
       
  5399 });
       
  5400 
       
  5401 // Included from: js/tinymce/classes/dom/Range.js
       
  5402 
       
  5403 /**
       
  5404  * Range.js
       
  5405  *
       
  5406  * Copyright, Moxiecode Systems AB
       
  5407  * Released under LGPL License.
       
  5408  *
       
  5409  * License: http://www.tinymce.com/license
       
  5410  * Contributing: http://www.tinymce.com/contributing
       
  5411  */
       
  5412 
       
  5413 define("tinymce/dom/Range", [
       
  5414 	"tinymce/util/Tools"
       
  5415 ], function(Tools) {
       
  5416 	// Range constructor
       
  5417 	function Range(dom) {
       
  5418 		var self = this,
       
  5419 			doc = dom.doc,
       
  5420 			EXTRACT = 0,
       
  5421 			CLONE = 1,
       
  5422 			DELETE = 2,
       
  5423 			TRUE = true,
       
  5424 			FALSE = false,
       
  5425 			START_OFFSET = 'startOffset',
       
  5426 			START_CONTAINER = 'startContainer',
       
  5427 			END_CONTAINER = 'endContainer',
       
  5428 			END_OFFSET = 'endOffset',
       
  5429 			extend = Tools.extend,
       
  5430 			nodeIndex = dom.nodeIndex;
       
  5431 
       
  5432 		function createDocumentFragment() {
       
  5433 			return doc.createDocumentFragment();
       
  5434 		}
       
  5435 
       
  5436 		function setStart(n, o) {
       
  5437 			_setEndPoint(TRUE, n, o);
       
  5438 		}
       
  5439 
       
  5440 		function setEnd(n, o) {
       
  5441 			_setEndPoint(FALSE, n, o);
       
  5442 		}
       
  5443 
       
  5444 		function setStartBefore(n) {
       
  5445 			setStart(n.parentNode, nodeIndex(n));
       
  5446 		}
       
  5447 
       
  5448 		function setStartAfter(n) {
       
  5449 			setStart(n.parentNode, nodeIndex(n) + 1);
       
  5450 		}
       
  5451 
       
  5452 		function setEndBefore(n) {
       
  5453 			setEnd(n.parentNode, nodeIndex(n));
       
  5454 		}
       
  5455 
       
  5456 		function setEndAfter(n) {
       
  5457 			setEnd(n.parentNode, nodeIndex(n) + 1);
       
  5458 		}
       
  5459 
       
  5460 		function collapse(ts) {
       
  5461 			if (ts) {
       
  5462 				self[END_CONTAINER] = self[START_CONTAINER];
       
  5463 				self[END_OFFSET] = self[START_OFFSET];
       
  5464 			} else {
       
  5465 				self[START_CONTAINER] = self[END_CONTAINER];
       
  5466 				self[START_OFFSET] = self[END_OFFSET];
       
  5467 			}
       
  5468 
       
  5469 			self.collapsed = TRUE;
       
  5470 		}
       
  5471 
       
  5472 		function selectNode(n) {
       
  5473 			setStartBefore(n);
       
  5474 			setEndAfter(n);
       
  5475 		}
       
  5476 
       
  5477 		function selectNodeContents(n) {
       
  5478 			setStart(n, 0);
       
  5479 			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
       
  5480 		}
       
  5481 
       
  5482 		function compareBoundaryPoints(h, r) {
       
  5483 			var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
       
  5484 			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
       
  5485 
       
  5486 			// Check START_TO_START
       
  5487 			if (h === 0) {
       
  5488 				return _compareBoundaryPoints(sc, so, rsc, rso);
       
  5489 			}
       
  5490 
       
  5491 			// Check START_TO_END
       
  5492 			if (h === 1) {
       
  5493 				return _compareBoundaryPoints(ec, eo, rsc, rso);
       
  5494 			}
       
  5495 
       
  5496 			// Check END_TO_END
       
  5497 			if (h === 2) {
       
  5498 				return _compareBoundaryPoints(ec, eo, rec, reo);
       
  5499 			}
       
  5500 
       
  5501 			// Check END_TO_START
       
  5502 			if (h === 3) {
       
  5503 				return _compareBoundaryPoints(sc, so, rec, reo);
       
  5504 			}
       
  5505 		}
       
  5506 
       
  5507 		function deleteContents() {
       
  5508 			_traverse(DELETE);
       
  5509 		}
       
  5510 
       
  5511 		function extractContents() {
       
  5512 			return _traverse(EXTRACT);
       
  5513 		}
       
  5514 
       
  5515 		function cloneContents() {
       
  5516 			return _traverse(CLONE);
       
  5517 		}
       
  5518 
       
  5519 		function insertNode(n) {
       
  5520 			var startContainer = this[START_CONTAINER],
       
  5521 				startOffset = this[START_OFFSET], nn, o;
       
  5522 
       
  5523 			// Node is TEXT_NODE or CDATA
       
  5524 			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
       
  5525 				if (!startOffset) {
       
  5526 					// At the start of text
       
  5527 					startContainer.parentNode.insertBefore(n, startContainer);
       
  5528 				} else if (startOffset >= startContainer.nodeValue.length) {
       
  5529 					// At the end of text
       
  5530 					dom.insertAfter(n, startContainer);
       
  5531 				} else {
       
  5532 					// Middle, need to split
       
  5533 					nn = startContainer.splitText(startOffset);
       
  5534 					startContainer.parentNode.insertBefore(n, nn);
       
  5535 				}
       
  5536 			} else {
       
  5537 				// Insert element node
       
  5538 				if (startContainer.childNodes.length > 0) {
       
  5539 					o = startContainer.childNodes[startOffset];
       
  5540 				}
       
  5541 
       
  5542 				if (o) {
       
  5543 					startContainer.insertBefore(n, o);
       
  5544 				} else {
       
  5545 					if (startContainer.nodeType == 3) {
       
  5546 						dom.insertAfter(n, startContainer);
       
  5547 					} else {
       
  5548 						startContainer.appendChild(n);
       
  5549 					}
       
  5550 				}
       
  5551 			}
       
  5552 		}
       
  5553 
       
  5554 		function surroundContents(n) {
       
  5555 			var f = self.extractContents();
       
  5556 
       
  5557 			self.insertNode(n);
       
  5558 			n.appendChild(f);
       
  5559 			self.selectNode(n);
       
  5560 		}
       
  5561 
       
  5562 		function cloneRange() {
       
  5563 			return extend(new Range(dom), {
       
  5564 				startContainer: self[START_CONTAINER],
       
  5565 				startOffset: self[START_OFFSET],
       
  5566 				endContainer: self[END_CONTAINER],
       
  5567 				endOffset: self[END_OFFSET],
       
  5568 				collapsed: self.collapsed,
       
  5569 				commonAncestorContainer: self.commonAncestorContainer
       
  5570 			});
       
  5571 		}
       
  5572 
       
  5573 		// Private methods
       
  5574 
       
  5575 		function _getSelectedNode(container, offset) {
       
  5576 			var child;
       
  5577 
       
  5578 			if (container.nodeType == 3 /* TEXT_NODE */) {
       
  5579 				return container;
       
  5580 			}
       
  5581 
       
  5582 			if (offset < 0) {
       
  5583 				return container;
       
  5584 			}
       
  5585 
       
  5586 			child = container.firstChild;
       
  5587 			while (child && offset > 0) {
       
  5588 				--offset;
       
  5589 				child = child.nextSibling;
       
  5590 			}
       
  5591 
       
  5592 			if (child) {
       
  5593 				return child;
       
  5594 			}
       
  5595 
       
  5596 			return container;
       
  5597 		}
       
  5598 
       
  5599 		function _isCollapsed() {
       
  5600 			return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
       
  5601 		}
       
  5602 
       
  5603 		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
       
  5604 			var c, offsetC, n, cmnRoot, childA, childB;
       
  5605 
       
  5606 			// In the first case the boundary-points have the same container. A is before B
       
  5607 			// if its offset is less than the offset of B, A is equal to B if its offset is
       
  5608 			// equal to the offset of B, and A is after B if its offset is greater than the
       
  5609 			// offset of B.
       
  5610 			if (containerA == containerB) {
       
  5611 				if (offsetA == offsetB) {
       
  5612 					return 0; // equal
       
  5613 				}
       
  5614 
       
  5615 				if (offsetA < offsetB) {
       
  5616 					return -1; // before
       
  5617 				}
       
  5618 
       
  5619 				return 1; // after
       
  5620 			}
       
  5621 
       
  5622 			// In the second case a child node C of the container of A is an ancestor
       
  5623 			// container of B. In this case, A is before B if the offset of A is less than or
       
  5624 			// equal to the index of the child node C and A is after B otherwise.
       
  5625 			c = containerB;
       
  5626 			while (c && c.parentNode != containerA) {
       
  5627 				c = c.parentNode;
       
  5628 			}
       
  5629 
       
  5630 			if (c) {
       
  5631 				offsetC = 0;
       
  5632 				n = containerA.firstChild;
       
  5633 
       
  5634 				while (n != c && offsetC < offsetA) {
       
  5635 					offsetC++;
       
  5636 					n = n.nextSibling;
       
  5637 				}
       
  5638 
       
  5639 				if (offsetA <= offsetC) {
       
  5640 					return -1; // before
       
  5641 				}
       
  5642 
       
  5643 				return 1; // after
       
  5644 			}
       
  5645 
       
  5646 			// In the third case a child node C of the container of B is an ancestor container
       
  5647 			// of A. In this case, A is before B if the index of the child node C is less than
       
  5648 			// the offset of B and A is after B otherwise.
       
  5649 			c = containerA;
       
  5650 			while (c && c.parentNode != containerB) {
       
  5651 				c = c.parentNode;
       
  5652 			}
       
  5653 
       
  5654 			if (c) {
       
  5655 				offsetC = 0;
       
  5656 				n = containerB.firstChild;
       
  5657 
       
  5658 				while (n != c && offsetC < offsetB) {
       
  5659 					offsetC++;
       
  5660 					n = n.nextSibling;
       
  5661 				}
       
  5662 
       
  5663 				if (offsetC < offsetB) {
       
  5664 					return -1; // before
       
  5665 				}
       
  5666 
       
  5667 				return 1; // after
       
  5668 			}
       
  5669 
       
  5670 			// In the fourth case, none of three other cases hold: the containers of A and B
       
  5671 			// are siblings or descendants of sibling nodes. In this case, A is before B if
       
  5672 			// the container of A is before the container of B in a pre-order traversal of the
       
  5673 			// Ranges' context tree and A is after B otherwise.
       
  5674 			cmnRoot = dom.findCommonAncestor(containerA, containerB);
       
  5675 			childA = containerA;
       
  5676 
       
  5677 			while (childA && childA.parentNode != cmnRoot) {
       
  5678 				childA = childA.parentNode;
       
  5679 			}
       
  5680 
       
  5681 			if (!childA) {
       
  5682 				childA = cmnRoot;
       
  5683 			}
       
  5684 
       
  5685 			childB = containerB;
       
  5686 			while (childB && childB.parentNode != cmnRoot) {
       
  5687 				childB = childB.parentNode;
       
  5688 			}
       
  5689 
       
  5690 			if (!childB) {
       
  5691 				childB = cmnRoot;
       
  5692 			}
       
  5693 
       
  5694 			if (childA == childB) {
       
  5695 				return 0; // equal
       
  5696 			}
       
  5697 
       
  5698 			n = cmnRoot.firstChild;
       
  5699 			while (n) {
       
  5700 				if (n == childA) {
       
  5701 					return -1; // before
       
  5702 				}
       
  5703 
       
  5704 				if (n == childB) {
       
  5705 					return 1; // after
       
  5706 				}
       
  5707 
       
  5708 				n = n.nextSibling;
       
  5709 			}
       
  5710 		}
       
  5711 
       
  5712 		function _setEndPoint(st, n, o) {
       
  5713 			var ec, sc;
       
  5714 
       
  5715 			if (st) {
       
  5716 				self[START_CONTAINER] = n;
       
  5717 				self[START_OFFSET] = o;
       
  5718 			} else {
       
  5719 				self[END_CONTAINER] = n;
       
  5720 				self[END_OFFSET] = o;
       
  5721 			}
       
  5722 
       
  5723 			// If one boundary-point of a Range is set to have a root container
       
  5724 			// other than the current one for the Range, the Range is collapsed to
       
  5725 			// the new position. This enforces the restriction that both boundary-
       
  5726 			// points of a Range must have the same root container.
       
  5727 			ec = self[END_CONTAINER];
       
  5728 			while (ec.parentNode) {
       
  5729 				ec = ec.parentNode;
       
  5730 			}
       
  5731 
       
  5732 			sc = self[START_CONTAINER];
       
  5733 			while (sc.parentNode) {
       
  5734 				sc = sc.parentNode;
       
  5735 			}
       
  5736 
       
  5737 			if (sc == ec) {
       
  5738 				// The start position of a Range is guaranteed to never be after the
       
  5739 				// end position. To enforce this restriction, if the start is set to
       
  5740 				// be at a position after the end, the Range is collapsed to that
       
  5741 				// position.
       
  5742 				if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
       
  5743 					self.collapse(st);
       
  5744 				}
       
  5745 			} else {
       
  5746 				self.collapse(st);
       
  5747 			}
       
  5748 
       
  5749 			self.collapsed = _isCollapsed();
       
  5750 			self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
       
  5751 		}
       
  5752 
       
  5753 		function _traverse(how) {
       
  5754 			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
       
  5755 
       
  5756 			if (self[START_CONTAINER] == self[END_CONTAINER]) {
       
  5757 				return _traverseSameContainer(how);
       
  5758 			}
       
  5759 
       
  5760 			for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
       
  5761 				if (p == self[START_CONTAINER]) {
       
  5762 					return _traverseCommonStartContainer(c, how);
       
  5763 				}
       
  5764 
       
  5765 				++endContainerDepth;
       
  5766 			}
       
  5767 
       
  5768 			for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
       
  5769 				if (p == self[END_CONTAINER]) {
       
  5770 					return _traverseCommonEndContainer(c, how);
       
  5771 				}
       
  5772 
       
  5773 				++startContainerDepth;
       
  5774 			}
       
  5775 
       
  5776 			depthDiff = startContainerDepth - endContainerDepth;
       
  5777 
       
  5778 			startNode = self[START_CONTAINER];
       
  5779 			while (depthDiff > 0) {
       
  5780 				startNode = startNode.parentNode;
       
  5781 				depthDiff--;
       
  5782 			}
       
  5783 
       
  5784 			endNode = self[END_CONTAINER];
       
  5785 			while (depthDiff < 0) {
       
  5786 				endNode = endNode.parentNode;
       
  5787 				depthDiff++;
       
  5788 			}
       
  5789 
       
  5790 			// ascend the ancestor hierarchy until we have a common parent.
       
  5791 			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
       
  5792 				startNode = sp;
       
  5793 				endNode = ep;
       
  5794 			}
       
  5795 
       
  5796 			return _traverseCommonAncestors(startNode, endNode, how);
       
  5797 		}
       
  5798 
       
  5799 		function _traverseSameContainer(how) {
       
  5800 			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
       
  5801 
       
  5802 			if (how != DELETE) {
       
  5803 				frag = createDocumentFragment();
       
  5804 			}
       
  5805 
       
  5806 			// If selection is empty, just return the fragment
       
  5807 			if (self[START_OFFSET] == self[END_OFFSET]) {
       
  5808 				return frag;
       
  5809 			}
       
  5810 
       
  5811 			// Text node needs special case handling
       
  5812 			if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
       
  5813 				// get the substring
       
  5814 				s = self[START_CONTAINER].nodeValue;
       
  5815 				sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
       
  5816 
       
  5817 				// set the original text node to its new value
       
  5818 				if (how != CLONE) {
       
  5819 					n = self[START_CONTAINER];
       
  5820 					start = self[START_OFFSET];
       
  5821 					len = self[END_OFFSET] - self[START_OFFSET];
       
  5822 
       
  5823 					if (start === 0 && len >= n.nodeValue.length - 1) {
       
  5824 						n.parentNode.removeChild(n);
       
  5825 					} else {
       
  5826 						n.deleteData(start, len);
       
  5827 					}
       
  5828 
       
  5829 					// Nothing is partially selected, so collapse to start point
       
  5830 					self.collapse(TRUE);
       
  5831 				}
       
  5832 
       
  5833 				if (how == DELETE) {
       
  5834 					return;
       
  5835 				}
       
  5836 
       
  5837 				if (sub.length > 0) {
       
  5838 					frag.appendChild(doc.createTextNode(sub));
       
  5839 				}
       
  5840 
       
  5841 				return frag;
       
  5842 			}
       
  5843 
       
  5844 			// Copy nodes between the start/end offsets.
       
  5845 			n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
       
  5846 			cnt = self[END_OFFSET] - self[START_OFFSET];
       
  5847 
       
  5848 			while (n && cnt > 0) {
       
  5849 				sibling = n.nextSibling;
       
  5850 				xferNode = _traverseFullySelected(n, how);
       
  5851 
       
  5852 				if (frag) {
       
  5853 					frag.appendChild(xferNode);
       
  5854 				}
       
  5855 
       
  5856 				--cnt;
       
  5857 				n = sibling;
       
  5858 			}
       
  5859 
       
  5860 			// Nothing is partially selected, so collapse to start point
       
  5861 			if (how != CLONE) {
       
  5862 				self.collapse(TRUE);
       
  5863 			}
       
  5864 
       
  5865 			return frag;
       
  5866 		}
       
  5867 
       
  5868 		function _traverseCommonStartContainer(endAncestor, how) {
       
  5869 			var frag, n, endIdx, cnt, sibling, xferNode;
       
  5870 
       
  5871 			if (how != DELETE) {
       
  5872 				frag = createDocumentFragment();
       
  5873 			}
       
  5874 
       
  5875 			n = _traverseRightBoundary(endAncestor, how);
       
  5876 
       
  5877 			if (frag) {
       
  5878 				frag.appendChild(n);
       
  5879 			}
       
  5880 
       
  5881 			endIdx = nodeIndex(endAncestor);
       
  5882 			cnt = endIdx - self[START_OFFSET];
       
  5883 
       
  5884 			if (cnt <= 0) {
       
  5885 				// Collapse to just before the endAncestor, which
       
  5886 				// is partially selected.
       
  5887 				if (how != CLONE) {
       
  5888 					self.setEndBefore(endAncestor);
       
  5889 					self.collapse(FALSE);
       
  5890 				}
       
  5891 
       
  5892 				return frag;
       
  5893 			}
       
  5894 
       
  5895 			n = endAncestor.previousSibling;
       
  5896 			while (cnt > 0) {
       
  5897 				sibling = n.previousSibling;
       
  5898 				xferNode = _traverseFullySelected(n, how);
       
  5899 
       
  5900 				if (frag) {
       
  5901 					frag.insertBefore(xferNode, frag.firstChild);
       
  5902 				}
       
  5903 
       
  5904 				--cnt;
       
  5905 				n = sibling;
       
  5906 			}
       
  5907 
       
  5908 			// Collapse to just before the endAncestor, which
       
  5909 			// is partially selected.
       
  5910 			if (how != CLONE) {
       
  5911 				self.setEndBefore(endAncestor);
       
  5912 				self.collapse(FALSE);
       
  5913 			}
       
  5914 
       
  5915 			return frag;
       
  5916 		}
       
  5917 
       
  5918 		function _traverseCommonEndContainer(startAncestor, how) {
       
  5919 			var frag, startIdx, n, cnt, sibling, xferNode;
       
  5920 
       
  5921 			if (how != DELETE) {
       
  5922 				frag = createDocumentFragment();
       
  5923 			}
       
  5924 
       
  5925 			n = _traverseLeftBoundary(startAncestor, how);
       
  5926 			if (frag) {
       
  5927 				frag.appendChild(n);
       
  5928 			}
       
  5929 
       
  5930 			startIdx = nodeIndex(startAncestor);
       
  5931 			++startIdx; // Because we already traversed it
       
  5932 
       
  5933 			cnt = self[END_OFFSET] - startIdx;
       
  5934 			n = startAncestor.nextSibling;
       
  5935 			while (n && cnt > 0) {
       
  5936 				sibling = n.nextSibling;
       
  5937 				xferNode = _traverseFullySelected(n, how);
       
  5938 
       
  5939 				if (frag) {
       
  5940 					frag.appendChild(xferNode);
       
  5941 				}
       
  5942 
       
  5943 				--cnt;
       
  5944 				n = sibling;
       
  5945 			}
       
  5946 
       
  5947 			if (how != CLONE) {
       
  5948 				self.setStartAfter(startAncestor);
       
  5949 				self.collapse(TRUE);
       
  5950 			}
       
  5951 
       
  5952 			return frag;
       
  5953 		}
       
  5954 
       
  5955 		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
       
  5956 			var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
       
  5957 
       
  5958 			if (how != DELETE) {
       
  5959 				frag = createDocumentFragment();
       
  5960 			}
       
  5961 
       
  5962 			n = _traverseLeftBoundary(startAncestor, how);
       
  5963 			if (frag) {
       
  5964 				frag.appendChild(n);
       
  5965 			}
       
  5966 
       
  5967 			startOffset = nodeIndex(startAncestor);
       
  5968 			endOffset = nodeIndex(endAncestor);
       
  5969 			++startOffset;
       
  5970 
       
  5971 			cnt = endOffset - startOffset;
       
  5972 			sibling = startAncestor.nextSibling;
       
  5973 
       
  5974 			while (cnt > 0) {
       
  5975 				nextSibling = sibling.nextSibling;
       
  5976 				n = _traverseFullySelected(sibling, how);
       
  5977 
       
  5978 				if (frag) {
       
  5979 					frag.appendChild(n);
       
  5980 				}
       
  5981 
       
  5982 				sibling = nextSibling;
       
  5983 				--cnt;
       
  5984 			}
       
  5985 
       
  5986 			n = _traverseRightBoundary(endAncestor, how);
       
  5987 
       
  5988 			if (frag) {
       
  5989 				frag.appendChild(n);
       
  5990 			}
       
  5991 
       
  5992 			if (how != CLONE) {
       
  5993 				self.setStartAfter(startAncestor);
       
  5994 				self.collapse(TRUE);
       
  5995 			}
       
  5996 
       
  5997 			return frag;
       
  5998 		}
       
  5999 
       
  6000 		function _traverseRightBoundary(root, how) {
       
  6001 			var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
       
  6002 			var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
       
  6003 
       
  6004 			if (next == root) {
       
  6005 				return _traverseNode(next, isFullySelected, FALSE, how);
       
  6006 			}
       
  6007 
       
  6008 			parent = next.parentNode;
       
  6009 			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
       
  6010 
       
  6011 			while (parent) {
       
  6012 				while (next) {
       
  6013 					prevSibling = next.previousSibling;
       
  6014 					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
       
  6015 
       
  6016 					if (how != DELETE) {
       
  6017 						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
       
  6018 					}
       
  6019 
       
  6020 					isFullySelected = TRUE;
       
  6021 					next = prevSibling;
       
  6022 				}
       
  6023 
       
  6024 				if (parent == root) {
       
  6025 					return clonedParent;
       
  6026 				}
       
  6027 
       
  6028 				next = parent.previousSibling;
       
  6029 				parent = parent.parentNode;
       
  6030 
       
  6031 				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
       
  6032 
       
  6033 				if (how != DELETE) {
       
  6034 					clonedGrandParent.appendChild(clonedParent);
       
  6035 				}
       
  6036 
       
  6037 				clonedParent = clonedGrandParent;
       
  6038 			}
       
  6039 		}
       
  6040 
       
  6041 		function _traverseLeftBoundary(root, how) {
       
  6042 			var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
       
  6043 			var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
       
  6044 
       
  6045 			if (next == root) {
       
  6046 				return _traverseNode(next, isFullySelected, TRUE, how);
       
  6047 			}
       
  6048 
       
  6049 			parent = next.parentNode;
       
  6050 			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
       
  6051 
       
  6052 			while (parent) {
       
  6053 				while (next) {
       
  6054 					nextSibling = next.nextSibling;
       
  6055 					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
       
  6056 
       
  6057 					if (how != DELETE) {
       
  6058 						clonedParent.appendChild(clonedChild);
       
  6059 					}
       
  6060 
       
  6061 					isFullySelected = TRUE;
       
  6062 					next = nextSibling;
       
  6063 				}
       
  6064 
       
  6065 				if (parent == root) {
       
  6066 					return clonedParent;
       
  6067 				}
       
  6068 
       
  6069 				next = parent.nextSibling;
       
  6070 				parent = parent.parentNode;
       
  6071 
       
  6072 				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
       
  6073 
       
  6074 				if (how != DELETE) {
       
  6075 					clonedGrandParent.appendChild(clonedParent);
       
  6076 				}
       
  6077 
       
  6078 				clonedParent = clonedGrandParent;
       
  6079 			}
       
  6080 		}
       
  6081 
       
  6082 		function _traverseNode(n, isFullySelected, isLeft, how) {
       
  6083 			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
       
  6084 
       
  6085 			if (isFullySelected) {
       
  6086 				return _traverseFullySelected(n, how);
       
  6087 			}
       
  6088 
       
  6089 			if (n.nodeType == 3 /* TEXT_NODE */) {
       
  6090 				txtValue = n.nodeValue;
       
  6091 
       
  6092 				if (isLeft) {
       
  6093 					offset = self[START_OFFSET];
       
  6094 					newNodeValue = txtValue.substring(offset);
       
  6095 					oldNodeValue = txtValue.substring(0, offset);
       
  6096 				} else {
       
  6097 					offset = self[END_OFFSET];
       
  6098 					newNodeValue = txtValue.substring(0, offset);
       
  6099 					oldNodeValue = txtValue.substring(offset);
       
  6100 				}
       
  6101 
       
  6102 				if (how != CLONE) {
       
  6103 					n.nodeValue = oldNodeValue;
       
  6104 				}
       
  6105 
       
  6106 				if (how == DELETE) {
       
  6107 					return;
       
  6108 				}
       
  6109 
       
  6110 				newNode = dom.clone(n, FALSE);
       
  6111 				newNode.nodeValue = newNodeValue;
       
  6112 
       
  6113 				return newNode;
       
  6114 			}
       
  6115 
       
  6116 			if (how == DELETE) {
       
  6117 				return;
       
  6118 			}
       
  6119 
       
  6120 			return dom.clone(n, FALSE);
       
  6121 		}
       
  6122 
       
  6123 		function _traverseFullySelected(n, how) {
       
  6124 			if (how != DELETE) {
       
  6125 				return how == CLONE ? dom.clone(n, TRUE) : n;
       
  6126 			}
       
  6127 
       
  6128 			n.parentNode.removeChild(n);
       
  6129 		}
       
  6130 
       
  6131 		function toStringIE() {
       
  6132 			return dom.create('body', null, cloneContents()).outerText;
       
  6133 		}
       
  6134 
       
  6135 		extend(self, {
       
  6136 			// Inital states
       
  6137 			startContainer: doc,
       
  6138 			startOffset: 0,
       
  6139 			endContainer: doc,
       
  6140 			endOffset: 0,
       
  6141 			collapsed: TRUE,
       
  6142 			commonAncestorContainer: doc,
       
  6143 
       
  6144 			// Range constants
       
  6145 			START_TO_START: 0,
       
  6146 			START_TO_END: 1,
       
  6147 			END_TO_END: 2,
       
  6148 			END_TO_START: 3,
       
  6149 
       
  6150 			// Public methods
       
  6151 			setStart: setStart,
       
  6152 			setEnd: setEnd,
       
  6153 			setStartBefore: setStartBefore,
       
  6154 			setStartAfter: setStartAfter,
       
  6155 			setEndBefore: setEndBefore,
       
  6156 			setEndAfter: setEndAfter,
       
  6157 			collapse: collapse,
       
  6158 			selectNode: selectNode,
       
  6159 			selectNodeContents: selectNodeContents,
       
  6160 			compareBoundaryPoints: compareBoundaryPoints,
       
  6161 			deleteContents: deleteContents,
       
  6162 			extractContents: extractContents,
       
  6163 			cloneContents: cloneContents,
       
  6164 			insertNode: insertNode,
       
  6165 			surroundContents: surroundContents,
       
  6166 			cloneRange: cloneRange,
       
  6167 			toStringIE: toStringIE
       
  6168 		});
       
  6169 
       
  6170 		return self;
       
  6171 	}
       
  6172 
       
  6173 	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
       
  6174 	Range.prototype.toString = function() {
       
  6175 		return this.toStringIE();
       
  6176 	};
       
  6177 
       
  6178 	return Range;
       
  6179 });
       
  6180 
       
  6181 // Included from: js/tinymce/classes/html/Entities.js
       
  6182 
       
  6183 /**
       
  6184  * Entities.js
       
  6185  *
       
  6186  * Copyright, Moxiecode Systems AB
       
  6187  * Released under LGPL License.
       
  6188  *
       
  6189  * License: http://www.tinymce.com/license
       
  6190  * Contributing: http://www.tinymce.com/contributing
       
  6191  */
       
  6192 
       
  6193 /*jshint bitwise:false */
       
  6194 /*eslint no-bitwise:0 */
       
  6195 
       
  6196 /**
       
  6197  * Entity encoder class.
       
  6198  *
       
  6199  * @class tinymce.html.Entities
       
  6200  * @static
       
  6201  * @version 3.4
       
  6202  */
       
  6203 define("tinymce/html/Entities", [
       
  6204 	"tinymce/util/Tools"
       
  6205 ], function(Tools) {
       
  6206 	var makeMap = Tools.makeMap;
       
  6207 
       
  6208 	var namedEntities, baseEntities, reverseEntities,
       
  6209 		attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
       
  6210 		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
       
  6211 		rawCharsRegExp = /[<>&\"\']/g,
       
  6212 		entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
       
  6213 		asciiMap = {
       
  6214 			128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
       
  6215 			135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
       
  6216 			142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
       
  6217 			150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
       
  6218 			156: "\u0153", 158: "\u017E", 159: "\u0178"
       
  6219 		};
       
  6220 
       
  6221 	// Raw entities
       
  6222 	baseEntities = {
       
  6223 		'\"': '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code
       
  6224 		"'": '&#39;',
       
  6225 		'<': '&lt;',
       
  6226 		'>': '&gt;',
       
  6227 		'&': '&amp;',
       
  6228 		'\u0060': '&#96;'
       
  6229 	};
       
  6230 
       
  6231 	// Reverse lookup table for raw entities
       
  6232 	reverseEntities = {
       
  6233 		'&lt;': '<',
       
  6234 		'&gt;': '>',
       
  6235 		'&amp;': '&',
       
  6236 		'&quot;': '"',
       
  6237 		'&apos;': "'"
       
  6238 	};
       
  6239 
       
  6240 	// Decodes text by using the browser
       
  6241 	function nativeDecode(text) {
       
  6242 		var elm;
       
  6243 
       
  6244 		elm = document.createElement("div");
       
  6245 		elm.innerHTML = text;
       
  6246 
       
  6247 		return elm.textContent || elm.innerText || text;
       
  6248 	}
       
  6249 
       
  6250 	// Build a two way lookup table for the entities
       
  6251 	function buildEntitiesLookup(items, radix) {
       
  6252 		var i, chr, entity, lookup = {};
       
  6253 
       
  6254 		if (items) {
       
  6255 			items = items.split(',');
       
  6256 			radix = radix || 10;
       
  6257 
       
  6258 			// Build entities lookup table
       
  6259 			for (i = 0; i < items.length; i += 2) {
       
  6260 				chr = String.fromCharCode(parseInt(items[i], radix));
       
  6261 
       
  6262 				// Only add non base entities
       
  6263 				if (!baseEntities[chr]) {
       
  6264 					entity = '&' + items[i + 1] + ';';
       
  6265 					lookup[chr] = entity;
       
  6266 					lookup[entity] = chr;
       
  6267 				}
       
  6268 			}
       
  6269 
       
  6270 			return lookup;
       
  6271 		}
       
  6272 	}
       
  6273 
       
  6274 	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
       
  6275 	namedEntities = buildEntitiesLookup(
       
  6276 		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
       
  6277 		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
       
  6278 		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
       
  6279 		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
       
  6280 		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
       
  6281 		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
       
  6282 		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
       
  6283 		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
       
  6284 		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
       
  6285 		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
       
  6286 		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
       
  6287 		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
       
  6288 		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
       
  6289 		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
       
  6290 		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
       
  6291 		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
       
  6292 		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
       
  6293 		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
       
  6294 		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
       
  6295 		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
       
  6296 		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
       
  6297 		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
       
  6298 		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
       
  6299 		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
       
  6300 		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
       
  6301 
       
  6302 	var Entities = {
       
  6303 		/**
       
  6304 		 * Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
       
  6305 		 *
       
  6306 		 * @method encodeRaw
       
  6307 		 * @param {String} text Text to encode.
       
  6308 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
       
  6309 		 * @return {String} Entity encoded text.
       
  6310 		 */
       
  6311 		encodeRaw: function(text, attr) {
       
  6312 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  6313 				return baseEntities[chr] || chr;
       
  6314 			});
       
  6315 		},
       
  6316 
       
  6317 		/**
       
  6318 		 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
       
  6319 		 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
       
  6320 		 * and is exposed as the DOMUtils.encode function.
       
  6321 		 *
       
  6322 		 * @method encodeAllRaw
       
  6323 		 * @param {String} text Text to encode.
       
  6324 		 * @return {String} Entity encoded text.
       
  6325 		 */
       
  6326 		encodeAllRaw: function(text) {
       
  6327 			return ('' + text).replace(rawCharsRegExp, function(chr) {
       
  6328 				return baseEntities[chr] || chr;
       
  6329 			});
       
  6330 		},
       
  6331 
       
  6332 		/**
       
  6333 		 * Encodes the specified string using numeric entities. The core entities will be
       
  6334 		 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
       
  6335 		 *
       
  6336 		 * @method encodeNumeric
       
  6337 		 * @param {String} text Text to encode.
       
  6338 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
       
  6339 		 * @return {String} Entity encoded text.
       
  6340 		 */
       
  6341 		encodeNumeric: function(text, attr) {
       
  6342 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  6343 				// Multi byte sequence convert it to a single entity
       
  6344 				if (chr.length > 1) {
       
  6345 					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
       
  6346 				}
       
  6347 
       
  6348 				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
       
  6349 			});
       
  6350 		},
       
  6351 
       
  6352 		/**
       
  6353 		 * Encodes the specified string using named entities. The core entities will be encoded
       
  6354 		 * as named ones but all non lower ascii characters will be encoded into named entities.
       
  6355 		 *
       
  6356 		 * @method encodeNamed
       
  6357 		 * @param {String} text Text to encode.
       
  6358 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
       
  6359 		 * @param {Object} entities Optional parameter with entities to use.
       
  6360 		 * @return {String} Entity encoded text.
       
  6361 		 */
       
  6362 		encodeNamed: function(text, attr, entities) {
       
  6363 			entities = entities || namedEntities;
       
  6364 
       
  6365 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  6366 				return baseEntities[chr] || entities[chr] || chr;
       
  6367 			});
       
  6368 		},
       
  6369 
       
  6370 		/**
       
  6371 		 * Returns an encode function based on the name(s) and it's optional entities.
       
  6372 		 *
       
  6373 		 * @method getEncodeFunc
       
  6374 		 * @param {String} name Comma separated list of encoders for example named,numeric.
       
  6375 		 * @param {String} entities Optional parameter with entities to use instead of the built in set.
       
  6376 		 * @return {function} Encode function to be used.
       
  6377 		 */
       
  6378 		getEncodeFunc: function(name, entities) {
       
  6379 			entities = buildEntitiesLookup(entities) || namedEntities;
       
  6380 
       
  6381 			function encodeNamedAndNumeric(text, attr) {
       
  6382 				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  6383 					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
       
  6384 				});
       
  6385 			}
       
  6386 
       
  6387 			function encodeCustomNamed(text, attr) {
       
  6388 				return Entities.encodeNamed(text, attr, entities);
       
  6389 			}
       
  6390 
       
  6391 			// Replace + with , to be compatible with previous TinyMCE versions
       
  6392 			name = makeMap(name.replace(/\+/g, ','));
       
  6393 
       
  6394 			// Named and numeric encoder
       
  6395 			if (name.named && name.numeric) {
       
  6396 				return encodeNamedAndNumeric;
       
  6397 			}
       
  6398 
       
  6399 			// Named encoder
       
  6400 			if (name.named) {
       
  6401 				// Custom names
       
  6402 				if (entities) {
       
  6403 					return encodeCustomNamed;
       
  6404 				}
       
  6405 
       
  6406 				return Entities.encodeNamed;
       
  6407 			}
       
  6408 
       
  6409 			// Numeric
       
  6410 			if (name.numeric) {
       
  6411 				return Entities.encodeNumeric;
       
  6412 			}
       
  6413 
       
  6414 			// Raw encoder
       
  6415 			return Entities.encodeRaw;
       
  6416 		},
       
  6417 
       
  6418 		/**
       
  6419 		 * Decodes the specified string, this will replace entities with raw UTF characters.
       
  6420 		 *
       
  6421 		 * @method decode
       
  6422 		 * @param {String} text Text to entity decode.
       
  6423 		 * @return {String} Entity decoded string.
       
  6424 		 */
       
  6425 		decode: function(text) {
       
  6426 			return text.replace(entityRegExp, function(all, numeric) {
       
  6427 				if (numeric) {
       
  6428 					if (numeric.charAt(0).toLowerCase() === 'x') {
       
  6429 						numeric = parseInt(numeric.substr(1), 16);
       
  6430 					} else {
       
  6431 						numeric = parseInt(numeric, 10);
       
  6432 					}
       
  6433 
       
  6434 					// Support upper UTF
       
  6435 					if (numeric > 0xFFFF) {
       
  6436 						numeric -= 0x10000;
       
  6437 
       
  6438 						return String.fromCharCode(0xD800 + (numeric >> 10), 0xDC00 + (numeric & 0x3FF));
       
  6439 					} else {
       
  6440 						return asciiMap[numeric] || String.fromCharCode(numeric);
       
  6441 					}
       
  6442 				}
       
  6443 
       
  6444 				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
       
  6445 			});
       
  6446 		}
       
  6447 	};
       
  6448 
       
  6449 	return Entities;
       
  6450 });
       
  6451 
       
  6452 // Included from: js/tinymce/classes/dom/StyleSheetLoader.js
       
  6453 
       
  6454 /**
       
  6455  * StyleSheetLoader.js
       
  6456  *
       
  6457  * Copyright, Moxiecode Systems AB
       
  6458  * Released under LGPL License.
       
  6459  *
       
  6460  * License: http://www.tinymce.com/license
       
  6461  * Contributing: http://www.tinymce.com/contributing
       
  6462  */
       
  6463 
       
  6464 /**
       
  6465  * This class handles loading of external stylesheets and fires events when these are loaded.
       
  6466  *
       
  6467  * @class tinymce.dom.StyleSheetLoader
       
  6468  * @private
       
  6469  */
       
  6470 define("tinymce/dom/StyleSheetLoader", [
       
  6471 	"tinymce/util/Tools"
       
  6472 ], function(Tools) {
       
  6473 	"use strict";
       
  6474 
       
  6475 	return function(document, settings) {
       
  6476 		var idCount = 0, loadedStates = {}, maxLoadTime;
       
  6477 
       
  6478 		settings = settings || {};
       
  6479 		maxLoadTime = settings.maxLoadTime || 5000;
       
  6480 
       
  6481 		function appendToHead(node) {
       
  6482 			document.getElementsByTagName('head')[0].appendChild(node);
       
  6483 		}
       
  6484 
       
  6485 		/**
       
  6486 		 * Loads the specified css style sheet file and call the loadedCallback once it's finished loading.
       
  6487 		 *
       
  6488 		 * @method load
       
  6489 		 * @param {String} url Url to be loaded.
       
  6490 		 * @param {Function} loadedCallback Callback to be executed when loaded.
       
  6491 		 * @param {Function} errorCallback Callback to be executed when failed loading.
       
  6492 		 */
       
  6493 		function load(url, loadedCallback, errorCallback) {
       
  6494 			var link, style, startTime, state;
       
  6495 
       
  6496 			function passed() {
       
  6497 				var callbacks = state.passed, i = callbacks.length;
       
  6498 
       
  6499 				while (i--) {
       
  6500 					callbacks[i]();
       
  6501 				}
       
  6502 
       
  6503 				state.status = 2;
       
  6504 				state.passed = [];
       
  6505 				state.failed = [];
       
  6506 			}
       
  6507 
       
  6508 			function failed() {
       
  6509 				var callbacks = state.failed, i = callbacks.length;
       
  6510 
       
  6511 				while (i--) {
       
  6512 					callbacks[i]();
       
  6513 				}
       
  6514 
       
  6515 				state.status = 3;
       
  6516 				state.passed = [];
       
  6517 				state.failed = [];
       
  6518 			}
       
  6519 
       
  6520 			// Sniffs for older WebKit versions that have the link.onload but a broken one
       
  6521 			function isOldWebKit() {
       
  6522 				var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/);
       
  6523 				return !!(webKitChunks && webKitChunks[1] < 536);
       
  6524 			}
       
  6525 
       
  6526 			// Calls the waitCallback until the test returns true or the timeout occurs
       
  6527 			function wait(testCallback, waitCallback) {
       
  6528 				if (!testCallback()) {
       
  6529 					// Wait for timeout
       
  6530 					if ((new Date().getTime()) - startTime < maxLoadTime) {
       
  6531 						window.setTimeout(waitCallback, 0);
       
  6532 					} else {
       
  6533 						failed();
       
  6534 					}
       
  6535 				}
       
  6536 			}
       
  6537 
       
  6538 			// Workaround for WebKit that doesn't properly support the onload event for link elements
       
  6539 			// Or WebKit that fires the onload event before the StyleSheet is added to the document
       
  6540 			function waitForWebKitLinkLoaded() {
       
  6541 				wait(function() {
       
  6542 					var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner;
       
  6543 
       
  6544 					while (i--) {
       
  6545 						styleSheet = styleSheets[i];
       
  6546 						owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement;
       
  6547 						if (owner && owner.id === link.id) {
       
  6548 							passed();
       
  6549 							return true;
       
  6550 						}
       
  6551 					}
       
  6552 				}, waitForWebKitLinkLoaded);
       
  6553 			}
       
  6554 
       
  6555 			// Workaround for older Geckos that doesn't have any onload event for StyleSheets
       
  6556 			function waitForGeckoLinkLoaded() {
       
  6557 				wait(function() {
       
  6558 					try {
       
  6559 						// Accessing the cssRules will throw an exception until the CSS file is loaded
       
  6560 						var cssRules = style.sheet.cssRules;
       
  6561 						passed();
       
  6562 						return !!cssRules;
       
  6563 					} catch (ex) {
       
  6564 						// Ignore
       
  6565 					}
       
  6566 				}, waitForGeckoLinkLoaded);
       
  6567 			}
       
  6568 
       
  6569 			url = Tools._addCacheSuffix(url);
       
  6570 
       
  6571 			if (!loadedStates[url]) {
       
  6572 				state = {
       
  6573 					passed: [],
       
  6574 					failed: []
       
  6575 				};
       
  6576 
       
  6577 				loadedStates[url] = state;
       
  6578 			} else {
       
  6579 				state = loadedStates[url];
       
  6580 			}
       
  6581 
       
  6582 			if (loadedCallback) {
       
  6583 				state.passed.push(loadedCallback);
       
  6584 			}
       
  6585 
       
  6586 			if (errorCallback) {
       
  6587 				state.failed.push(errorCallback);
       
  6588 			}
       
  6589 
       
  6590 			// Is loading wait for it to pass
       
  6591 			if (state.status == 1) {
       
  6592 				return;
       
  6593 			}
       
  6594 
       
  6595 			// Has finished loading and was success
       
  6596 			if (state.status == 2) {
       
  6597 				passed();
       
  6598 				return;
       
  6599 			}
       
  6600 
       
  6601 			// Has finished loading and was a failure
       
  6602 			if (state.status == 3) {
       
  6603 				failed();
       
  6604 				return;
       
  6605 			}
       
  6606 
       
  6607 			// Start loading
       
  6608 			state.status = 1;
       
  6609 			link = document.createElement('link');
       
  6610 			link.rel = 'stylesheet';
       
  6611 			link.type = 'text/css';
       
  6612 			link.id = 'u' + (idCount++);
       
  6613 			link.async = false;
       
  6614 			link.defer = false;
       
  6615 			startTime = new Date().getTime();
       
  6616 
       
  6617 			// Feature detect onload on link element and sniff older webkits since it has an broken onload event
       
  6618 			if ("onload" in link && !isOldWebKit()) {
       
  6619 				link.onload = waitForWebKitLinkLoaded;
       
  6620 				link.onerror = failed;
       
  6621 			} else {
       
  6622 				// Sniff for old Firefox that doesn't support the onload event on link elements
       
  6623 				// TODO: Remove this in the future when everyone uses modern browsers
       
  6624 				if (navigator.userAgent.indexOf("Firefox") > 0) {
       
  6625 					style = document.createElement('style');
       
  6626 					style.textContent = '@import "' + url + '"';
       
  6627 					waitForGeckoLinkLoaded();
       
  6628 					appendToHead(style);
       
  6629 					return;
       
  6630 				} else {
       
  6631 					// Use the id owner on older webkits
       
  6632 					waitForWebKitLinkLoaded();
       
  6633 				}
       
  6634 			}
       
  6635 
       
  6636 			appendToHead(link);
       
  6637 			link.href = url;
       
  6638 		}
       
  6639 
       
  6640 		this.load = load;
       
  6641 	};
       
  6642 });
       
  6643 
       
  6644 // Included from: js/tinymce/classes/dom/DOMUtils.js
       
  6645 
       
  6646 /**
       
  6647  * DOMUtils.js
       
  6648  *
       
  6649  * Copyright, Moxiecode Systems AB
       
  6650  * Released under LGPL License.
       
  6651  *
       
  6652  * License: http://www.tinymce.com/license
       
  6653  * Contributing: http://www.tinymce.com/contributing
       
  6654  */
       
  6655 
       
  6656 /**
       
  6657  * Utility class for various DOM manipulation and retrieval functions.
       
  6658  *
       
  6659  * @class tinymce.dom.DOMUtils
       
  6660  * @example
       
  6661  * // Add a class to an element by id in the page
       
  6662  * tinymce.DOM.addClass('someid', 'someclass');
       
  6663  *
       
  6664  * // Add a class to an element by id inside the editor
       
  6665  * tinymce.activeEditor.dom.addClass('someid', 'someclass');
       
  6666  */
       
  6667 define("tinymce/dom/DOMUtils", [
       
  6668 	"tinymce/dom/Sizzle",
       
  6669 	"tinymce/dom/DomQuery",
       
  6670 	"tinymce/html/Styles",
       
  6671 	"tinymce/dom/EventUtils",
       
  6672 	"tinymce/dom/TreeWalker",
       
  6673 	"tinymce/dom/Range",
       
  6674 	"tinymce/html/Entities",
       
  6675 	"tinymce/Env",
       
  6676 	"tinymce/util/Tools",
       
  6677 	"tinymce/dom/StyleSheetLoader"
       
  6678 ], function(Sizzle, $, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools, StyleSheetLoader) {
       
  6679 	// Shorten names
       
  6680 	var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim;
       
  6681 	var isIE = Env.ie;
       
  6682 	var simpleSelectorRe = /^([a-z0-9],?)+$/i;
       
  6683 	var whiteSpaceRegExp = /^[ \t\r\n]*$/;
       
  6684 
       
  6685 	function setupAttrHooks(domUtils, settings) {
       
  6686 		var attrHooks = {}, keepValues = settings.keep_values, keepUrlHook;
       
  6687 
       
  6688 		keepUrlHook = {
       
  6689 			set: function($elm, value, name) {
       
  6690 				if (settings.url_converter) {
       
  6691 					value = settings.url_converter.call(settings.url_converter_scope || domUtils, value, name, $elm[0]);
       
  6692 				}
       
  6693 
       
  6694 				$elm.attr('data-mce-' + name, value).attr(name, value);
       
  6695 			},
       
  6696 
       
  6697 			get: function($elm, name) {
       
  6698 				return $elm.attr('data-mce-' + name) || $elm.attr(name);
       
  6699 			}
       
  6700 		};
       
  6701 
       
  6702 		attrHooks = {
       
  6703 			style: {
       
  6704 				set: function($elm, value) {
       
  6705 					if (value !== null && typeof value === 'object') {
       
  6706 						$elm.css(value);
       
  6707 						return;
       
  6708 					}
       
  6709 
       
  6710 					if (keepValues) {
       
  6711 						$elm.attr('data-mce-style', value);
       
  6712 					}
       
  6713 
       
  6714 					$elm.attr('style', value);
       
  6715 				},
       
  6716 
       
  6717 				get: function($elm) {
       
  6718 					var value = $elm.attr('data-mce-style') || $elm.attr('style');
       
  6719 
       
  6720 					value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
       
  6721 
       
  6722 					return value;
       
  6723 				}
       
  6724 			}
       
  6725 		};
       
  6726 
       
  6727 		if (keepValues) {
       
  6728 			attrHooks.href = attrHooks.src = keepUrlHook;
       
  6729 		}
       
  6730 
       
  6731 		return attrHooks;
       
  6732 	}
       
  6733 
       
  6734 	/**
       
  6735 	 * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
       
  6736 	 *
       
  6737 	 * @constructor
       
  6738 	 * @method DOMUtils
       
  6739 	 * @param {Document} d Document reference to bind the utility class to.
       
  6740 	 * @param {settings} s Optional settings collection.
       
  6741 	 */
       
  6742 	function DOMUtils(doc, settings) {
       
  6743 		var self = this, blockElementsMap;
       
  6744 
       
  6745 		self.doc = doc;
       
  6746 		self.win = window;
       
  6747 		self.files = {};
       
  6748 		self.counter = 0;
       
  6749 		self.stdMode = !isIE || doc.documentMode >= 8;
       
  6750 		self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
       
  6751 		self.styleSheetLoader = new StyleSheetLoader(doc);
       
  6752 		self.boundEvents = [];
       
  6753 		self.settings = settings = settings || {};
       
  6754 		self.schema = settings.schema;
       
  6755 		self.styles = new Styles({
       
  6756 			url_converter: settings.url_converter,
       
  6757 			url_converter_scope: settings.url_converter_scope
       
  6758 		}, settings.schema);
       
  6759 
       
  6760 		self.fixDoc(doc);
       
  6761 		self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
       
  6762 		self.attrHooks = setupAttrHooks(self, settings);
       
  6763 		blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
       
  6764 		self.$ = $.overrideDefaults(function() {
       
  6765 			return {
       
  6766 				context: doc,
       
  6767 				element: self.getRoot()
       
  6768 			};
       
  6769 		});
       
  6770 
       
  6771 		/**
       
  6772 		 * Returns true/false if the specified element is a block element or not.
       
  6773 		 *
       
  6774 		 * @method isBlock
       
  6775 		 * @param {Node/String} node Element/Node to check.
       
  6776 		 * @return {Boolean} True/False state if the node is a block element or not.
       
  6777 		 */
       
  6778 		self.isBlock = function(node) {
       
  6779 			// Fix for #5446
       
  6780 			if (!node) {
       
  6781 				return false;
       
  6782 			}
       
  6783 
       
  6784 			// This function is called in module pattern style since it might be executed with the wrong this scope
       
  6785 			var type = node.nodeType;
       
  6786 
       
  6787 			// If it's a node then check the type and use the nodeName
       
  6788 			if (type) {
       
  6789 				return !!(type === 1 && blockElementsMap[node.nodeName]);
       
  6790 			}
       
  6791 
       
  6792 			return !!blockElementsMap[node];
       
  6793 		};
       
  6794 	}
       
  6795 
       
  6796 	DOMUtils.prototype = {
       
  6797 		$$: function(elm) {
       
  6798 			if (typeof elm == 'string') {
       
  6799 				elm = this.get(elm);
       
  6800 			}
       
  6801 
       
  6802 			return this.$(elm);
       
  6803 		},
       
  6804 
       
  6805 		root: null,
       
  6806 
       
  6807 		fixDoc: function(doc) {
       
  6808 			var settings = this.settings, name;
       
  6809 
       
  6810 			if (isIE && settings.schema) {
       
  6811 				// Add missing HTML 4/5 elements to IE
       
  6812 				('abbr article aside audio canvas ' +
       
  6813 				'details figcaption figure footer ' +
       
  6814 				'header hgroup mark menu meter nav ' +
       
  6815 				'output progress section summary ' +
       
  6816 				'time video').replace(/\w+/g, function(name) {
       
  6817 					doc.createElement(name);
       
  6818 				});
       
  6819 
       
  6820 				// Create all custom elements
       
  6821 				for (name in settings.schema.getCustomElements()) {
       
  6822 					doc.createElement(name);
       
  6823 				}
       
  6824 			}
       
  6825 		},
       
  6826 
       
  6827 		clone: function(node, deep) {
       
  6828 			var self = this, clone, doc;
       
  6829 
       
  6830 			// TODO: Add feature detection here in the future
       
  6831 			if (!isIE || node.nodeType !== 1 || deep) {
       
  6832 				return node.cloneNode(deep);
       
  6833 			}
       
  6834 
       
  6835 			doc = self.doc;
       
  6836 
       
  6837 			// Make a HTML5 safe shallow copy
       
  6838 			if (!deep) {
       
  6839 				clone = doc.createElement(node.nodeName);
       
  6840 
       
  6841 				// Copy attribs
       
  6842 				each(self.getAttribs(node), function(attr) {
       
  6843 					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
       
  6844 				});
       
  6845 
       
  6846 				return clone;
       
  6847 			}
       
  6848 
       
  6849 			return clone.firstChild;
       
  6850 		},
       
  6851 
       
  6852 		/**
       
  6853 		 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
       
  6854 		 * go above the point of this root node.
       
  6855 		 *
       
  6856 		 * @method getRoot
       
  6857 		 * @return {Element} Root element for the utility class.
       
  6858 		 */
       
  6859 		getRoot: function() {
       
  6860 			var self = this;
       
  6861 
       
  6862 			return self.settings.root_element || self.doc.body;
       
  6863 		},
       
  6864 
       
  6865 		/**
       
  6866 		 * Returns the viewport of the window.
       
  6867 		 *
       
  6868 		 * @method getViewPort
       
  6869 		 * @param {Window} win Optional window to get viewport of.
       
  6870 		 * @return {Object} Viewport object with fields x, y, w and h.
       
  6871 		 */
       
  6872 		getViewPort: function(win) {
       
  6873 			var doc, rootElm;
       
  6874 
       
  6875 			win = !win ? this.win : win;
       
  6876 			doc = win.document;
       
  6877 			rootElm = this.boxModel ? doc.documentElement : doc.body;
       
  6878 
       
  6879 			// Returns viewport size excluding scrollbars
       
  6880 			return {
       
  6881 				x: win.pageXOffset || rootElm.scrollLeft,
       
  6882 				y: win.pageYOffset || rootElm.scrollTop,
       
  6883 				w: win.innerWidth || rootElm.clientWidth,
       
  6884 				h: win.innerHeight || rootElm.clientHeight
       
  6885 			};
       
  6886 		},
       
  6887 
       
  6888 		/**
       
  6889 		 * Returns the rectangle for a specific element.
       
  6890 		 *
       
  6891 		 * @method getRect
       
  6892 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
       
  6893 		 * @return {object} Rectangle for specified element object with x, y, w, h fields.
       
  6894 		 */
       
  6895 		getRect: function(elm) {
       
  6896 			var self = this, pos, size;
       
  6897 
       
  6898 			elm = self.get(elm);
       
  6899 			pos = self.getPos(elm);
       
  6900 			size = self.getSize(elm);
       
  6901 
       
  6902 			return {
       
  6903 				x: pos.x, y: pos.y,
       
  6904 				w: size.w, h: size.h
       
  6905 			};
       
  6906 		},
       
  6907 
       
  6908 		/**
       
  6909 		 * Returns the size dimensions of the specified element.
       
  6910 		 *
       
  6911 		 * @method getSize
       
  6912 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
       
  6913 		 * @return {object} Rectangle for specified element object with w, h fields.
       
  6914 		 */
       
  6915 		getSize: function(elm) {
       
  6916 			var self = this, w, h;
       
  6917 
       
  6918 			elm = self.get(elm);
       
  6919 			w = self.getStyle(elm, 'width');
       
  6920 			h = self.getStyle(elm, 'height');
       
  6921 
       
  6922 			// Non pixel value, then force offset/clientWidth
       
  6923 			if (w.indexOf('px') === -1) {
       
  6924 				w = 0;
       
  6925 			}
       
  6926 
       
  6927 			// Non pixel value, then force offset/clientWidth
       
  6928 			if (h.indexOf('px') === -1) {
       
  6929 				h = 0;
       
  6930 			}
       
  6931 
       
  6932 			return {
       
  6933 				w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
       
  6934 				h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
       
  6935 			};
       
  6936 		},
       
  6937 
       
  6938 		/**
       
  6939 		 * Returns a node by the specified selector function. This function will
       
  6940 		 * loop through all parent nodes and call the specified function for each node.
       
  6941 		 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
       
  6942 		 * and the node it found will be returned.
       
  6943 		 *
       
  6944 		 * @method getParent
       
  6945 		 * @param {Node/String} node DOM node to search parents on or ID string.
       
  6946 		 * @param {function} selector Selection function or CSS selector to execute on each node.
       
  6947 		 * @param {Node} root Optional root element, never go below this point.
       
  6948 		 * @return {Node} DOM Node or null if it wasn't found.
       
  6949 		 */
       
  6950 		getParent: function(node, selector, root) {
       
  6951 			return this.getParents(node, selector, root, false);
       
  6952 		},
       
  6953 
       
  6954 		/**
       
  6955 		 * Returns a node list of all parents matching the specified selector function or pattern.
       
  6956 		 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
       
  6957 		 *
       
  6958 		 * @method getParents
       
  6959 		 * @param {Node/String} node DOM node to search parents on or ID string.
       
  6960 		 * @param {function} selector Selection function to execute on each node or CSS pattern.
       
  6961 		 * @param {Node} root Optional root element, never go below this point.
       
  6962 		 * @return {Array} Array of nodes or null if it wasn't found.
       
  6963 		 */
       
  6964 		getParents: function(node, selector, root, collect) {
       
  6965 			var self = this, selectorVal, result = [];
       
  6966 
       
  6967 			node = self.get(node);
       
  6968 			collect = collect === undefined;
       
  6969 
       
  6970 			// Default root on inline mode
       
  6971 			root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
       
  6972 
       
  6973 			// Wrap node name as func
       
  6974 			if (is(selector, 'string')) {
       
  6975 				selectorVal = selector;
       
  6976 
       
  6977 				if (selector === '*') {
       
  6978 					selector = function(node) {
       
  6979 						return node.nodeType == 1;
       
  6980 					};
       
  6981 				} else {
       
  6982 					selector = function(node) {
       
  6983 						return self.is(node, selectorVal);
       
  6984 					};
       
  6985 				}
       
  6986 			}
       
  6987 
       
  6988 			while (node) {
       
  6989 				if (node == root || !node.nodeType || node.nodeType === 9) {
       
  6990 					break;
       
  6991 				}
       
  6992 
       
  6993 				if (!selector || selector(node)) {
       
  6994 					if (collect) {
       
  6995 						result.push(node);
       
  6996 					} else {
       
  6997 						return node;
       
  6998 					}
       
  6999 				}
       
  7000 
       
  7001 				node = node.parentNode;
       
  7002 			}
       
  7003 
       
  7004 			return collect ? result : null;
       
  7005 		},
       
  7006 
       
  7007 		/**
       
  7008 		 * Returns the specified element by ID or the input element if it isn't a string.
       
  7009 		 *
       
  7010 		 * @method get
       
  7011 		 * @param {String/Element} n Element id to look for or element to just pass though.
       
  7012 		 * @return {Element} Element matching the specified id or null if it wasn't found.
       
  7013 		 */
       
  7014 		get: function(elm) {
       
  7015 			var name;
       
  7016 
       
  7017 			if (elm && this.doc && typeof elm == 'string') {
       
  7018 				name = elm;
       
  7019 				elm = this.doc.getElementById(elm);
       
  7020 
       
  7021 				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
       
  7022 				if (elm && elm.id !== name) {
       
  7023 					return this.doc.getElementsByName(name)[1];
       
  7024 				}
       
  7025 			}
       
  7026 
       
  7027 			return elm;
       
  7028 		},
       
  7029 
       
  7030 		/**
       
  7031 		 * Returns the next node that matches selector or function
       
  7032 		 *
       
  7033 		 * @method getNext
       
  7034 		 * @param {Node} node Node to find siblings from.
       
  7035 		 * @param {String/function} selector Selector CSS expression or function.
       
  7036 		 * @return {Node} Next node item matching the selector or null if it wasn't found.
       
  7037 		 */
       
  7038 		getNext: function(node, selector) {
       
  7039 			return this._findSib(node, selector, 'nextSibling');
       
  7040 		},
       
  7041 
       
  7042 		/**
       
  7043 		 * Returns the previous node that matches selector or function
       
  7044 		 *
       
  7045 		 * @method getPrev
       
  7046 		 * @param {Node} node Node to find siblings from.
       
  7047 		 * @param {String/function} selector Selector CSS expression or function.
       
  7048 		 * @return {Node} Previous node item matching the selector or null if it wasn't found.
       
  7049 		 */
       
  7050 		getPrev: function(node, selector) {
       
  7051 			return this._findSib(node, selector, 'previousSibling');
       
  7052 		},
       
  7053 
       
  7054 		// #ifndef jquery
       
  7055 
       
  7056 		/**
       
  7057 		 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
       
  7058 		 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
       
  7059 		 * on more complex patterns.
       
  7060 		 *
       
  7061 		 * @method select
       
  7062 		 * @param {String} selector CSS level 3 pattern to select/find elements by.
       
  7063 		 * @param {Object} scope Optional root element/scope element to search in.
       
  7064 		 * @return {Array} Array with all matched elements.
       
  7065 		 * @example
       
  7066 		 * // Adds a class to all paragraphs in the currently active editor
       
  7067 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
  7068 		 *
       
  7069 		 * // Adds a class to all spans that have the test class in the currently active editor
       
  7070 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
       
  7071 		 */
       
  7072 		select: function(selector, scope) {
       
  7073 			var self = this;
       
  7074 
       
  7075 			/*eslint new-cap:0 */
       
  7076 			return Sizzle(selector, self.get(scope) || self.settings.root_element || self.doc, []);
       
  7077 		},
       
  7078 
       
  7079 		/**
       
  7080 		 * Returns true/false if the specified element matches the specified css pattern.
       
  7081 		 *
       
  7082 		 * @method is
       
  7083 		 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
       
  7084 		 * @param {String} selector CSS pattern to match the element against.
       
  7085 		 */
       
  7086 		is: function(elm, selector) {
       
  7087 			var i;
       
  7088 
       
  7089 			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
       
  7090 			if (elm.length === undefined) {
       
  7091 				// Simple all selector
       
  7092 				if (selector === '*') {
       
  7093 					return elm.nodeType == 1;
       
  7094 				}
       
  7095 
       
  7096 				// Simple selector just elements
       
  7097 				if (simpleSelectorRe.test(selector)) {
       
  7098 					selector = selector.toLowerCase().split(/,/);
       
  7099 					elm = elm.nodeName.toLowerCase();
       
  7100 
       
  7101 					for (i = selector.length - 1; i >= 0; i--) {
       
  7102 						if (selector[i] == elm) {
       
  7103 							return true;
       
  7104 						}
       
  7105 					}
       
  7106 
       
  7107 					return false;
       
  7108 				}
       
  7109 			}
       
  7110 
       
  7111 			// Is non element
       
  7112 			if (elm.nodeType && elm.nodeType != 1) {
       
  7113 				return false;
       
  7114 			}
       
  7115 
       
  7116 			var elms = elm.nodeType ? [elm] : elm;
       
  7117 
       
  7118 			/*eslint new-cap:0 */
       
  7119 			return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0;
       
  7120 		},
       
  7121 
       
  7122 		// #endif
       
  7123 
       
  7124 		/**
       
  7125 		 * Adds the specified element to another element or elements.
       
  7126 		 *
       
  7127 		 * @method add
       
  7128 		 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
       
  7129 		 * @param {String/Element} name Name of new element to add or existing element to add.
       
  7130 		 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
       
  7131 		 * @param {String} html Optional inner HTML contents to add for each element.
       
  7132 		 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
       
  7133 		 * were passed in.
       
  7134 		 * @example
       
  7135 		 * // Adds a new paragraph to the end of the active editor
       
  7136 		 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
       
  7137 		 */
       
  7138 		add: function(parentElm, name, attrs, html, create) {
       
  7139 			var self = this;
       
  7140 
       
  7141 			return this.run(parentElm, function(parentElm) {
       
  7142 				var newElm;
       
  7143 
       
  7144 				newElm = is(name, 'string') ? self.doc.createElement(name) : name;
       
  7145 				self.setAttribs(newElm, attrs);
       
  7146 
       
  7147 				if (html) {
       
  7148 					if (html.nodeType) {
       
  7149 						newElm.appendChild(html);
       
  7150 					} else {
       
  7151 						self.setHTML(newElm, html);
       
  7152 					}
       
  7153 				}
       
  7154 
       
  7155 				return !create ? parentElm.appendChild(newElm) : newElm;
       
  7156 			});
       
  7157 		},
       
  7158 
       
  7159 		/**
       
  7160 		 * Creates a new element.
       
  7161 		 *
       
  7162 		 * @method create
       
  7163 		 * @param {String} name Name of new element.
       
  7164 		 * @param {Object} attrs Optional object name/value collection with element attributes.
       
  7165 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
       
  7166 		 * @return {Element} HTML DOM node element that got created.
       
  7167 		 * @example
       
  7168 		 * // Adds an element where the caret/selection is in the active editor
       
  7169 		 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
       
  7170 		 * tinymce.activeEditor.selection.setNode(el);
       
  7171 		 */
       
  7172 		create: function(name, attrs, html) {
       
  7173 			return this.add(this.doc.createElement(name), name, attrs, html, 1);
       
  7174 		},
       
  7175 
       
  7176 		/**
       
  7177 		 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
       
  7178 		 *
       
  7179 		 * @method createHTML
       
  7180 		 * @param {String} name Name of new element.
       
  7181 		 * @param {Object} attrs Optional object name/value collection with element attributes.
       
  7182 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
       
  7183 		 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
       
  7184 		 * @example
       
  7185 		 * // Creates a html chunk and inserts it at the current selection/caret location
       
  7186 		 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
       
  7187 		 */
       
  7188 		createHTML: function(name, attrs, html) {
       
  7189 			var outHtml = '', key;
       
  7190 
       
  7191 			outHtml += '<' + name;
       
  7192 
       
  7193 			for (key in attrs) {
       
  7194 				if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') {
       
  7195 					outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
       
  7196 				}
       
  7197 			}
       
  7198 
       
  7199 			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
       
  7200 			if (typeof html != "undefined") {
       
  7201 				return outHtml + '>' + html + '</' + name + '>';
       
  7202 			}
       
  7203 
       
  7204 			return outHtml + ' />';
       
  7205 		},
       
  7206 
       
  7207 		/**
       
  7208 		 * Creates a document fragment out of the specified HTML string.
       
  7209 		 *
       
  7210 		 * @method createFragment
       
  7211 		 * @param {String} html Html string to create fragment from.
       
  7212 		 * @return {DocumentFragment} Document fragment node.
       
  7213 		 */
       
  7214 		createFragment: function(html) {
       
  7215 			var frag, node, doc = this.doc, container;
       
  7216 
       
  7217 			container = doc.createElement("div");
       
  7218 			frag = doc.createDocumentFragment();
       
  7219 
       
  7220 			if (html) {
       
  7221 				container.innerHTML = html;
       
  7222 			}
       
  7223 
       
  7224 			while ((node = container.firstChild)) {
       
  7225 				frag.appendChild(node);
       
  7226 			}
       
  7227 
       
  7228 			return frag;
       
  7229 		},
       
  7230 
       
  7231 		/**
       
  7232 		 * Removes/deletes the specified element(s) from the DOM.
       
  7233 		 *
       
  7234 		 * @method remove
       
  7235 		 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
       
  7236 		 * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be
       
  7237 		 * placed at the location of the removed element.
       
  7238 		 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
       
  7239 		 * were passed in.
       
  7240 		 * @example
       
  7241 		 * // Removes all paragraphs in the active editor
       
  7242 		 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
       
  7243 		 *
       
  7244 		 * // Removes an element by id in the document
       
  7245 		 * tinymce.DOM.remove('mydiv');
       
  7246 		 */
       
  7247 		remove: function(node, keepChildren) {
       
  7248 			node = this.$$(node);
       
  7249 
       
  7250 			if (keepChildren) {
       
  7251 				node.each(function() {
       
  7252 					var child;
       
  7253 
       
  7254 					while ((child = this.firstChild)) {
       
  7255 						if (child.nodeType == 3 && child.data.length === 0) {
       
  7256 							this.removeChild(child);
       
  7257 						} else {
       
  7258 							this.parentNode.insertBefore(child, this);
       
  7259 						}
       
  7260 					}
       
  7261 				}).remove();
       
  7262 			} else {
       
  7263 				node.remove();
       
  7264 			}
       
  7265 
       
  7266 			return node.length > 1 ? node.toArray() : node[0];
       
  7267 		},
       
  7268 
       
  7269 		/**
       
  7270 		 * Sets the CSS style value on a HTML element. The name can be a camelcase string
       
  7271 		 * or the CSS style name like background-color.
       
  7272 		 *
       
  7273 		 * @method setStyle
       
  7274 		 * @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
       
  7275 		 * @param {String} na Name of the style value to set.
       
  7276 		 * @param {String} v Value to set on the style.
       
  7277 		 * @example
       
  7278 		 * // Sets a style value on all paragraphs in the currently active editor
       
  7279 		 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
       
  7280 		 *
       
  7281 		 * // Sets a style value to an element by id in the current document
       
  7282 		 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
       
  7283 		 */
       
  7284 		setStyle: function(elm, name, value) {
       
  7285 			elm = this.$$(elm).css(name, value);
       
  7286 
       
  7287 			if (this.settings.update_styles) {
       
  7288 				elm.attr('data-mce-style', null);
       
  7289 			}
       
  7290 		},
       
  7291 
       
  7292 		/**
       
  7293 		 * Returns the current style or runtime/computed value of an element.
       
  7294 		 *
       
  7295 		 * @method getStyle
       
  7296 		 * @param {String/Element} elm HTML element or element id string to get style from.
       
  7297 		 * @param {String} name Style name to return.
       
  7298 		 * @param {Boolean} computed Computed style.
       
  7299 		 * @return {String} Current style or computed style value of an element.
       
  7300 		 */
       
  7301 		getStyle: function(elm, name, computed) {
       
  7302 			elm = this.$$(elm);
       
  7303 
       
  7304 			if (computed) {
       
  7305 				return elm.css(name);
       
  7306 			}
       
  7307 
       
  7308 			// Camelcase it, if needed
       
  7309 			name = name.replace(/-(\D)/g, function(a, b) {
       
  7310 				return b.toUpperCase();
       
  7311 			});
       
  7312 
       
  7313 			if (name == 'float') {
       
  7314 				name = isIE ? 'styleFloat' : 'cssFloat';
       
  7315 			}
       
  7316 
       
  7317 			return elm[0] && elm[0].style ? elm[0].style[name] : undefined;
       
  7318 		},
       
  7319 
       
  7320 		/**
       
  7321 		 * Sets multiple styles on the specified element(s).
       
  7322 		 *
       
  7323 		 * @method setStyles
       
  7324 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set styles on.
       
  7325 		 * @param {Object} o Name/Value collection of style items to add to the element(s).
       
  7326 		 * @example
       
  7327 		 * // Sets styles on all paragraphs in the currently active editor
       
  7328 		 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
       
  7329 		 *
       
  7330 		 * // Sets styles to an element by id in the current document
       
  7331 		 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
       
  7332 		 */
       
  7333 		setStyles: function(elm, styles) {
       
  7334 			elm = this.$$(elm).css(styles);
       
  7335 
       
  7336 			if (this.settings.update_styles) {
       
  7337 				elm.attr('data-mce-style', null);
       
  7338 			}
       
  7339 		},
       
  7340 
       
  7341 		/**
       
  7342 		 * Removes all attributes from an element or elements.
       
  7343 		 *
       
  7344 		 * @method removeAllAttribs
       
  7345 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
       
  7346 		 */
       
  7347 		removeAllAttribs: function(e) {
       
  7348 			return this.run(e, function(e) {
       
  7349 				var i, attrs = e.attributes;
       
  7350 				for (i = attrs.length - 1; i >= 0; i--) {
       
  7351 					e.removeAttributeNode(attrs.item(i));
       
  7352 				}
       
  7353 			});
       
  7354 		},
       
  7355 
       
  7356 		/**
       
  7357 		 * Sets the specified attribute of an element or elements.
       
  7358 		 *
       
  7359 		 * @method setAttrib
       
  7360 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set attribute on.
       
  7361 		 * @param {String} n Name of attribute to set.
       
  7362 		 * @param {String} v Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove the attribute instead.
       
  7363 		 * @example
       
  7364 		 * // Sets class attribute on all paragraphs in the active editor
       
  7365 		 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
       
  7366 		 *
       
  7367 		 * // Sets class attribute on a specific element in the current page
       
  7368 		 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
       
  7369 		 */
       
  7370 		setAttrib: function(elm, name, value) {
       
  7371 			var self = this, originalValue, hook, settings = self.settings;
       
  7372 
       
  7373 			if (value === '') {
       
  7374 				value = null;
       
  7375 			}
       
  7376 
       
  7377 			elm = self.$$(elm);
       
  7378 			originalValue = elm.attr(name);
       
  7379 
       
  7380 			if (!elm.length) {
       
  7381 				return;
       
  7382 			}
       
  7383 
       
  7384 			hook = self.attrHooks[name];
       
  7385 			if (hook && hook.set) {
       
  7386 				hook.set(elm, value, name);
       
  7387 			} else {
       
  7388 				elm.attr(name, value);
       
  7389 			}
       
  7390 
       
  7391 			if (originalValue != value && settings.onSetAttrib) {
       
  7392 				settings.onSetAttrib({
       
  7393 					attrElm: elm,
       
  7394 					attrName: name,
       
  7395 					attrValue: value
       
  7396 				});
       
  7397 			}
       
  7398 		},
       
  7399 
       
  7400 		/**
       
  7401 		 * Sets two or more specified attributes of an element or elements.
       
  7402 		 *
       
  7403 		 * @method setAttribs
       
  7404 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
       
  7405 		 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
       
  7406 		 * @example
       
  7407 		 * // Sets class and title attributes on all paragraphs in the active editor
       
  7408 		 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
       
  7409 		 *
       
  7410 		 * // Sets class and title attributes on a specific element in the current page
       
  7411 		 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
       
  7412 		 */
       
  7413 		setAttribs: function(elm, attrs) {
       
  7414 			var self = this;
       
  7415 
       
  7416 			self.$$(elm).each(function(i, node) {
       
  7417 				each(attrs, function(value, name) {
       
  7418 					self.setAttrib(node, name, value);
       
  7419 				});
       
  7420 			});
       
  7421 		},
       
  7422 
       
  7423 		/**
       
  7424 		 * Returns the specified attribute by name.
       
  7425 		 *
       
  7426 		 * @method getAttrib
       
  7427 		 * @param {String/Element} elm Element string id or DOM element to get attribute from.
       
  7428 		 * @param {String} name Name of attribute to get.
       
  7429 		 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
       
  7430 		 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
       
  7431 		 */
       
  7432 		getAttrib: function(elm, name, defaultVal) {
       
  7433 			var self = this, hook, value;
       
  7434 
       
  7435 			elm = self.$$(elm);
       
  7436 
       
  7437 			if (elm.length) {
       
  7438 				hook = self.attrHooks[name];
       
  7439 
       
  7440 				if (hook && hook.get) {
       
  7441 					value = hook.get(elm, name);
       
  7442 				} else {
       
  7443 					value = elm.attr(name);
       
  7444 				}
       
  7445 			}
       
  7446 
       
  7447 			if (typeof value == 'undefined') {
       
  7448 				value = defaultVal || '';
       
  7449 			}
       
  7450 
       
  7451 			return value;
       
  7452 		},
       
  7453 
       
  7454 		/**
       
  7455 		 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
       
  7456 		 *
       
  7457 		 * @method getPos
       
  7458 		 * @param {Element/String} elm HTML element or element id to get x, y position from.
       
  7459 		 * @param {Element} rootElm Optional root element to stop calculations at.
       
  7460 		 * @return {object} Absolute position of the specified element object with x, y fields.
       
  7461 		 */
       
  7462 		getPos: function(elm, rootElm) {
       
  7463 			var self = this, x = 0, y = 0, offsetParent, doc = self.doc, body = doc.body, pos;
       
  7464 
       
  7465 			elm = self.get(elm);
       
  7466 			rootElm = rootElm || body;
       
  7467 
       
  7468 			if (elm) {
       
  7469 				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
       
  7470 				// Fallback to offsetParent calculations if the body isn't static better since it stops at the body root
       
  7471 				if (rootElm === body && elm.getBoundingClientRect && $(body).css('position') === 'static') {
       
  7472 					pos = elm.getBoundingClientRect();
       
  7473 					rootElm = self.boxModel ? doc.documentElement : body;
       
  7474 
       
  7475 					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
       
  7476 					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
       
  7477 					x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft;
       
  7478 					y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop;
       
  7479 
       
  7480 					return {x: x, y: y};
       
  7481 				}
       
  7482 
       
  7483 				offsetParent = elm;
       
  7484 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
       
  7485 					x += offsetParent.offsetLeft || 0;
       
  7486 					y += offsetParent.offsetTop || 0;
       
  7487 					offsetParent = offsetParent.offsetParent;
       
  7488 				}
       
  7489 
       
  7490 				offsetParent = elm.parentNode;
       
  7491 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
       
  7492 					x -= offsetParent.scrollLeft || 0;
       
  7493 					y -= offsetParent.scrollTop || 0;
       
  7494 					offsetParent = offsetParent.parentNode;
       
  7495 				}
       
  7496 			}
       
  7497 
       
  7498 			return {x: x, y: y};
       
  7499 		},
       
  7500 
       
  7501 		/**
       
  7502 		 * Parses the specified style value into an object collection. This parser will also
       
  7503 		 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
       
  7504 		 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
       
  7505 		 *
       
  7506 		 * @method parseStyle
       
  7507 		 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
       
  7508 		 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
       
  7509 		 */
       
  7510 		parseStyle: function(cssText) {
       
  7511 			return this.styles.parse(cssText);
       
  7512 		},
       
  7513 
       
  7514 		/**
       
  7515 		 * Serializes the specified style object into a string.
       
  7516 		 *
       
  7517 		 * @method serializeStyle
       
  7518 		 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
       
  7519 		 * @param {String} name Optional element name.
       
  7520 		 * @return {String} String representation of the style object, for example: border: 1px solid red.
       
  7521 		 */
       
  7522 		serializeStyle: function(styles, name) {
       
  7523 			return this.styles.serialize(styles, name);
       
  7524 		},
       
  7525 
       
  7526 		/**
       
  7527 		 * Adds a style element at the top of the document with the specified cssText content.
       
  7528 		 *
       
  7529 		 * @method addStyle
       
  7530 		 * @param {String} cssText CSS Text style to add to top of head of document.
       
  7531 		 */
       
  7532 		addStyle: function(cssText) {
       
  7533 			var self = this, doc = self.doc, head, styleElm;
       
  7534 
       
  7535 			// Prevent inline from loading the same styles twice
       
  7536 			if (self !== DOMUtils.DOM && doc === document) {
       
  7537 				var addedStyles = DOMUtils.DOM.addedStyles;
       
  7538 
       
  7539 				addedStyles = addedStyles || [];
       
  7540 				if (addedStyles[cssText]) {
       
  7541 					return;
       
  7542 				}
       
  7543 
       
  7544 				addedStyles[cssText] = true;
       
  7545 				DOMUtils.DOM.addedStyles = addedStyles;
       
  7546 			}
       
  7547 
       
  7548 			// Create style element if needed
       
  7549 			styleElm = doc.getElementById('mceDefaultStyles');
       
  7550 			if (!styleElm) {
       
  7551 				styleElm = doc.createElement('style');
       
  7552 				styleElm.id = 'mceDefaultStyles';
       
  7553 				styleElm.type = 'text/css';
       
  7554 
       
  7555 				head = doc.getElementsByTagName('head')[0];
       
  7556 				if (head.firstChild) {
       
  7557 					head.insertBefore(styleElm, head.firstChild);
       
  7558 				} else {
       
  7559 					head.appendChild(styleElm);
       
  7560 				}
       
  7561 			}
       
  7562 
       
  7563 			// Append style data to old or new style element
       
  7564 			if (styleElm.styleSheet) {
       
  7565 				styleElm.styleSheet.cssText += cssText;
       
  7566 			} else {
       
  7567 				styleElm.appendChild(doc.createTextNode(cssText));
       
  7568 			}
       
  7569 		},
       
  7570 
       
  7571 		/**
       
  7572 		 * Imports/loads the specified CSS file into the document bound to the class.
       
  7573 		 *
       
  7574 		 * @method loadCSS
       
  7575 		 * @param {String} u URL to CSS file to load.
       
  7576 		 * @example
       
  7577 		 * // Loads a CSS file dynamically into the current document
       
  7578 		 * tinymce.DOM.loadCSS('somepath/some.css');
       
  7579 		 *
       
  7580 		 * // Loads a CSS file into the currently active editor instance
       
  7581 		 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
       
  7582 		 *
       
  7583 		 * // Loads a CSS file into an editor instance by id
       
  7584 		 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
       
  7585 		 *
       
  7586 		 * // Loads multiple CSS files into the current document
       
  7587 		 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
       
  7588 		 */
       
  7589 		loadCSS: function(url) {
       
  7590 			var self = this, doc = self.doc, head;
       
  7591 
       
  7592 			// Prevent inline from loading the same CSS file twice
       
  7593 			if (self !== DOMUtils.DOM && doc === document) {
       
  7594 				DOMUtils.DOM.loadCSS(url);
       
  7595 				return;
       
  7596 			}
       
  7597 
       
  7598 			if (!url) {
       
  7599 				url = '';
       
  7600 			}
       
  7601 
       
  7602 			head = doc.getElementsByTagName('head')[0];
       
  7603 
       
  7604 			each(url.split(','), function(url) {
       
  7605 				var link;
       
  7606 
       
  7607 				url = Tools._addCacheSuffix(url);
       
  7608 
       
  7609 				if (self.files[url]) {
       
  7610 					return;
       
  7611 				}
       
  7612 
       
  7613 				self.files[url] = true;
       
  7614 				link = self.create('link', {rel: 'stylesheet', href: url});
       
  7615 
       
  7616 				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
       
  7617 				// This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
       
  7618 				// It's ugly but it seems to work fine.
       
  7619 				if (isIE && doc.documentMode && doc.recalc) {
       
  7620 					link.onload = function() {
       
  7621 						if (doc.recalc) {
       
  7622 							doc.recalc();
       
  7623 						}
       
  7624 
       
  7625 						link.onload = null;
       
  7626 					};
       
  7627 				}
       
  7628 
       
  7629 				head.appendChild(link);
       
  7630 			});
       
  7631 		},
       
  7632 
       
  7633 		/**
       
  7634 		 * Adds a class to the specified element or elements.
       
  7635 		 *
       
  7636 		 * @method addClass
       
  7637 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
       
  7638 		 * @param {String} cls Class name to add to each element.
       
  7639 		 * @return {String/Array} String with new class value or array with new class values for all elements.
       
  7640 		 * @example
       
  7641 		 * // Adds a class to all paragraphs in the active editor
       
  7642 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
       
  7643 		 *
       
  7644 		 * // Adds a class to a specific element in the current page
       
  7645 		 * tinymce.DOM.addClass('mydiv', 'myclass');
       
  7646 		 */
       
  7647 		addClass: function(elm, cls) {
       
  7648 			this.$$(elm).addClass(cls);
       
  7649 		},
       
  7650 
       
  7651 		/**
       
  7652 		 * Removes a class from the specified element or elements.
       
  7653 		 *
       
  7654 		 * @method removeClass
       
  7655 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
       
  7656 		 * @param {String} cls Class name to remove from each element.
       
  7657 		 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
       
  7658 		 * were passed in.
       
  7659 		 * @example
       
  7660 		 * // Removes a class from all paragraphs in the active editor
       
  7661 		 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
       
  7662 		 *
       
  7663 		 * // Removes a class from a specific element in the current page
       
  7664 		 * tinymce.DOM.removeClass('mydiv', 'myclass');
       
  7665 		 */
       
  7666 		removeClass: function(elm, cls) {
       
  7667 			this.toggleClass(elm, cls, false);
       
  7668 		},
       
  7669 
       
  7670 		/**
       
  7671 		 * Returns true if the specified element has the specified class.
       
  7672 		 *
       
  7673 		 * @method hasClass
       
  7674 		 * @param {String/Element} n HTML element or element id string to check CSS class on.
       
  7675 		 * @param {String} c CSS class to check for.
       
  7676 		 * @return {Boolean} true/false if the specified element has the specified class.
       
  7677 		 */
       
  7678 		hasClass: function(elm, cls) {
       
  7679 			return this.$$(elm).hasClass(cls);
       
  7680 		},
       
  7681 
       
  7682 		/**
       
  7683 		 * Toggles the specified class on/off.
       
  7684 		 *
       
  7685 		 * @method toggleClass
       
  7686 		 * @param {Element} elm Element to toggle class on.
       
  7687 		 * @param {[type]} cls Class to toggle on/off.
       
  7688 		 * @param {[type]} state Optional state to set.
       
  7689 		 */
       
  7690 		toggleClass: function(elm, cls, state) {
       
  7691 			this.$$(elm).toggleClass(cls, state).each(function() {
       
  7692 				if (this.className === '') {
       
  7693 					$(this).attr('class', null);
       
  7694 				}
       
  7695 			});
       
  7696 		},
       
  7697 
       
  7698 		/**
       
  7699 		 * Shows the specified element(s) by ID by setting the "display" style.
       
  7700 		 *
       
  7701 		 * @method show
       
  7702 		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
       
  7703 		 */
       
  7704 		show: function(elm) {
       
  7705 			this.$$(elm).show();
       
  7706 		},
       
  7707 
       
  7708 		/**
       
  7709 		 * Hides the specified element(s) by ID by setting the "display" style.
       
  7710 		 *
       
  7711 		 * @method hide
       
  7712 		 * @param {String/Element/Array} e ID of DOM element or DOM element or array with elements or IDs to hide.
       
  7713 		 * @example
       
  7714 		 * // Hides an element by id in the document
       
  7715 		 * tinymce.DOM.hide('myid');
       
  7716 		 */
       
  7717 		hide: function(elm) {
       
  7718 			this.$$(elm).hide();
       
  7719 		},
       
  7720 
       
  7721 		/**
       
  7722 		 * Returns true/false if the element is hidden or not by checking the "display" style.
       
  7723 		 *
       
  7724 		 * @method isHidden
       
  7725 		 * @param {String/Element} e Id or element to check display state on.
       
  7726 		 * @return {Boolean} true/false if the element is hidden or not.
       
  7727 		 */
       
  7728 		isHidden: function(elm) {
       
  7729 			return this.$$(elm).css('display') == 'none';
       
  7730 		},
       
  7731 
       
  7732 		/**
       
  7733 		 * Returns a unique id. This can be useful when generating elements on the fly.
       
  7734 		 * This method will not check if the element already exists.
       
  7735 		 *
       
  7736 		 * @method uniqueId
       
  7737 		 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
       
  7738 		 * @return {String} Unique id.
       
  7739 		 */
       
  7740 		uniqueId: function(prefix) {
       
  7741 			return (!prefix ? 'mce_' : prefix) + (this.counter++);
       
  7742 		},
       
  7743 
       
  7744 		/**
       
  7745 		 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
       
  7746 		 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
       
  7747 		 *
       
  7748 		 * @method setHTML
       
  7749 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of.
       
  7750 		 * @param {String} h HTML content to set as inner HTML of the element.
       
  7751 		 * @example
       
  7752 		 * // Sets the inner HTML of all paragraphs in the active editor
       
  7753 		 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
       
  7754 		 *
       
  7755 		 * // Sets the inner HTML of an element by id in the document
       
  7756 		 * tinymce.DOM.setHTML('mydiv', 'some inner html');
       
  7757 		 */
       
  7758 		setHTML: function(elm, html) {
       
  7759 			elm = this.$$(elm);
       
  7760 
       
  7761 			if (isIE) {
       
  7762 				elm.each(function(i, target) {
       
  7763 					if (target.canHaveHTML === false) {
       
  7764 						return;
       
  7765 					}
       
  7766 
       
  7767 					// Remove all child nodes, IE keeps empty text nodes in DOM
       
  7768 					while (target.firstChild) {
       
  7769 						target.removeChild(target.firstChild);
       
  7770 					}
       
  7771 
       
  7772 					try {
       
  7773 						// IE will remove comments from the beginning
       
  7774 						// unless you padd the contents with something
       
  7775 						target.innerHTML = '<br>' + html;
       
  7776 						target.removeChild(target.firstChild);
       
  7777 					} catch (ex) {
       
  7778 						// IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p
       
  7779 						$('<div>').html('<br>' + html).contents().slice(1).appendTo(target);
       
  7780 					}
       
  7781 
       
  7782 					return html;
       
  7783 				});
       
  7784 			} else {
       
  7785 				elm.html(html);
       
  7786 			}
       
  7787 		},
       
  7788 
       
  7789 		/**
       
  7790 		 * Returns the outer HTML of an element.
       
  7791 		 *
       
  7792 		 * @method getOuterHTML
       
  7793 		 * @param {String/Element} elm Element ID or element object to get outer HTML from.
       
  7794 		 * @return {String} Outer HTML string.
       
  7795 		 * @example
       
  7796 		 * tinymce.DOM.getOuterHTML(editorElement);
       
  7797 		 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
       
  7798 		 */
       
  7799 		getOuterHTML: function(elm) {
       
  7800 			elm = this.get(elm);
       
  7801 
       
  7802 			// Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
       
  7803 			return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : $('<div>').append($(elm).clone()).html();
       
  7804 		},
       
  7805 
       
  7806 		/**
       
  7807 		 * Sets the specified outer HTML on an element or elements.
       
  7808 		 *
       
  7809 		 * @method setOuterHTML
       
  7810 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
       
  7811 		 * @param {Object} html HTML code to set as outer value for the element.
       
  7812 		 * @param {Document} doc Optional document scope to use in this process - defaults to the document of the DOM class.
       
  7813 		 * @example
       
  7814 		 * // Sets the outer HTML of all paragraphs in the active editor
       
  7815 		 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
       
  7816 		 *
       
  7817 		 * // Sets the outer HTML of an element by id in the document
       
  7818 		 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
       
  7819 		 */
       
  7820 		setOuterHTML: function(elm, html) {
       
  7821 			var self = this;
       
  7822 
       
  7823 			self.$$(elm).each(function() {
       
  7824 				try {
       
  7825 					// Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
       
  7826 					if ("outerHTML" in this) {
       
  7827 						this.outerHTML = html;
       
  7828 						return;
       
  7829 					}
       
  7830 				} catch (ex) {
       
  7831 					// Ignore
       
  7832 				}
       
  7833 
       
  7834 				// OuterHTML for IE it sometimes produces an "unknown runtime error"
       
  7835 				self.remove($(this).html(html), true);
       
  7836 			});
       
  7837 		},
       
  7838 
       
  7839 		/**
       
  7840 		 * Entity decodes a string. This method decodes any HTML entities, such as &aring;.
       
  7841 		 *
       
  7842 		 * @method decode
       
  7843 		 * @param {String} s String to decode entities on.
       
  7844 		 * @return {String} Entity decoded string.
       
  7845 		 */
       
  7846 		decode: Entities.decode,
       
  7847 
       
  7848 		/**
       
  7849 		 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
       
  7850 		 *
       
  7851 		 * @method encode
       
  7852 		 * @param {String} text String to encode with entities.
       
  7853 		 * @return {String} Entity encoded string.
       
  7854 		 */
       
  7855 		encode: Entities.encodeAllRaw,
       
  7856 
       
  7857 		/**
       
  7858 		 * Inserts an element after the reference element.
       
  7859 		 *
       
  7860 		 * @method insertAfter
       
  7861 		 * @param {Element} node Element to insert after the reference.
       
  7862 		 * @param {Element/String/Array} reference_node Reference element, element id or array of elements to insert after.
       
  7863 		 * @return {Element/Array} Element that got added or an array with elements.
       
  7864 		 */
       
  7865 		insertAfter: function(node, referenceNode) {
       
  7866 			referenceNode = this.get(referenceNode);
       
  7867 
       
  7868 			return this.run(node, function(node) {
       
  7869 				var parent, nextSibling;
       
  7870 
       
  7871 				parent = referenceNode.parentNode;
       
  7872 				nextSibling = referenceNode.nextSibling;
       
  7873 
       
  7874 				if (nextSibling) {
       
  7875 					parent.insertBefore(node, nextSibling);
       
  7876 				} else {
       
  7877 					parent.appendChild(node);
       
  7878 				}
       
  7879 
       
  7880 				return node;
       
  7881 			});
       
  7882 		},
       
  7883 
       
  7884 		/**
       
  7885 		 * Replaces the specified element or elements with the new element specified. The new element will
       
  7886 		 * be cloned if multiple input elements are passed in.
       
  7887 		 *
       
  7888 		 * @method replace
       
  7889 		 * @param {Element} newElm New element to replace old ones with.
       
  7890 		 * @param {Element/String/Array} oldELm Element DOM node, element id or array of elements or ids to replace.
       
  7891 		 * @param {Boolean} k Optional keep children state, if set to true child nodes from the old object will be added to new ones.
       
  7892 		 */
       
  7893 		replace: function(newElm, oldElm, keepChildren) {
       
  7894 			var self = this;
       
  7895 
       
  7896 			return self.run(oldElm, function(oldElm) {
       
  7897 				if (is(oldElm, 'array')) {
       
  7898 					newElm = newElm.cloneNode(true);
       
  7899 				}
       
  7900 
       
  7901 				if (keepChildren) {
       
  7902 					each(grep(oldElm.childNodes), function(node) {
       
  7903 						newElm.appendChild(node);
       
  7904 					});
       
  7905 				}
       
  7906 
       
  7907 				return oldElm.parentNode.replaceChild(newElm, oldElm);
       
  7908 			});
       
  7909 		},
       
  7910 
       
  7911 		/**
       
  7912 		 * Renames the specified element and keeps its attributes and children.
       
  7913 		 *
       
  7914 		 * @method rename
       
  7915 		 * @param {Element} elm Element to rename.
       
  7916 		 * @param {String} name Name of the new element.
       
  7917 		 * @return {Element} New element or the old element if it needed renaming.
       
  7918 		 */
       
  7919 		rename: function(elm, name) {
       
  7920 			var self = this, newElm;
       
  7921 
       
  7922 			if (elm.nodeName != name.toUpperCase()) {
       
  7923 				// Rename block element
       
  7924 				newElm = self.create(name);
       
  7925 
       
  7926 				// Copy attribs to new block
       
  7927 				each(self.getAttribs(elm), function(attrNode) {
       
  7928 					self.setAttrib(newElm, attrNode.nodeName, self.getAttrib(elm, attrNode.nodeName));
       
  7929 				});
       
  7930 
       
  7931 				// Replace block
       
  7932 				self.replace(newElm, elm, 1);
       
  7933 			}
       
  7934 
       
  7935 			return newElm || elm;
       
  7936 		},
       
  7937 
       
  7938 		/**
       
  7939 		 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
       
  7940 		 *
       
  7941 		 * @method findCommonAncestor
       
  7942 		 * @param {Element} a Element to find common ancestor of.
       
  7943 		 * @param {Element} b Element to find common ancestor of.
       
  7944 		 * @return {Element} Common ancestor element of the two input elements.
       
  7945 		 */
       
  7946 		findCommonAncestor: function(a, b) {
       
  7947 			var ps = a, pe;
       
  7948 
       
  7949 			while (ps) {
       
  7950 				pe = b;
       
  7951 
       
  7952 				while (pe && ps != pe) {
       
  7953 					pe = pe.parentNode;
       
  7954 				}
       
  7955 
       
  7956 				if (ps == pe) {
       
  7957 					break;
       
  7958 				}
       
  7959 
       
  7960 				ps = ps.parentNode;
       
  7961 			}
       
  7962 
       
  7963 			if (!ps && a.ownerDocument) {
       
  7964 				return a.ownerDocument.documentElement;
       
  7965 			}
       
  7966 
       
  7967 			return ps;
       
  7968 		},
       
  7969 
       
  7970 		/**
       
  7971 		 * Parses the specified RGB color value and returns a hex version of that color.
       
  7972 		 *
       
  7973 		 * @method toHex
       
  7974 		 * @param {String} rgbVal RGB string value like rgb(1,2,3)
       
  7975 		 * @return {String} Hex version of that RGB value like #FF00FF.
       
  7976 		 */
       
  7977 		toHex: function(rgbVal) {
       
  7978 			return this.styles.toHex(Tools.trim(rgbVal));
       
  7979 		},
       
  7980 
       
  7981 		/**
       
  7982 		 * Executes the specified function on the element by id or dom element node or array of elements/id.
       
  7983 		 *
       
  7984 		 * @method run
       
  7985 		 * @param {String/Element/Array} Element ID or DOM element object or array with ids or elements.
       
  7986 		 * @param {function} f Function to execute for each item.
       
  7987 		 * @param {Object} s Optional scope to execute the function in.
       
  7988 		 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
       
  7989 		 */
       
  7990 		run: function(elm, func, scope) {
       
  7991 			var self = this, result;
       
  7992 
       
  7993 			if (typeof elm === 'string') {
       
  7994 				elm = self.get(elm);
       
  7995 			}
       
  7996 
       
  7997 			if (!elm) {
       
  7998 				return false;
       
  7999 			}
       
  8000 
       
  8001 			scope = scope || this;
       
  8002 			if (!elm.nodeType && (elm.length || elm.length === 0)) {
       
  8003 				result = [];
       
  8004 
       
  8005 				each(elm, function(elm, i) {
       
  8006 					if (elm) {
       
  8007 						if (typeof elm == 'string') {
       
  8008 							elm = self.get(elm);
       
  8009 						}
       
  8010 
       
  8011 						result.push(func.call(scope, elm, i));
       
  8012 					}
       
  8013 				});
       
  8014 
       
  8015 				return result;
       
  8016 			}
       
  8017 
       
  8018 			return func.call(scope, elm);
       
  8019 		},
       
  8020 
       
  8021 		/**
       
  8022 		 * Returns a NodeList with attributes for the element.
       
  8023 		 *
       
  8024 		 * @method getAttribs
       
  8025 		 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
       
  8026 		 * @return {NodeList} NodeList with attributes.
       
  8027 		 */
       
  8028 		getAttribs: function(elm) {
       
  8029 			var attrs;
       
  8030 
       
  8031 			elm = this.get(elm);
       
  8032 
       
  8033 			if (!elm) {
       
  8034 				return [];
       
  8035 			}
       
  8036 
       
  8037 			if (isIE) {
       
  8038 				attrs = [];
       
  8039 
       
  8040 				// Object will throw exception in IE
       
  8041 				if (elm.nodeName == 'OBJECT') {
       
  8042 					return elm.attributes;
       
  8043 				}
       
  8044 
       
  8045 				// IE doesn't keep the selected attribute if you clone option elements
       
  8046 				if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
       
  8047 					attrs.push({specified: 1, nodeName: 'selected'});
       
  8048 				}
       
  8049 
       
  8050 				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
       
  8051 				var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
       
  8052 				elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
       
  8053 					attrs.push({specified: 1, nodeName: a});
       
  8054 				});
       
  8055 
       
  8056 				return attrs;
       
  8057 			}
       
  8058 
       
  8059 			return elm.attributes;
       
  8060 		},
       
  8061 
       
  8062 		/**
       
  8063 		 * Returns true/false if the specified node is to be considered empty or not.
       
  8064 		 *
       
  8065 		 * @example
       
  8066 		 * tinymce.DOM.isEmpty(node, {img: true});
       
  8067 		 * @method isEmpty
       
  8068 		 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
       
  8069 		 * @return {Boolean} true/false if the node is empty or not.
       
  8070 		 */
       
  8071 		isEmpty: function(node, elements) {
       
  8072 			var self = this, i, attributes, type, walker, name, brCount = 0;
       
  8073 
       
  8074 			node = node.firstChild;
       
  8075 			if (node) {
       
  8076 				walker = new TreeWalker(node, node.parentNode);
       
  8077 				elements = elements || (self.schema ? self.schema.getNonEmptyElements() : null);
       
  8078 
       
  8079 				do {
       
  8080 					type = node.nodeType;
       
  8081 
       
  8082 					if (type === 1) {
       
  8083 						// Ignore bogus elements
       
  8084 						if (node.getAttribute('data-mce-bogus')) {
       
  8085 							continue;
       
  8086 						}
       
  8087 
       
  8088 						// Keep empty elements like <img />
       
  8089 						name = node.nodeName.toLowerCase();
       
  8090 						if (elements && elements[name]) {
       
  8091 							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
       
  8092 							if (name === 'br') {
       
  8093 								brCount++;
       
  8094 								continue;
       
  8095 							}
       
  8096 
       
  8097 							return false;
       
  8098 						}
       
  8099 
       
  8100 						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
       
  8101 						attributes = self.getAttribs(node);
       
  8102 						i = attributes.length;
       
  8103 						while (i--) {
       
  8104 							name = attributes[i].nodeName;
       
  8105 							if (name === "name" || name === 'data-mce-bookmark') {
       
  8106 								return false;
       
  8107 							}
       
  8108 						}
       
  8109 					}
       
  8110 
       
  8111 					// Keep comment nodes
       
  8112 					if (type == 8) {
       
  8113 						return false;
       
  8114 					}
       
  8115 
       
  8116 					// Keep non whitespace text nodes
       
  8117 					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
       
  8118 						return false;
       
  8119 					}
       
  8120 				} while ((node = walker.next()));
       
  8121 			}
       
  8122 
       
  8123 			return brCount <= 1;
       
  8124 		},
       
  8125 
       
  8126 		/**
       
  8127 		 * Creates a new DOM Range object. This will use the native DOM Range API if it's
       
  8128 		 * available. If it's not, it will fall back to the custom TinyMCE implementation.
       
  8129 		 *
       
  8130 		 * @method createRng
       
  8131 		 * @return {DOMRange} DOM Range object.
       
  8132 		 * @example
       
  8133 		 * var rng = tinymce.DOM.createRng();
       
  8134 		 * alert(rng.startContainer + "," + rng.startOffset);
       
  8135 		 */
       
  8136 		createRng: function() {
       
  8137 			var doc = this.doc;
       
  8138 
       
  8139 			return doc.createRange ? doc.createRange() : new Range(this);
       
  8140 		},
       
  8141 
       
  8142 		/**
       
  8143 		 * Returns the index of the specified node within its parent.
       
  8144 		 *
       
  8145 		 * @method nodeIndex
       
  8146 		 * @param {Node} node Node to look for.
       
  8147 		 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
       
  8148 		 * @return {Number} Index of the specified node.
       
  8149 		 */
       
  8150 		nodeIndex: function(node, normalized) {
       
  8151 			var idx = 0, lastNodeType, nodeType;
       
  8152 
       
  8153 			if (node) {
       
  8154 				for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
       
  8155 					nodeType = node.nodeType;
       
  8156 
       
  8157 					// Normalize text nodes
       
  8158 					if (normalized && nodeType == 3) {
       
  8159 						if (nodeType == lastNodeType || !node.nodeValue.length) {
       
  8160 							continue;
       
  8161 						}
       
  8162 					}
       
  8163 					idx++;
       
  8164 					lastNodeType = nodeType;
       
  8165 				}
       
  8166 			}
       
  8167 
       
  8168 			return idx;
       
  8169 		},
       
  8170 
       
  8171 		/**
       
  8172 		 * Splits an element into two new elements and places the specified split
       
  8173 		 * element or elements between the new ones. For example splitting the paragraph at the bold element in
       
  8174 		 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
       
  8175 		 *
       
  8176 		 * @method split
       
  8177 		 * @param {Element} parentElm Parent element to split.
       
  8178 		 * @param {Element} splitElm Element to split at.
       
  8179 		 * @param {Element} replacementElm Optional replacement element to replace the split element with.
       
  8180 		 * @return {Element} Returns the split element or the replacement element if that is specified.
       
  8181 		 */
       
  8182 		split: function(parentElm, splitElm, replacementElm) {
       
  8183 			var self = this, r = self.createRng(), bef, aft, pa;
       
  8184 
       
  8185 			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
       
  8186 			// but we don't want that in our code since it serves no purpose for the end user
       
  8187 			// For example splitting this html at the bold element:
       
  8188 			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
       
  8189 			// would produce:
       
  8190 			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
       
  8191 			// this function will then trim off empty edges and produce:
       
  8192 			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
       
  8193 			function trimNode(node) {
       
  8194 				var i, children = node.childNodes, type = node.nodeType;
       
  8195 
       
  8196 				function surroundedBySpans(node) {
       
  8197 					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
       
  8198 					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
       
  8199 					return previousIsSpan && nextIsSpan;
       
  8200 				}
       
  8201 
       
  8202 				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
       
  8203 					return;
       
  8204 				}
       
  8205 
       
  8206 				for (i = children.length - 1; i >= 0; i--) {
       
  8207 					trimNode(children[i]);
       
  8208 				}
       
  8209 
       
  8210 				if (type != 9) {
       
  8211 					// Keep non whitespace text nodes
       
  8212 					if (type == 3 && node.nodeValue.length > 0) {
       
  8213 						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
       
  8214 						// Also keep text nodes with only spaces if surrounded by spans.
       
  8215 						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
       
  8216 						var trimmedLength = trim(node.nodeValue).length;
       
  8217 						if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
       
  8218 							return;
       
  8219 						}
       
  8220 					} else if (type == 1) {
       
  8221 						// If the only child is a bookmark then move it up
       
  8222 						children = node.childNodes;
       
  8223 
       
  8224 						// TODO fix this complex if
       
  8225 						if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
       
  8226 							children[0].getAttribute('data-mce-type') == 'bookmark') {
       
  8227 							node.parentNode.insertBefore(children[0], node);
       
  8228 						}
       
  8229 
       
  8230 						// Keep non empty elements or img, hr etc
       
  8231 						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
       
  8232 							return;
       
  8233 						}
       
  8234 					}
       
  8235 
       
  8236 					self.remove(node);
       
  8237 				}
       
  8238 
       
  8239 				return node;
       
  8240 			}
       
  8241 
       
  8242 			if (parentElm && splitElm) {
       
  8243 				// Get before chunk
       
  8244 				r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
       
  8245 				r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
       
  8246 				bef = r.extractContents();
       
  8247 
       
  8248 				// Get after chunk
       
  8249 				r = self.createRng();
       
  8250 				r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
       
  8251 				r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
       
  8252 				aft = r.extractContents();
       
  8253 
       
  8254 				// Insert before chunk
       
  8255 				pa = parentElm.parentNode;
       
  8256 				pa.insertBefore(trimNode(bef), parentElm);
       
  8257 
       
  8258 				// Insert middle chunk
       
  8259 				if (replacementElm) {
       
  8260 					pa.replaceChild(replacementElm, splitElm);
       
  8261 				} else {
       
  8262 					pa.insertBefore(splitElm, parentElm);
       
  8263 				}
       
  8264 
       
  8265 				// Insert after chunk
       
  8266 				pa.insertBefore(trimNode(aft), parentElm);
       
  8267 				self.remove(parentElm);
       
  8268 
       
  8269 				return replacementElm || splitElm;
       
  8270 			}
       
  8271 		},
       
  8272 
       
  8273 		/**
       
  8274 		 * Adds an event handler to the specified object.
       
  8275 		 *
       
  8276 		 * @method bind
       
  8277 		 * @param {Element/Document/Window/Array} target Target element to bind events to.
       
  8278 		 * handler to or an array of elements/ids/documents.
       
  8279 		 * @param {String} name Name of event handler to add, for example: click.
       
  8280 		 * @param {function} func Function to execute when the event occurs.
       
  8281 		 * @param {Object} scope Optional scope to execute the function in.
       
  8282 		 * @return {function} Function callback handler the same as the one passed in.
       
  8283 		 */
       
  8284 		bind: function(target, name, func, scope) {
       
  8285 			var self = this;
       
  8286 
       
  8287 			if (Tools.isArray(target)) {
       
  8288 				var i = target.length;
       
  8289 
       
  8290 				while (i--) {
       
  8291 					target[i] = self.bind(target[i], name, func, scope);
       
  8292 				}
       
  8293 
       
  8294 				return target;
       
  8295 			}
       
  8296 
       
  8297 			// Collect all window/document events bound by editor instance
       
  8298 			if (self.settings.collect && (target === self.doc || target === self.win)) {
       
  8299 				self.boundEvents.push([target, name, func, scope]);
       
  8300 			}
       
  8301 
       
  8302 			return self.events.bind(target, name, func, scope || self);
       
  8303 		},
       
  8304 
       
  8305 		/**
       
  8306 		 * Removes the specified event handler by name and function from an element or collection of elements.
       
  8307 		 *
       
  8308 		 * @method unbind
       
  8309 		 * @param {Element/Document/Window/Array} target Target element to unbind events on.
       
  8310 		 * @param {String} name Event handler name, for example: "click"
       
  8311 		 * @param {function} func Function to remove.
       
  8312 		 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
       
  8313 		 * were passed in.
       
  8314 		 */
       
  8315 		unbind: function(target, name, func) {
       
  8316 			var self = this, i;
       
  8317 
       
  8318 			if (Tools.isArray(target)) {
       
  8319 				i = target.length;
       
  8320 
       
  8321 				while (i--) {
       
  8322 					target[i] = self.unbind(target[i], name, func);
       
  8323 				}
       
  8324 
       
  8325 				return target;
       
  8326 			}
       
  8327 
       
  8328 			// Remove any bound events matching the input
       
  8329 			if (self.boundEvents && (target === self.doc || target === self.win)) {
       
  8330 				i = self.boundEvents.length;
       
  8331 
       
  8332 				while (i--) {
       
  8333 					var item = self.boundEvents[i];
       
  8334 
       
  8335 					if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
       
  8336 						this.events.unbind(item[0], item[1], item[2]);
       
  8337 					}
       
  8338 				}
       
  8339 			}
       
  8340 
       
  8341 			return this.events.unbind(target, name, func);
       
  8342 		},
       
  8343 
       
  8344 		/**
       
  8345 		 * Fires the specified event name with object on target.
       
  8346 		 *
       
  8347 		 * @method fire
       
  8348 		 * @param {Node/Document/Window} target Target element or object to fire event on.
       
  8349 		 * @param {String} name Name of the event to fire.
       
  8350 		 * @param {Object} evt Event object to send.
       
  8351 		 * @return {Event} Event object.
       
  8352 		 */
       
  8353 		fire: function(target, name, evt) {
       
  8354 			return this.events.fire(target, name, evt);
       
  8355 		},
       
  8356 
       
  8357 		// Returns the content editable state of a node
       
  8358 		getContentEditable: function(node) {
       
  8359 			var contentEditable;
       
  8360 
       
  8361 			// Check type
       
  8362 			if (!node || node.nodeType != 1) {
       
  8363 				return null;
       
  8364 			}
       
  8365 
       
  8366 			// Check for fake content editable
       
  8367 			contentEditable = node.getAttribute("data-mce-contenteditable");
       
  8368 			if (contentEditable && contentEditable !== "inherit") {
       
  8369 				return contentEditable;
       
  8370 			}
       
  8371 
       
  8372 			// Check for real content editable
       
  8373 			return node.contentEditable !== "inherit" ? node.contentEditable : null;
       
  8374 		},
       
  8375 
       
  8376 		getContentEditableParent: function(node) {
       
  8377 			var root = this.getRoot(), state = null;
       
  8378 
       
  8379 			for (; node && node !== root; node = node.parentNode) {
       
  8380 				state = this.getContentEditable(node);
       
  8381 
       
  8382 				if (state !== null) {
       
  8383 					break;
       
  8384 				}
       
  8385 			}
       
  8386 
       
  8387 			return state;
       
  8388 		},
       
  8389 
       
  8390 		/**
       
  8391 		 * Destroys all internal references to the DOM to solve IE leak issues.
       
  8392 		 *
       
  8393 		 * @method destroy
       
  8394 		 */
       
  8395 		destroy: function() {
       
  8396 			var self = this;
       
  8397 
       
  8398 			// Unbind all events bound to window/document by editor instance
       
  8399 			if (self.boundEvents) {
       
  8400 				var i = self.boundEvents.length;
       
  8401 
       
  8402 				while (i--) {
       
  8403 					var item = self.boundEvents[i];
       
  8404 					this.events.unbind(item[0], item[1], item[2]);
       
  8405 				}
       
  8406 
       
  8407 				self.boundEvents = null;
       
  8408 			}
       
  8409 
       
  8410 			// Restore sizzle document to window.document
       
  8411 			// Since the current document might be removed producing "Permission denied" on IE see #6325
       
  8412 			if (Sizzle.setDocument) {
       
  8413 				Sizzle.setDocument();
       
  8414 			}
       
  8415 
       
  8416 			self.win = self.doc = self.root = self.events = self.frag = null;
       
  8417 		},
       
  8418 
       
  8419 		isChildOf: function(node, parent) {
       
  8420 			while (node) {
       
  8421 				if (parent === node) {
       
  8422 					return true;
       
  8423 				}
       
  8424 
       
  8425 				node = node.parentNode;
       
  8426 			}
       
  8427 
       
  8428 			return false;
       
  8429 		},
       
  8430 
       
  8431 		// #ifdef debug
       
  8432 
       
  8433 		dumpRng: function(r) {
       
  8434 			return (
       
  8435 				'startContainer: ' + r.startContainer.nodeName +
       
  8436 				', startOffset: ' + r.startOffset +
       
  8437 				', endContainer: ' + r.endContainer.nodeName +
       
  8438 				', endOffset: ' + r.endOffset
       
  8439 			);
       
  8440 		},
       
  8441 
       
  8442 		// #endif
       
  8443 
       
  8444 		_findSib: function(node, selector, name) {
       
  8445 			var self = this, func = selector;
       
  8446 
       
  8447 			if (node) {
       
  8448 				// If expression make a function of it using is
       
  8449 				if (typeof func == 'string') {
       
  8450 					func = function(node) {
       
  8451 						return self.is(node, selector);
       
  8452 					};
       
  8453 				}
       
  8454 
       
  8455 				// Loop all siblings
       
  8456 				for (node = node[name]; node; node = node[name]) {
       
  8457 					if (func(node)) {
       
  8458 						return node;
       
  8459 					}
       
  8460 				}
       
  8461 			}
       
  8462 
       
  8463 			return null;
       
  8464 		}
       
  8465 	};
       
  8466 
       
  8467 	/**
       
  8468 	 * Instance of DOMUtils for the current document.
       
  8469 	 *
       
  8470 	 * @static
       
  8471 	 * @property DOM
       
  8472 	 * @type tinymce.dom.DOMUtils
       
  8473 	 * @example
       
  8474 	 * // Example of how to add a class to some element by id
       
  8475 	 * tinymce.DOM.addClass('someid', 'someclass');
       
  8476 	 */
       
  8477 	DOMUtils.DOM = new DOMUtils(document);
       
  8478 
       
  8479 	return DOMUtils;
       
  8480 });
       
  8481 
       
  8482 // Included from: js/tinymce/classes/dom/ScriptLoader.js
       
  8483 
       
  8484 /**
       
  8485  * ScriptLoader.js
       
  8486  *
       
  8487  * Copyright, Moxiecode Systems AB
       
  8488  * Released under LGPL License.
       
  8489  *
       
  8490  * License: http://www.tinymce.com/license
       
  8491  * Contributing: http://www.tinymce.com/contributing
       
  8492  */
       
  8493 
       
  8494 /*globals console*/
       
  8495 
       
  8496 /**
       
  8497  * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
       
  8498  * when various items gets loaded. This class is useful to load external JavaScript files.
       
  8499  *
       
  8500  * @class tinymce.dom.ScriptLoader
       
  8501  * @example
       
  8502  * // Load a script from a specific URL using the global script loader
       
  8503  * tinymce.ScriptLoader.load('somescript.js');
       
  8504  *
       
  8505  * // Load a script using a unique instance of the script loader
       
  8506  * var scriptLoader = new tinymce.dom.ScriptLoader();
       
  8507  *
       
  8508  * scriptLoader.load('somescript.js');
       
  8509  *
       
  8510  * // Load multiple scripts
       
  8511  * var scriptLoader = new tinymce.dom.ScriptLoader();
       
  8512  *
       
  8513  * scriptLoader.add('somescript1.js');
       
  8514  * scriptLoader.add('somescript2.js');
       
  8515  * scriptLoader.add('somescript3.js');
       
  8516  *
       
  8517  * scriptLoader.loadQueue(function() {
       
  8518  *    alert('All scripts are now loaded.');
       
  8519  * });
       
  8520  */
       
  8521 define("tinymce/dom/ScriptLoader", [
       
  8522 	"tinymce/dom/DOMUtils",
       
  8523 	"tinymce/util/Tools"
       
  8524 ], function(DOMUtils, Tools) {
       
  8525 	var DOM = DOMUtils.DOM;
       
  8526 	var each = Tools.each, grep = Tools.grep;
       
  8527 
       
  8528 	function ScriptLoader() {
       
  8529 		var QUEUED = 0,
       
  8530 			LOADING = 1,
       
  8531 			LOADED = 2,
       
  8532 			states = {},
       
  8533 			queue = [],
       
  8534 			scriptLoadedCallbacks = {},
       
  8535 			queueLoadedCallbacks = [],
       
  8536 			loading = 0,
       
  8537 			undef;
       
  8538 
       
  8539 		/**
       
  8540 		 * Loads a specific script directly without adding it to the load queue.
       
  8541 		 *
       
  8542 		 * @method load
       
  8543 		 * @param {String} url Absolute URL to script to add.
       
  8544 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
       
  8545 		 * @param {Object} scope Optional scope to execute callback in.
       
  8546 		 */
       
  8547 		function loadScript(url, callback) {
       
  8548 			var dom = DOM, elm, id;
       
  8549 
       
  8550 			// Execute callback when script is loaded
       
  8551 			function done() {
       
  8552 				dom.remove(id);
       
  8553 
       
  8554 				if (elm) {
       
  8555 					elm.onreadystatechange = elm.onload = elm = null;
       
  8556 				}
       
  8557 
       
  8558 				callback();
       
  8559 			}
       
  8560 
       
  8561 			function error() {
       
  8562 				/*eslint no-console:0 */
       
  8563 
       
  8564 				// Report the error so it's easier for people to spot loading errors
       
  8565 				if (typeof console !== "undefined" && console.log) {
       
  8566 					console.log("Failed to load: " + url);
       
  8567 				}
       
  8568 
       
  8569 				// We can't mark it as done if there is a load error since
       
  8570 				// A) We don't want to produce 404 errors on the server and
       
  8571 				// B) the onerror event won't fire on all browsers.
       
  8572 				// done();
       
  8573 			}
       
  8574 
       
  8575 			id = dom.uniqueId();
       
  8576 
       
  8577 			// Create new script element
       
  8578 			elm = document.createElement('script');
       
  8579 			elm.id = id;
       
  8580 			elm.type = 'text/javascript';
       
  8581 			elm.src = Tools._addCacheSuffix(url);
       
  8582 
       
  8583 			// Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
       
  8584 			if ("onreadystatechange" in elm) {
       
  8585 				elm.onreadystatechange = function() {
       
  8586 					if (/loaded|complete/.test(elm.readyState)) {
       
  8587 						done();
       
  8588 					}
       
  8589 				};
       
  8590 			} else {
       
  8591 				elm.onload = done;
       
  8592 			}
       
  8593 
       
  8594 			// Add onerror event will get fired on some browsers but not all of them
       
  8595 			elm.onerror = error;
       
  8596 
       
  8597 			// Add script to document
       
  8598 			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
       
  8599 		}
       
  8600 
       
  8601 		/**
       
  8602 		 * Returns true/false if a script has been loaded or not.
       
  8603 		 *
       
  8604 		 * @method isDone
       
  8605 		 * @param {String} url URL to check for.
       
  8606 		 * @return {Boolean} true/false if the URL is loaded.
       
  8607 		 */
       
  8608 		this.isDone = function(url) {
       
  8609 			return states[url] == LOADED;
       
  8610 		};
       
  8611 
       
  8612 		/**
       
  8613 		 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
       
  8614 		 * the script loader or to skip it from loading some script.
       
  8615 		 *
       
  8616 		 * @method markDone
       
  8617 		 * @param {string} u Absolute URL to the script to mark as loaded.
       
  8618 		 */
       
  8619 		this.markDone = function(url) {
       
  8620 			states[url] = LOADED;
       
  8621 		};
       
  8622 
       
  8623 		/**
       
  8624 		 * Adds a specific script to the load queue of the script loader.
       
  8625 		 *
       
  8626 		 * @method add
       
  8627 		 * @param {String} url Absolute URL to script to add.
       
  8628 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
       
  8629 		 * @param {Object} scope Optional scope to execute callback in.
       
  8630 		 */
       
  8631 		this.add = this.load = function(url, callback, scope) {
       
  8632 			var state = states[url];
       
  8633 
       
  8634 			// Add url to load queue
       
  8635 			if (state == undef) {
       
  8636 				queue.push(url);
       
  8637 				states[url] = QUEUED;
       
  8638 			}
       
  8639 
       
  8640 			if (callback) {
       
  8641 				// Store away callback for later execution
       
  8642 				if (!scriptLoadedCallbacks[url]) {
       
  8643 					scriptLoadedCallbacks[url] = [];
       
  8644 				}
       
  8645 
       
  8646 				scriptLoadedCallbacks[url].push({
       
  8647 					func: callback,
       
  8648 					scope: scope || this
       
  8649 				});
       
  8650 			}
       
  8651 		};
       
  8652 
       
  8653 		/**
       
  8654 		 * Starts the loading of the queue.
       
  8655 		 *
       
  8656 		 * @method loadQueue
       
  8657 		 * @param {function} callback Optional callback to execute when all queued items are loaded.
       
  8658 		 * @param {Object} scope Optional scope to execute the callback in.
       
  8659 		 */
       
  8660 		this.loadQueue = function(callback, scope) {
       
  8661 			this.loadScripts(queue, callback, scope);
       
  8662 		};
       
  8663 
       
  8664 		/**
       
  8665 		 * Loads the specified queue of files and executes the callback ones they are loaded.
       
  8666 		 * This method is generally not used outside this class but it might be useful in some scenarios.
       
  8667 		 *
       
  8668 		 * @method loadScripts
       
  8669 		 * @param {Array} scripts Array of queue items to load.
       
  8670 		 * @param {function} callback Optional callback to execute ones all items are loaded.
       
  8671 		 * @param {Object} scope Optional scope to execute callback in.
       
  8672 		 */
       
  8673 		this.loadScripts = function(scripts, callback, scope) {
       
  8674 			var loadScripts;
       
  8675 
       
  8676 			function execScriptLoadedCallbacks(url) {
       
  8677 				// Execute URL callback functions
       
  8678 				each(scriptLoadedCallbacks[url], function(callback) {
       
  8679 					callback.func.call(callback.scope);
       
  8680 				});
       
  8681 
       
  8682 				scriptLoadedCallbacks[url] = undef;
       
  8683 			}
       
  8684 
       
  8685 			queueLoadedCallbacks.push({
       
  8686 				func: callback,
       
  8687 				scope: scope || this
       
  8688 			});
       
  8689 
       
  8690 			loadScripts = function() {
       
  8691 				var loadingScripts = grep(scripts);
       
  8692 
       
  8693 				// Current scripts has been handled
       
  8694 				scripts.length = 0;
       
  8695 
       
  8696 				// Load scripts that needs to be loaded
       
  8697 				each(loadingScripts, function(url) {
       
  8698 					// Script is already loaded then execute script callbacks directly
       
  8699 					if (states[url] == LOADED) {
       
  8700 						execScriptLoadedCallbacks(url);
       
  8701 						return;
       
  8702 					}
       
  8703 
       
  8704 					// Is script not loading then start loading it
       
  8705 					if (states[url] != LOADING) {
       
  8706 						states[url] = LOADING;
       
  8707 						loading++;
       
  8708 
       
  8709 						loadScript(url, function() {
       
  8710 							states[url] = LOADED;
       
  8711 							loading--;
       
  8712 
       
  8713 							execScriptLoadedCallbacks(url);
       
  8714 
       
  8715 							// Load more scripts if they where added by the recently loaded script
       
  8716 							loadScripts();
       
  8717 						});
       
  8718 					}
       
  8719 				});
       
  8720 
       
  8721 				// No scripts are currently loading then execute all pending queue loaded callbacks
       
  8722 				if (!loading) {
       
  8723 					each(queueLoadedCallbacks, function(callback) {
       
  8724 						callback.func.call(callback.scope);
       
  8725 					});
       
  8726 
       
  8727 					queueLoadedCallbacks.length = 0;
       
  8728 				}
       
  8729 			};
       
  8730 
       
  8731 			loadScripts();
       
  8732 		};
       
  8733 	}
       
  8734 
       
  8735 	ScriptLoader.ScriptLoader = new ScriptLoader();
       
  8736 
       
  8737 	return ScriptLoader;
       
  8738 });
       
  8739 
       
  8740 // Included from: js/tinymce/classes/AddOnManager.js
       
  8741 
       
  8742 /**
       
  8743  * AddOnManager.js
       
  8744  *
       
  8745  * Copyright, Moxiecode Systems AB
       
  8746  * Released under LGPL License.
       
  8747  *
       
  8748  * License: http://www.tinymce.com/license
       
  8749  * Contributing: http://www.tinymce.com/contributing
       
  8750  */
       
  8751 
       
  8752 /**
       
  8753  * This class handles the loading of themes/plugins or other add-ons and their language packs.
       
  8754  *
       
  8755  * @class tinymce.AddOnManager
       
  8756  */
       
  8757 define("tinymce/AddOnManager", [
       
  8758 	"tinymce/dom/ScriptLoader",
       
  8759 	"tinymce/util/Tools"
       
  8760 ], function(ScriptLoader, Tools) {
       
  8761 	var each = Tools.each;
       
  8762 
       
  8763 	function AddOnManager() {
       
  8764 		var self = this;
       
  8765 
       
  8766 		self.items = [];
       
  8767 		self.urls = {};
       
  8768 		self.lookup = {};
       
  8769 	}
       
  8770 
       
  8771 	AddOnManager.prototype = {
       
  8772 		/**
       
  8773 		 * Returns the specified add on by the short name.
       
  8774 		 *
       
  8775 		 * @method get
       
  8776 		 * @param {String} name Add-on to look for.
       
  8777 		 * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
       
  8778 		 */
       
  8779 		get: function(name) {
       
  8780 			if (this.lookup[name]) {
       
  8781 				return this.lookup[name].instance;
       
  8782 			} else {
       
  8783 				return undefined;
       
  8784 			}
       
  8785 		},
       
  8786 
       
  8787 		dependencies: function(name) {
       
  8788 			var result;
       
  8789 
       
  8790 			if (this.lookup[name]) {
       
  8791 				result = this.lookup[name].dependencies;
       
  8792 			}
       
  8793 
       
  8794 			return result || [];
       
  8795 		},
       
  8796 
       
  8797 		/**
       
  8798 		 * Loads a language pack for the specified add-on.
       
  8799 		 *
       
  8800 		 * @method requireLangPack
       
  8801 		 * @param {String} name Short name of the add-on.
       
  8802 		 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
       
  8803 		 */
       
  8804 		requireLangPack: function(name, languages) {
       
  8805 			var language = AddOnManager.language;
       
  8806 
       
  8807 			if (language && AddOnManager.languageLoad !== false) {
       
  8808 				if (languages) {
       
  8809 					languages = ',' + languages + ',';
       
  8810 
       
  8811 					// Load short form sv.js or long form sv_SE.js
       
  8812 					if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) {
       
  8813 						language = language.substr(0, 2);
       
  8814 					} else if (languages.indexOf(',' + language + ',') == -1) {
       
  8815 						return;
       
  8816 					}
       
  8817 				}
       
  8818 
       
  8819 				ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js');
       
  8820 			}
       
  8821 		},
       
  8822 
       
  8823 		/**
       
  8824 		 * Adds a instance of the add-on by it's short name.
       
  8825 		 *
       
  8826 		 * @method add
       
  8827 		 * @param {String} id Short name/id for the add-on.
       
  8828 		 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
       
  8829 		 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
       
  8830 		 * @example
       
  8831 		 * // Create a simple plugin
       
  8832 		 * tinymce.create('tinymce.plugins.TestPlugin', {
       
  8833 		 *   TestPlugin: function(ed, url) {
       
  8834 		 *   ed.on('click', function(e) {
       
  8835 		 *      ed.windowManager.alert('Hello World!');
       
  8836 		 *   });
       
  8837 		 *   }
       
  8838 		 * });
       
  8839 		 *
       
  8840 		 * // Register plugin using the add method
       
  8841 		 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
       
  8842 		 *
       
  8843 		 * // Initialize TinyMCE
       
  8844 		 * tinymce.init({
       
  8845 		 *  ...
       
  8846 		 *  plugins: '-test' // Init the plugin but don't try to load it
       
  8847 		 * });
       
  8848 		 */
       
  8849 		add: function(id, addOn, dependencies) {
       
  8850 			this.items.push(addOn);
       
  8851 			this.lookup[id] = {instance: addOn, dependencies: dependencies};
       
  8852 
       
  8853 			return addOn;
       
  8854 		},
       
  8855 
       
  8856 		createUrl: function(baseUrl, dep) {
       
  8857 			if (typeof dep === "object") {
       
  8858 				return dep;
       
  8859 			} else {
       
  8860 				return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
       
  8861 			}
       
  8862 		},
       
  8863 
       
  8864 		/**
       
  8865 		 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
       
  8866 		 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
       
  8867 		 * components are put together into the plugin.js file and compressed correctly.
       
  8868 		 *
       
  8869 		 * @method addComponents
       
  8870 		 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
       
  8871 		 * @param {Array} scripts Array containing the names of the scripts to load.
       
  8872 		 */
       
  8873 		addComponents: function(pluginName, scripts) {
       
  8874 			var pluginUrl = this.urls[pluginName];
       
  8875 
       
  8876 			each(scripts, function(script) {
       
  8877 				ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
       
  8878 			});
       
  8879 		},
       
  8880 
       
  8881 		/**
       
  8882 		 * Loads an add-on from a specific url.
       
  8883 		 *
       
  8884 		 * @method load
       
  8885 		 * @param {String} name Short name of the add-on that gets loaded.
       
  8886 		 * @param {String} addOnUrl URL to the add-on that will get loaded.
       
  8887 		 * @param {function} callback Optional callback to execute ones the add-on is loaded.
       
  8888 		 * @param {Object} scope Optional scope to execute the callback in.
       
  8889 		 * @example
       
  8890 		 * // Loads a plugin from an external URL
       
  8891 		 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
       
  8892 		 *
       
  8893 		 * // Initialize TinyMCE
       
  8894 		 * tinymce.init({
       
  8895 		 *  ...
       
  8896 		 *  plugins: '-myplugin' // Don't try to load it again
       
  8897 		 * });
       
  8898 		 */
       
  8899 		load: function(name, addOnUrl, callback, scope) {
       
  8900 			var self = this, url = addOnUrl;
       
  8901 
       
  8902 			function loadDependencies() {
       
  8903 				var dependencies = self.dependencies(name);
       
  8904 
       
  8905 				each(dependencies, function(dep) {
       
  8906 					var newUrl = self.createUrl(addOnUrl, dep);
       
  8907 
       
  8908 					self.load(newUrl.resource, newUrl, undefined, undefined);
       
  8909 				});
       
  8910 
       
  8911 				if (callback) {
       
  8912 					if (scope) {
       
  8913 						callback.call(scope);
       
  8914 					} else {
       
  8915 						callback.call(ScriptLoader);
       
  8916 					}
       
  8917 				}
       
  8918 			}
       
  8919 
       
  8920 			if (self.urls[name]) {
       
  8921 				return;
       
  8922 			}
       
  8923 
       
  8924 			if (typeof addOnUrl === "object") {
       
  8925 				url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
       
  8926 			}
       
  8927 
       
  8928 			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
       
  8929 				url = AddOnManager.baseURL + '/' + url;
       
  8930 			}
       
  8931 
       
  8932 			self.urls[name] = url.substring(0, url.lastIndexOf('/'));
       
  8933 
       
  8934 			if (self.lookup[name]) {
       
  8935 				loadDependencies();
       
  8936 			} else {
       
  8937 				ScriptLoader.ScriptLoader.add(url, loadDependencies, scope);
       
  8938 			}
       
  8939 		}
       
  8940 	};
       
  8941 
       
  8942 	AddOnManager.PluginManager = new AddOnManager();
       
  8943 	AddOnManager.ThemeManager = new AddOnManager();
       
  8944 
       
  8945 	return AddOnManager;
       
  8946 });
       
  8947 
       
  8948 /**
       
  8949  * TinyMCE theme class.
       
  8950  *
       
  8951  * @class tinymce.Theme
       
  8952  */
       
  8953 
       
  8954 /**
       
  8955  * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
       
  8956  *
       
  8957  * @method renderUI
       
  8958  * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
       
  8959  * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
       
  8960  */
       
  8961 
       
  8962 /**
       
  8963  * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional.
       
  8964  *
       
  8965  * @class tinymce.Plugin
       
  8966  * @example
       
  8967  * tinymce.PluginManager.add('example', function(editor, url) {
       
  8968  *     // Add a button that opens a window
       
  8969  *     editor.addButton('example', {
       
  8970  *         text: 'My button',
       
  8971  *         icon: false,
       
  8972  *         onclick: function() {
       
  8973  *             // Open window
       
  8974  *             editor.windowManager.open({
       
  8975  *                 title: 'Example plugin',
       
  8976  *                 body: [
       
  8977  *                     {type: 'textbox', name: 'title', label: 'Title'}
       
  8978  *                 ],
       
  8979  *                 onsubmit: function(e) {
       
  8980  *                     // Insert content when the window form is submitted
       
  8981  *                     editor.insertContent('Title: ' + e.data.title);
       
  8982  *                 }
       
  8983  *             });
       
  8984  *         }
       
  8985  *     });
       
  8986  *
       
  8987  *     // Adds a menu item to the tools menu
       
  8988  *     editor.addMenuItem('example', {
       
  8989  *         text: 'Example plugin',
       
  8990  *         context: 'tools',
       
  8991  *         onclick: function() {
       
  8992  *             // Open window with a specific url
       
  8993  *             editor.windowManager.open({
       
  8994  *                 title: 'TinyMCE site',
       
  8995  *                 url: 'http://www.tinymce.com',
       
  8996  *                 width: 800,
       
  8997  *                 height: 600,
       
  8998  *                 buttons: [{
       
  8999  *                     text: 'Close',
       
  9000  *                     onclick: 'close'
       
  9001  *                 }]
       
  9002  *             });
       
  9003  *         }
       
  9004  *     });
       
  9005  * });
       
  9006  */
       
  9007 
       
  9008 // Included from: js/tinymce/classes/dom/RangeUtils.js
       
  9009 
       
  9010 /**
       
  9011  * RangeUtils.js
       
  9012  *
       
  9013  * Copyright, Moxiecode Systems AB
       
  9014  * Released under LGPL License.
       
  9015  *
       
  9016  * License: http://www.tinymce.com/license
       
  9017  * Contributing: http://www.tinymce.com/contributing
       
  9018  */
       
  9019 
       
  9020 /**
       
  9021  * This class contains a few utility methods for ranges.
       
  9022  *
       
  9023  * @class tinymce.dom.RangeUtils
       
  9024  */
       
  9025 define("tinymce/dom/RangeUtils", [
       
  9026 	"tinymce/util/Tools",
       
  9027 	"tinymce/dom/TreeWalker"
       
  9028 ], function(Tools, TreeWalker) {
       
  9029 	var each = Tools.each;
       
  9030 
       
  9031 	function getEndChild(container, index) {
       
  9032 		var childNodes = container.childNodes;
       
  9033 
       
  9034 		index--;
       
  9035 
       
  9036 		if (index > childNodes.length - 1) {
       
  9037 			index = childNodes.length - 1;
       
  9038 		} else if (index < 0) {
       
  9039 			index = 0;
       
  9040 		}
       
  9041 
       
  9042 		return childNodes[index] || container;
       
  9043 	}
       
  9044 
       
  9045 	function RangeUtils(dom) {
       
  9046 		/**
       
  9047 		 * Walks the specified range like object and executes the callback for each sibling collection it finds.
       
  9048 		 *
       
  9049 		 * @method walk
       
  9050 		 * @param {Object} rng Range like object.
       
  9051 		 * @param {function} callback Callback function to execute for each sibling collection.
       
  9052 		 */
       
  9053 		this.walk = function(rng, callback) {
       
  9054 			var startContainer = rng.startContainer,
       
  9055 				startOffset = rng.startOffset,
       
  9056 				endContainer = rng.endContainer,
       
  9057 				endOffset = rng.endOffset,
       
  9058 				ancestor, startPoint,
       
  9059 				endPoint, node, parent, siblings, nodes;
       
  9060 
       
  9061 			// Handle table cell selection the table plugin enables
       
  9062 			// you to fake select table cells and perform formatting actions on them
       
  9063 			nodes = dom.select('td.mce-item-selected,th.mce-item-selected');
       
  9064 			if (nodes.length > 0) {
       
  9065 				each(nodes, function(node) {
       
  9066 					callback([node]);
       
  9067 				});
       
  9068 
       
  9069 				return;
       
  9070 			}
       
  9071 
       
  9072 			/**
       
  9073 			 * Excludes start/end text node if they are out side the range
       
  9074 			 *
       
  9075 			 * @private
       
  9076 			 * @param {Array} nodes Nodes to exclude items from.
       
  9077 			 * @return {Array} Array with nodes excluding the start/end container if needed.
       
  9078 			 */
       
  9079 			function exclude(nodes) {
       
  9080 				var node;
       
  9081 
       
  9082 				// First node is excluded
       
  9083 				node = nodes[0];
       
  9084 				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
       
  9085 					nodes.splice(0, 1);
       
  9086 				}
       
  9087 
       
  9088 				// Last node is excluded
       
  9089 				node = nodes[nodes.length - 1];
       
  9090 				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
       
  9091 					nodes.splice(nodes.length - 1, 1);
       
  9092 				}
       
  9093 
       
  9094 				return nodes;
       
  9095 			}
       
  9096 
       
  9097 			/**
       
  9098 			 * Collects siblings
       
  9099 			 *
       
  9100 			 * @private
       
  9101 			 * @param {Node} node Node to collect siblings from.
       
  9102 			 * @param {String} name Name of the sibling to check for.
       
  9103 			 * @return {Array} Array of collected siblings.
       
  9104 			 */
       
  9105 			function collectSiblings(node, name, end_node) {
       
  9106 				var siblings = [];
       
  9107 
       
  9108 				for (; node && node != end_node; node = node[name]) {
       
  9109 					siblings.push(node);
       
  9110 				}
       
  9111 
       
  9112 				return siblings;
       
  9113 			}
       
  9114 
       
  9115 			/**
       
  9116 			 * Find an end point this is the node just before the common ancestor root.
       
  9117 			 *
       
  9118 			 * @private
       
  9119 			 * @param {Node} node Node to start at.
       
  9120 			 * @param {Node} root Root/ancestor element to stop just before.
       
  9121 			 * @return {Node} Node just before the root element.
       
  9122 			 */
       
  9123 			function findEndPoint(node, root) {
       
  9124 				do {
       
  9125 					if (node.parentNode == root) {
       
  9126 						return node;
       
  9127 					}
       
  9128 
       
  9129 					node = node.parentNode;
       
  9130 				} while (node);
       
  9131 			}
       
  9132 
       
  9133 			function walkBoundary(start_node, end_node, next) {
       
  9134 				var siblingName = next ? 'nextSibling' : 'previousSibling';
       
  9135 
       
  9136 				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
       
  9137 					parent = node.parentNode;
       
  9138 					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
       
  9139 
       
  9140 					if (siblings.length) {
       
  9141 						if (!next) {
       
  9142 							siblings.reverse();
       
  9143 						}
       
  9144 
       
  9145 						callback(exclude(siblings));
       
  9146 					}
       
  9147 				}
       
  9148 			}
       
  9149 
       
  9150 			// If index based start position then resolve it
       
  9151 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
       
  9152 				startContainer = startContainer.childNodes[startOffset];
       
  9153 			}
       
  9154 
       
  9155 			// If index based end position then resolve it
       
  9156 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
       
  9157 				endContainer = getEndChild(endContainer, endOffset);
       
  9158 			}
       
  9159 
       
  9160 			// Same container
       
  9161 			if (startContainer == endContainer) {
       
  9162 				return callback(exclude([startContainer]));
       
  9163 			}
       
  9164 
       
  9165 			// Find common ancestor and end points
       
  9166 			ancestor = dom.findCommonAncestor(startContainer, endContainer);
       
  9167 
       
  9168 			// Process left side
       
  9169 			for (node = startContainer; node; node = node.parentNode) {
       
  9170 				if (node === endContainer) {
       
  9171 					return walkBoundary(startContainer, ancestor, true);
       
  9172 				}
       
  9173 
       
  9174 				if (node === ancestor) {
       
  9175 					break;
       
  9176 				}
       
  9177 			}
       
  9178 
       
  9179 			// Process right side
       
  9180 			for (node = endContainer; node; node = node.parentNode) {
       
  9181 				if (node === startContainer) {
       
  9182 					return walkBoundary(endContainer, ancestor);
       
  9183 				}
       
  9184 
       
  9185 				if (node === ancestor) {
       
  9186 					break;
       
  9187 				}
       
  9188 			}
       
  9189 
       
  9190 			// Find start/end point
       
  9191 			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
       
  9192 			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
       
  9193 
       
  9194 			// Walk left leaf
       
  9195 			walkBoundary(startContainer, startPoint, true);
       
  9196 
       
  9197 			// Walk the middle from start to end point
       
  9198 			siblings = collectSiblings(
       
  9199 				startPoint == startContainer ? startPoint : startPoint.nextSibling,
       
  9200 				'nextSibling',
       
  9201 				endPoint == endContainer ? endPoint.nextSibling : endPoint
       
  9202 			);
       
  9203 
       
  9204 			if (siblings.length) {
       
  9205 				callback(exclude(siblings));
       
  9206 			}
       
  9207 
       
  9208 			// Walk right leaf
       
  9209 			walkBoundary(endContainer, endPoint);
       
  9210 		};
       
  9211 
       
  9212 		/**
       
  9213 		 * Splits the specified range at it's start/end points.
       
  9214 		 *
       
  9215 		 * @private
       
  9216 		 * @param {Range/RangeObject} rng Range to split.
       
  9217 		 * @return {Object} Range position object.
       
  9218 		 */
       
  9219 		this.split = function(rng) {
       
  9220 			var startContainer = rng.startContainer,
       
  9221 				startOffset = rng.startOffset,
       
  9222 				endContainer = rng.endContainer,
       
  9223 				endOffset = rng.endOffset;
       
  9224 
       
  9225 			function splitText(node, offset) {
       
  9226 				return node.splitText(offset);
       
  9227 			}
       
  9228 
       
  9229 			// Handle single text node
       
  9230 			if (startContainer == endContainer && startContainer.nodeType == 3) {
       
  9231 				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
       
  9232 					endContainer = splitText(startContainer, startOffset);
       
  9233 					startContainer = endContainer.previousSibling;
       
  9234 
       
  9235 					if (endOffset > startOffset) {
       
  9236 						endOffset = endOffset - startOffset;
       
  9237 						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
       
  9238 						endOffset = endContainer.nodeValue.length;
       
  9239 						startOffset = 0;
       
  9240 					} else {
       
  9241 						endOffset = 0;
       
  9242 					}
       
  9243 				}
       
  9244 			} else {
       
  9245 				// Split startContainer text node if needed
       
  9246 				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
       
  9247 					startContainer = splitText(startContainer, startOffset);
       
  9248 					startOffset = 0;
       
  9249 				}
       
  9250 
       
  9251 				// Split endContainer text node if needed
       
  9252 				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
       
  9253 					endContainer = splitText(endContainer, endOffset).previousSibling;
       
  9254 					endOffset = endContainer.nodeValue.length;
       
  9255 				}
       
  9256 			}
       
  9257 
       
  9258 			return {
       
  9259 				startContainer: startContainer,
       
  9260 				startOffset: startOffset,
       
  9261 				endContainer: endContainer,
       
  9262 				endOffset: endOffset
       
  9263 			};
       
  9264 		};
       
  9265 
       
  9266 		/**
       
  9267 		 * Normalizes the specified range by finding the closest best suitable caret location.
       
  9268 		 *
       
  9269 		 * @private
       
  9270 		 * @param {Range} rng Range to normalize.
       
  9271 		 * @return {Boolean} True/false if the specified range was normalized or not.
       
  9272 		 */
       
  9273 		this.normalize = function(rng) {
       
  9274 			var normalized, collapsed;
       
  9275 
       
  9276 			function normalizeEndPoint(start) {
       
  9277 				var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
       
  9278 				var directionLeft, isAfterNode;
       
  9279 
       
  9280 				function hasBrBeforeAfter(node, left) {
       
  9281 					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
       
  9282 
       
  9283 					while ((node = walker[left ? 'prev' : 'next']())) {
       
  9284 						if (node.nodeName === "BR") {
       
  9285 							return true;
       
  9286 						}
       
  9287 					}
       
  9288 				}
       
  9289 
       
  9290 				function isPrevNode(node, name) {
       
  9291 					return node.previousSibling && node.previousSibling.nodeName == name;
       
  9292 				}
       
  9293 
       
  9294 				// Walks the dom left/right to find a suitable text node to move the endpoint into
       
  9295 				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
       
  9296 				function findTextNodeRelative(left, startNode) {
       
  9297 					var walker, lastInlineElement, parentBlockContainer;
       
  9298 
       
  9299 					startNode = startNode || container;
       
  9300 					parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body;
       
  9301 
       
  9302 					// Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680
       
  9303 					// This: <p><br>|</p> becomes <p>|<br></p>
       
  9304 					if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) {
       
  9305 						container = startNode.parentNode;
       
  9306 						offset = dom.nodeIndex(startNode);
       
  9307 						normalized = true;
       
  9308 						return;
       
  9309 					}
       
  9310 
       
  9311 					// Walk left until we hit a text node we can move to or a block/br/img
       
  9312 					walker = new TreeWalker(startNode, parentBlockContainer);
       
  9313 					while ((node = walker[left ? 'prev' : 'next']())) {
       
  9314 						// Break if we hit a non content editable node
       
  9315 						if (dom.getContentEditableParent(node) === "false") {
       
  9316 							return;
       
  9317 						}
       
  9318 
       
  9319 						// Found text node that has a length
       
  9320 						if (node.nodeType === 3 && node.nodeValue.length > 0) {
       
  9321 							container = node;
       
  9322 							offset = left ? node.nodeValue.length : 0;
       
  9323 							normalized = true;
       
  9324 							return;
       
  9325 						}
       
  9326 
       
  9327 						// Break if we find a block or a BR/IMG/INPUT etc
       
  9328 						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
  9329 							return;
       
  9330 						}
       
  9331 
       
  9332 						lastInlineElement = node;
       
  9333 					}
       
  9334 
       
  9335 					// Only fetch the last inline element when in caret mode for now
       
  9336 					if (collapsed && lastInlineElement) {
       
  9337 						container = lastInlineElement;
       
  9338 						normalized = true;
       
  9339 						offset = 0;
       
  9340 					}
       
  9341 				}
       
  9342 
       
  9343 				container = rng[(start ? 'start' : 'end') + 'Container'];
       
  9344 				offset = rng[(start ? 'start' : 'end') + 'Offset'];
       
  9345 				isAfterNode = container.nodeType == 1 && offset === container.childNodes.length;
       
  9346 				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
       
  9347 				directionLeft = start;
       
  9348 
       
  9349 				if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
       
  9350 					directionLeft = false;
       
  9351 				}
       
  9352 
       
  9353 				// If the container is a document move it to the body element
       
  9354 				if (container.nodeType === 9) {
       
  9355 					container = dom.getRoot();
       
  9356 					offset = 0;
       
  9357 				}
       
  9358 
       
  9359 				// If the container is body try move it into the closest text node or position
       
  9360 				if (container === body) {
       
  9361 					// If start is before/after a image, table etc
       
  9362 					if (directionLeft) {
       
  9363 						node = container.childNodes[offset > 0 ? offset - 1 : 0];
       
  9364 						if (node) {
       
  9365 							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
       
  9366 								return;
       
  9367 							}
       
  9368 						}
       
  9369 					}
       
  9370 
       
  9371 					// Resolve the index
       
  9372 					if (container.hasChildNodes()) {
       
  9373 						offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
       
  9374 						container = container.childNodes[offset];
       
  9375 						offset = 0;
       
  9376 
       
  9377 						// Don't walk into elements that doesn't have any child nodes like a IMG
       
  9378 						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
       
  9379 							// Walk the DOM to find a text node to place the caret at or a BR
       
  9380 							node = container;
       
  9381 							walker = new TreeWalker(container, body);
       
  9382 
       
  9383 							do {
       
  9384 								// Found a text node use that position
       
  9385 								if (node.nodeType === 3 && node.nodeValue.length > 0) {
       
  9386 									offset = directionLeft ? 0 : node.nodeValue.length;
       
  9387 									container = node;
       
  9388 									normalized = true;
       
  9389 									break;
       
  9390 								}
       
  9391 
       
  9392 								// Found a BR/IMG element that we can place the caret before
       
  9393 								if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
  9394 									offset = dom.nodeIndex(node);
       
  9395 									container = node.parentNode;
       
  9396 
       
  9397 									// Put caret after image when moving the end point
       
  9398 									if (node.nodeName == "IMG" && !directionLeft) {
       
  9399 										offset++;
       
  9400 									}
       
  9401 
       
  9402 									normalized = true;
       
  9403 									break;
       
  9404 								}
       
  9405 							} while ((node = (directionLeft ? walker.next() : walker.prev())));
       
  9406 						}
       
  9407 					}
       
  9408 				}
       
  9409 
       
  9410 				// Lean the caret to the left if possible
       
  9411 				if (collapsed) {
       
  9412 					// So this: <b>x</b><i>|x</i>
       
  9413 					// Becomes: <b>x|</b><i>x</i>
       
  9414 					// Seems that only gecko has issues with this
       
  9415 					if (container.nodeType === 3 && offset === 0) {
       
  9416 						findTextNodeRelative(true);
       
  9417 					}
       
  9418 
       
  9419 					// Lean left into empty inline elements when the caret is before a BR
       
  9420 					// So this: <i><b></b><i>|<br></i>
       
  9421 					// Becomes: <i><b>|</b><i><br></i>
       
  9422 					// Seems that only gecko has issues with this.
       
  9423 					// Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p>
       
  9424 					if (container.nodeType === 1) {
       
  9425 						node = container.childNodes[offset];
       
  9426 
       
  9427 						// Offset is after the containers last child
       
  9428 						// then use the previous child for normalization
       
  9429 						if (!node) {
       
  9430 							node = container.childNodes[offset - 1];
       
  9431 						}
       
  9432 
       
  9433 						if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
       
  9434 							!hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
       
  9435 							findTextNodeRelative(true, node);
       
  9436 						}
       
  9437 					}
       
  9438 				}
       
  9439 
       
  9440 				// Lean the start of the selection right if possible
       
  9441 				// So this: x[<b>x]</b>
       
  9442 				// Becomes: x<b>[x]</b>
       
  9443 				if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
       
  9444 					findTextNodeRelative(false);
       
  9445 				}
       
  9446 
       
  9447 				// Set endpoint if it was normalized
       
  9448 				if (normalized) {
       
  9449 					rng['set' + (start ? 'Start' : 'End')](container, offset);
       
  9450 				}
       
  9451 			}
       
  9452 
       
  9453 			collapsed = rng.collapsed;
       
  9454 
       
  9455 			normalizeEndPoint(true);
       
  9456 
       
  9457 			if (!collapsed) {
       
  9458 				normalizeEndPoint();
       
  9459 			}
       
  9460 
       
  9461 			// If it was collapsed then make sure it still is
       
  9462 			if (normalized && collapsed) {
       
  9463 				rng.collapse(true);
       
  9464 			}
       
  9465 
       
  9466 			return normalized;
       
  9467 		};
       
  9468 	}
       
  9469 
       
  9470 	/**
       
  9471 	 * Compares two ranges and checks if they are equal.
       
  9472 	 *
       
  9473 	 * @static
       
  9474 	 * @method compareRanges
       
  9475 	 * @param {DOMRange} rng1 First range to compare.
       
  9476 	 * @param {DOMRange} rng2 First range to compare.
       
  9477 	 * @return {Boolean} true/false if the ranges are equal.
       
  9478 	 */
       
  9479 	RangeUtils.compareRanges = function(rng1, rng2) {
       
  9480 		if (rng1 && rng2) {
       
  9481 			// Compare native IE ranges
       
  9482 			if (rng1.item || rng1.duplicate) {
       
  9483 				// Both are control ranges and the selected element matches
       
  9484 				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
       
  9485 					return true;
       
  9486 				}
       
  9487 
       
  9488 				// Both are text ranges and the range matches
       
  9489 				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
       
  9490 					return true;
       
  9491 				}
       
  9492 			} else {
       
  9493 				// Compare w3c ranges
       
  9494 				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
       
  9495 			}
       
  9496 		}
       
  9497 
       
  9498 		return false;
       
  9499 	};
       
  9500 
       
  9501 	/**
       
  9502 	 * Gets the caret range for the given x/y location.
       
  9503 	 *
       
  9504 	 * @static
       
  9505 	 * @method getCaretRangeFromPoint
       
  9506 	 * @param {Number} x X coordinate for range
       
  9507 	 * @param {Number} y Y coordinate for range
       
  9508 	 * @param {Document} doc Document that x/y are relative to
       
  9509 	 * @returns {Range} caret range
       
  9510 	 */
       
  9511 	RangeUtils.getCaretRangeFromPoint = function(x, y, doc) {
       
  9512 		var rng, point;
       
  9513 
       
  9514 		if (doc.caretPositionFromPoint) {
       
  9515 			point = doc.caretPositionFromPoint(x, y);
       
  9516 			rng = doc.createRange();
       
  9517 			rng.setStart(point.offsetNode, point.offset);
       
  9518 			rng.collapse(true);
       
  9519 		} else if (doc.caretRangeFromPoint) {
       
  9520 			rng = doc.caretRangeFromPoint(x, y);
       
  9521 		} else if (doc.body.createTextRange) {
       
  9522 			rng = doc.body.createTextRange();
       
  9523 
       
  9524 			try {
       
  9525 				rng.moveToPoint(x, y);
       
  9526 				rng.collapse(true);
       
  9527 			} catch (ex) {
       
  9528 				// Append to top or bottom depending on drop location
       
  9529 				rng.collapse(y < doc.body.clientHeight);
       
  9530 			}
       
  9531 		}
       
  9532 
       
  9533 		return rng;
       
  9534 	};
       
  9535 
       
  9536 	RangeUtils.getNode = function(container, offset) {
       
  9537 		if (container.nodeType == 1 && container.hasChildNodes()) {
       
  9538 			if (offset >= container.childNodes.length) {
       
  9539 				offset = container.childNodes.length - 1;
       
  9540 			}
       
  9541 
       
  9542 			container = container.childNodes[offset];
       
  9543 		}
       
  9544 
       
  9545 		return container;
       
  9546 	};
       
  9547 
       
  9548 	return RangeUtils;
       
  9549 });
       
  9550 
       
  9551 // Included from: js/tinymce/classes/NodeChange.js
       
  9552 
       
  9553 /**
       
  9554  * NodeChange.js
       
  9555  *
       
  9556  * Copyright, Moxiecode Systems AB
       
  9557  * Released under LGPL License.
       
  9558  *
       
  9559  * License: http://www.tinymce.com/license
       
  9560  * Contributing: http://www.tinymce.com/contributing
       
  9561  */
       
  9562 
       
  9563 /**
       
  9564  * This class handles the nodechange event dispatching both manual and though selection change events.
       
  9565  *
       
  9566  * @class tinymce.NodeChange
       
  9567  * @private
       
  9568  */
       
  9569 define("tinymce/NodeChange", [
       
  9570 	"tinymce/dom/RangeUtils",
       
  9571 	"tinymce/Env"
       
  9572 ], function(RangeUtils, Env) {
       
  9573 	return function(editor) {
       
  9574 		var lastRng, lastPath = [];
       
  9575 
       
  9576 		/**
       
  9577 		 * Returns true/false if the current element path has been changed or not.
       
  9578 		 *
       
  9579 		 * @private
       
  9580 		 * @return {Boolean} True if the element path is the same false if it's not.
       
  9581 		 */
       
  9582 		function isSameElementPath(startElm) {
       
  9583 			var i, currentPath;
       
  9584 
       
  9585 			currentPath = editor.$(startElm).parentsUntil(editor.getBody()).add(startElm);
       
  9586 			if (currentPath.length === lastPath.length) {
       
  9587 				for (i = currentPath.length; i >= 0; i--) {
       
  9588 					if (currentPath[i] !== lastPath[i]) {
       
  9589 						break;
       
  9590 					}
       
  9591 				}
       
  9592 
       
  9593 				if (i === -1) {
       
  9594 					lastPath = currentPath;
       
  9595 					return true;
       
  9596 				}
       
  9597 			}
       
  9598 
       
  9599 			lastPath = currentPath;
       
  9600 
       
  9601 			return false;
       
  9602 		}
       
  9603 
       
  9604 		// Gecko doesn't support the "selectionchange" event
       
  9605 		if (!('onselectionchange' in editor.getDoc())) {
       
  9606 			editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) {
       
  9607 				var nativeRng, fakeRng;
       
  9608 
       
  9609 				// Since DOM Ranges mutate on modification
       
  9610 				// of the DOM we need to clone it's contents
       
  9611 				nativeRng = editor.selection.getRng();
       
  9612 				fakeRng = {
       
  9613 					startContainer: nativeRng.startContainer,
       
  9614 					startOffset: nativeRng.startOffset,
       
  9615 					endContainer: nativeRng.endContainer,
       
  9616 					endOffset: nativeRng.endOffset
       
  9617 				};
       
  9618 
       
  9619 				// Always treat nodechange as a selectionchange since applying
       
  9620 				// formatting to the current range wouldn't update the range but it's parent
       
  9621 				if (e.type == 'nodechange' || !RangeUtils.compareRanges(fakeRng, lastRng)) {
       
  9622 					editor.fire('SelectionChange');
       
  9623 				}
       
  9624 
       
  9625 				lastRng = fakeRng;
       
  9626 			});
       
  9627 		}
       
  9628 
       
  9629 		// IE has a bug where it fires a selectionchange on right click that has a range at the start of the body
       
  9630 		// When the contextmenu event fires the selection is located at the right location
       
  9631 		editor.on('contextmenu', function() {
       
  9632 			editor.fire('SelectionChange');
       
  9633 		});
       
  9634 
       
  9635 		// Selection change is delayed ~200ms on IE when you click inside the current range
       
  9636 		editor.on('SelectionChange', function() {
       
  9637 			var startElm = editor.selection.getStart(true);
       
  9638 
       
  9639 			// IE 8 will fire a selectionchange event with an incorrect selection
       
  9640 			// when focusing out of table cells. Click inside cell -> toolbar = Invalid SelectionChange event
       
  9641 			if (!Env.range && editor.selection.isCollapsed()) {
       
  9642 				return;
       
  9643 			}
       
  9644 
       
  9645 			if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
       
  9646 				editor.nodeChanged({selectionChange: true});
       
  9647 			}
       
  9648 		});
       
  9649 
       
  9650 		// Fire an extra nodeChange on mouseup for compatibility reasons
       
  9651 		editor.on('MouseUp', function(e) {
       
  9652 			if (!e.isDefaultPrevented()) {
       
  9653 				// Delay nodeChanged call for WebKit edge case issue where the range
       
  9654 				// isn't updated until after you click outside a selected image
       
  9655 				if (editor.selection.getNode().nodeName == 'IMG') {
       
  9656 					setTimeout(function() {
       
  9657 						editor.nodeChanged();
       
  9658 					}, 0);
       
  9659 				} else {
       
  9660 					editor.nodeChanged();
       
  9661 				}
       
  9662 			}
       
  9663 		});
       
  9664 
       
  9665 		/**
       
  9666 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
       
  9667 		 * need to update the UI states or element path etc.
       
  9668 		 *
       
  9669 		 * @method nodeChanged
       
  9670 		 * @param {Object} args Optional args to pass to NodeChange event handlers.
       
  9671 		 */
       
  9672 		this.nodeChanged = function(args) {
       
  9673 			var selection = editor.selection, node, parents, root;
       
  9674 
       
  9675 			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
       
  9676 			if (editor.initialized && selection && !editor.settings.disable_nodechange && !editor.settings.readonly) {
       
  9677 				// Get start node
       
  9678 				root = editor.getBody();
       
  9679 				node = selection.getStart() || root;
       
  9680 				node = node.ownerDocument != editor.getDoc() ? editor.getBody() : node;
       
  9681 
       
  9682 				// Edge case for <p>|<img></p>
       
  9683 				if (node.nodeName == 'IMG' && selection.isCollapsed()) {
       
  9684 					node = node.parentNode;
       
  9685 				}
       
  9686 
       
  9687 				// Get parents and add them to object
       
  9688 				parents = [];
       
  9689 				editor.dom.getParent(node, function(node) {
       
  9690 					if (node === root) {
       
  9691 						return true;
       
  9692 					}
       
  9693 
       
  9694 					parents.push(node);
       
  9695 				});
       
  9696 
       
  9697 				args = args || {};
       
  9698 				args.element = node;
       
  9699 				args.parents = parents;
       
  9700 
       
  9701 				editor.fire('NodeChange', args);
       
  9702 			}
       
  9703 		};
       
  9704 	};
       
  9705 });
       
  9706 
       
  9707 // Included from: js/tinymce/classes/html/Node.js
       
  9708 
       
  9709 /**
       
  9710  * Node.js
       
  9711  *
       
  9712  * Copyright, Moxiecode Systems AB
       
  9713  * Released under LGPL License.
       
  9714  *
       
  9715  * License: http://www.tinymce.com/license
       
  9716  * Contributing: http://www.tinymce.com/contributing
       
  9717  */
       
  9718 
       
  9719 /**
       
  9720  * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
       
  9721  *
       
  9722  * @example
       
  9723  * var node = new tinymce.html.Node('strong', 1);
       
  9724  * someRoot.append(node);
       
  9725  *
       
  9726  * @class tinymce.html.Node
       
  9727  * @version 3.4
       
  9728  */
       
  9729 define("tinymce/html/Node", [], function() {
       
  9730 	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
       
  9731 		'#text': 3,
       
  9732 		'#comment': 8,
       
  9733 		'#cdata': 4,
       
  9734 		'#pi': 7,
       
  9735 		'#doctype': 10,
       
  9736 		'#document-fragment': 11
       
  9737 	};
       
  9738 
       
  9739 	// Walks the tree left/right
       
  9740 	function walk(node, root_node, prev) {
       
  9741 		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
       
  9742 
       
  9743 		// Walk into nodes if it has a start
       
  9744 		if (node[startName]) {
       
  9745 			return node[startName];
       
  9746 		}
       
  9747 
       
  9748 		// Return the sibling if it has one
       
  9749 		if (node !== root_node) {
       
  9750 			sibling = node[siblingName];
       
  9751 
       
  9752 			if (sibling) {
       
  9753 				return sibling;
       
  9754 			}
       
  9755 
       
  9756 			// Walk up the parents to look for siblings
       
  9757 			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
       
  9758 				sibling = parent[siblingName];
       
  9759 
       
  9760 				if (sibling) {
       
  9761 					return sibling;
       
  9762 				}
       
  9763 			}
       
  9764 		}
       
  9765 	}
       
  9766 
       
  9767 	/**
       
  9768 	 * Constructs a new Node instance.
       
  9769 	 *
       
  9770 	 * @constructor
       
  9771 	 * @method Node
       
  9772 	 * @param {String} name Name of the node type.
       
  9773 	 * @param {Number} type Numeric type representing the node.
       
  9774 	 */
       
  9775 	function Node(name, type) {
       
  9776 		this.name = name;
       
  9777 		this.type = type;
       
  9778 
       
  9779 		if (type === 1) {
       
  9780 			this.attributes = [];
       
  9781 			this.attributes.map = {};
       
  9782 		}
       
  9783 	}
       
  9784 
       
  9785 	Node.prototype = {
       
  9786 		/**
       
  9787 		 * Replaces the current node with the specified one.
       
  9788 		 *
       
  9789 		 * @example
       
  9790 		 * someNode.replace(someNewNode);
       
  9791 		 *
       
  9792 		 * @method replace
       
  9793 		 * @param {tinymce.html.Node} node Node to replace the current node with.
       
  9794 		 * @return {tinymce.html.Node} The old node that got replaced.
       
  9795 		 */
       
  9796 		replace: function(node) {
       
  9797 			var self = this;
       
  9798 
       
  9799 			if (node.parent) {
       
  9800 				node.remove();
       
  9801 			}
       
  9802 
       
  9803 			self.insert(node, self);
       
  9804 			self.remove();
       
  9805 
       
  9806 			return self;
       
  9807 		},
       
  9808 
       
  9809 		/**
       
  9810 		 * Gets/sets or removes an attribute by name.
       
  9811 		 *
       
  9812 		 * @example
       
  9813 		 * someNode.attr("name", "value"); // Sets an attribute
       
  9814 		 * console.log(someNode.attr("name")); // Gets an attribute
       
  9815 		 * someNode.attr("name", null); // Removes an attribute
       
  9816 		 *
       
  9817 		 * @method attr
       
  9818 		 * @param {String} name Attribute name to set or get.
       
  9819 		 * @param {String} value Optional value to set.
       
  9820 		 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
       
  9821 		 */
       
  9822 		attr: function(name, value) {
       
  9823 			var self = this, attrs, i, undef;
       
  9824 
       
  9825 			if (typeof name !== "string") {
       
  9826 				for (i in name) {
       
  9827 					self.attr(i, name[i]);
       
  9828 				}
       
  9829 
       
  9830 				return self;
       
  9831 			}
       
  9832 
       
  9833 			if ((attrs = self.attributes)) {
       
  9834 				if (value !== undef) {
       
  9835 					// Remove attribute
       
  9836 					if (value === null) {
       
  9837 						if (name in attrs.map) {
       
  9838 							delete attrs.map[name];
       
  9839 
       
  9840 							i = attrs.length;
       
  9841 							while (i--) {
       
  9842 								if (attrs[i].name === name) {
       
  9843 									attrs = attrs.splice(i, 1);
       
  9844 									return self;
       
  9845 								}
       
  9846 							}
       
  9847 						}
       
  9848 
       
  9849 						return self;
       
  9850 					}
       
  9851 
       
  9852 					// Set attribute
       
  9853 					if (name in attrs.map) {
       
  9854 						// Set attribute
       
  9855 						i = attrs.length;
       
  9856 						while (i--) {
       
  9857 							if (attrs[i].name === name) {
       
  9858 								attrs[i].value = value;
       
  9859 								break;
       
  9860 							}
       
  9861 						}
       
  9862 					} else {
       
  9863 						attrs.push({name: name, value: value});
       
  9864 					}
       
  9865 
       
  9866 					attrs.map[name] = value;
       
  9867 
       
  9868 					return self;
       
  9869 				} else {
       
  9870 					return attrs.map[name];
       
  9871 				}
       
  9872 			}
       
  9873 		},
       
  9874 
       
  9875 		/**
       
  9876 		 * Does a shallow clones the node into a new node. It will also exclude id attributes since
       
  9877 		 * there should only be one id per document.
       
  9878 		 *
       
  9879 		 * @example
       
  9880 		 * var clonedNode = node.clone();
       
  9881 		 *
       
  9882 		 * @method clone
       
  9883 		 * @return {tinymce.html.Node} New copy of the original node.
       
  9884 		 */
       
  9885 		clone: function() {
       
  9886 			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
       
  9887 
       
  9888 			// Clone element attributes
       
  9889 			if ((selfAttrs = self.attributes)) {
       
  9890 				cloneAttrs = [];
       
  9891 				cloneAttrs.map = {};
       
  9892 
       
  9893 				for (i = 0, l = selfAttrs.length; i < l; i++) {
       
  9894 					selfAttr = selfAttrs[i];
       
  9895 
       
  9896 					// Clone everything except id
       
  9897 					if (selfAttr.name !== 'id') {
       
  9898 						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
       
  9899 						cloneAttrs.map[selfAttr.name] = selfAttr.value;
       
  9900 					}
       
  9901 				}
       
  9902 
       
  9903 				clone.attributes = cloneAttrs;
       
  9904 			}
       
  9905 
       
  9906 			clone.value = self.value;
       
  9907 			clone.shortEnded = self.shortEnded;
       
  9908 
       
  9909 			return clone;
       
  9910 		},
       
  9911 
       
  9912 		/**
       
  9913 		 * Wraps the node in in another node.
       
  9914 		 *
       
  9915 		 * @example
       
  9916 		 * node.wrap(wrapperNode);
       
  9917 		 *
       
  9918 		 * @method wrap
       
  9919 		 */
       
  9920 		wrap: function(wrapper) {
       
  9921 			var self = this;
       
  9922 
       
  9923 			self.parent.insert(wrapper, self);
       
  9924 			wrapper.append(self);
       
  9925 
       
  9926 			return self;
       
  9927 		},
       
  9928 
       
  9929 		/**
       
  9930 		 * Unwraps the node in other words it removes the node but keeps the children.
       
  9931 		 *
       
  9932 		 * @example
       
  9933 		 * node.unwrap();
       
  9934 		 *
       
  9935 		 * @method unwrap
       
  9936 		 */
       
  9937 		unwrap: function() {
       
  9938 			var self = this, node, next;
       
  9939 
       
  9940 			for (node = self.firstChild; node;) {
       
  9941 				next = node.next;
       
  9942 				self.insert(node, self, true);
       
  9943 				node = next;
       
  9944 			}
       
  9945 
       
  9946 			self.remove();
       
  9947 		},
       
  9948 
       
  9949 		/**
       
  9950 		 * Removes the node from it's parent.
       
  9951 		 *
       
  9952 		 * @example
       
  9953 		 * node.remove();
       
  9954 		 *
       
  9955 		 * @method remove
       
  9956 		 * @return {tinymce.html.Node} Current node that got removed.
       
  9957 		 */
       
  9958 		remove: function() {
       
  9959 			var self = this, parent = self.parent, next = self.next, prev = self.prev;
       
  9960 
       
  9961 			if (parent) {
       
  9962 				if (parent.firstChild === self) {
       
  9963 					parent.firstChild = next;
       
  9964 
       
  9965 					if (next) {
       
  9966 						next.prev = null;
       
  9967 					}
       
  9968 				} else {
       
  9969 					prev.next = next;
       
  9970 				}
       
  9971 
       
  9972 				if (parent.lastChild === self) {
       
  9973 					parent.lastChild = prev;
       
  9974 
       
  9975 					if (prev) {
       
  9976 						prev.next = null;
       
  9977 					}
       
  9978 				} else {
       
  9979 					next.prev = prev;
       
  9980 				}
       
  9981 
       
  9982 				self.parent = self.next = self.prev = null;
       
  9983 			}
       
  9984 
       
  9985 			return self;
       
  9986 		},
       
  9987 
       
  9988 		/**
       
  9989 		 * Appends a new node as a child of the current node.
       
  9990 		 *
       
  9991 		 * @example
       
  9992 		 * node.append(someNode);
       
  9993 		 *
       
  9994 		 * @method append
       
  9995 		 * @param {tinymce.html.Node} node Node to append as a child of the current one.
       
  9996 		 * @return {tinymce.html.Node} The node that got appended.
       
  9997 		 */
       
  9998 		append: function(node) {
       
  9999 			var self = this, last;
       
 10000 
       
 10001 			if (node.parent) {
       
 10002 				node.remove();
       
 10003 			}
       
 10004 
       
 10005 			last = self.lastChild;
       
 10006 			if (last) {
       
 10007 				last.next = node;
       
 10008 				node.prev = last;
       
 10009 				self.lastChild = node;
       
 10010 			} else {
       
 10011 				self.lastChild = self.firstChild = node;
       
 10012 			}
       
 10013 
       
 10014 			node.parent = self;
       
 10015 
       
 10016 			return node;
       
 10017 		},
       
 10018 
       
 10019 		/**
       
 10020 		 * Inserts a node at a specific position as a child of the current node.
       
 10021 		 *
       
 10022 		 * @example
       
 10023 		 * parentNode.insert(newChildNode, oldChildNode);
       
 10024 		 *
       
 10025 		 * @method insert
       
 10026 		 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
       
 10027 		 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
       
 10028 		 * @param {Boolean} before Optional state to insert the node before the reference node.
       
 10029 		 * @return {tinymce.html.Node} The node that got inserted.
       
 10030 		 */
       
 10031 		insert: function(node, ref_node, before) {
       
 10032 			var parent;
       
 10033 
       
 10034 			if (node.parent) {
       
 10035 				node.remove();
       
 10036 			}
       
 10037 
       
 10038 			parent = ref_node.parent || this;
       
 10039 
       
 10040 			if (before) {
       
 10041 				if (ref_node === parent.firstChild) {
       
 10042 					parent.firstChild = node;
       
 10043 				} else {
       
 10044 					ref_node.prev.next = node;
       
 10045 				}
       
 10046 
       
 10047 				node.prev = ref_node.prev;
       
 10048 				node.next = ref_node;
       
 10049 				ref_node.prev = node;
       
 10050 			} else {
       
 10051 				if (ref_node === parent.lastChild) {
       
 10052 					parent.lastChild = node;
       
 10053 				} else {
       
 10054 					ref_node.next.prev = node;
       
 10055 				}
       
 10056 
       
 10057 				node.next = ref_node.next;
       
 10058 				node.prev = ref_node;
       
 10059 				ref_node.next = node;
       
 10060 			}
       
 10061 
       
 10062 			node.parent = parent;
       
 10063 
       
 10064 			return node;
       
 10065 		},
       
 10066 
       
 10067 		/**
       
 10068 		 * Get all children by name.
       
 10069 		 *
       
 10070 		 * @method getAll
       
 10071 		 * @param {String} name Name of the child nodes to collect.
       
 10072 		 * @return {Array} Array with child nodes matchin the specified name.
       
 10073 		 */
       
 10074 		getAll: function(name) {
       
 10075 			var self = this, node, collection = [];
       
 10076 
       
 10077 			for (node = self.firstChild; node; node = walk(node, self)) {
       
 10078 				if (node.name === name) {
       
 10079 					collection.push(node);
       
 10080 				}
       
 10081 			}
       
 10082 
       
 10083 			return collection;
       
 10084 		},
       
 10085 
       
 10086 		/**
       
 10087 		 * Removes all children of the current node.
       
 10088 		 *
       
 10089 		 * @method empty
       
 10090 		 * @return {tinymce.html.Node} The current node that got cleared.
       
 10091 		 */
       
 10092 		empty: function() {
       
 10093 			var self = this, nodes, i, node;
       
 10094 
       
 10095 			// Remove all children
       
 10096 			if (self.firstChild) {
       
 10097 				nodes = [];
       
 10098 
       
 10099 				// Collect the children
       
 10100 				for (node = self.firstChild; node; node = walk(node, self)) {
       
 10101 					nodes.push(node);
       
 10102 				}
       
 10103 
       
 10104 				// Remove the children
       
 10105 				i = nodes.length;
       
 10106 				while (i--) {
       
 10107 					node = nodes[i];
       
 10108 					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
       
 10109 				}
       
 10110 			}
       
 10111 
       
 10112 			self.firstChild = self.lastChild = null;
       
 10113 
       
 10114 			return self;
       
 10115 		},
       
 10116 
       
 10117 		/**
       
 10118 		 * Returns true/false if the node is to be considered empty or not.
       
 10119 		 *
       
 10120 		 * @example
       
 10121 		 * node.isEmpty({img: true});
       
 10122 		 * @method isEmpty
       
 10123 		 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
       
 10124 		 * @return {Boolean} true/false if the node is empty or not.
       
 10125 		 */
       
 10126 		isEmpty: function(elements) {
       
 10127 			var self = this, node = self.firstChild, i, name;
       
 10128 
       
 10129 			if (node) {
       
 10130 				do {
       
 10131 					if (node.type === 1) {
       
 10132 						// Ignore bogus elements
       
 10133 						if (node.attributes.map['data-mce-bogus']) {
       
 10134 							continue;
       
 10135 						}
       
 10136 
       
 10137 						// Keep empty elements like <img />
       
 10138 						if (elements[node.name]) {
       
 10139 							return false;
       
 10140 						}
       
 10141 
       
 10142 						// Keep bookmark nodes and name attribute like <a name="1"></a>
       
 10143 						i = node.attributes.length;
       
 10144 						while (i--) {
       
 10145 							name = node.attributes[i].name;
       
 10146 							if (name === "name" || name.indexOf('data-mce-bookmark') === 0) {
       
 10147 								return false;
       
 10148 							}
       
 10149 						}
       
 10150 					}
       
 10151 
       
 10152 					// Keep comments
       
 10153 					if (node.type === 8) {
       
 10154 						return false;
       
 10155 					}
       
 10156 
       
 10157 					// Keep non whitespace text nodes
       
 10158 					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
       
 10159 						return false;
       
 10160 					}
       
 10161 				} while ((node = walk(node, self)));
       
 10162 			}
       
 10163 
       
 10164 			return true;
       
 10165 		},
       
 10166 
       
 10167 		/**
       
 10168 		 * Walks to the next or previous node and returns that node or null if it wasn't found.
       
 10169 		 *
       
 10170 		 * @method walk
       
 10171 		 * @param {Boolean} prev Optional previous node state defaults to false.
       
 10172 		 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
       
 10173 		 */
       
 10174 		walk: function(prev) {
       
 10175 			return walk(this, null, prev);
       
 10176 		}
       
 10177 	};
       
 10178 
       
 10179 	/**
       
 10180 	 * Creates a node of a specific type.
       
 10181 	 *
       
 10182 	 * @static
       
 10183 	 * @method create
       
 10184 	 * @param {String} name Name of the node type to create for example "b" or "#text".
       
 10185 	 * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
       
 10186 	 */
       
 10187 	Node.create = function(name, attrs) {
       
 10188 		var node, attrName;
       
 10189 
       
 10190 		// Create node
       
 10191 		node = new Node(name, typeLookup[name] || 1);
       
 10192 
       
 10193 		// Add attributes if needed
       
 10194 		if (attrs) {
       
 10195 			for (attrName in attrs) {
       
 10196 				node.attr(attrName, attrs[attrName]);
       
 10197 			}
       
 10198 		}
       
 10199 
       
 10200 		return node;
       
 10201 	};
       
 10202 
       
 10203 	return Node;
       
 10204 });
       
 10205 
       
 10206 // Included from: js/tinymce/classes/html/Schema.js
       
 10207 
       
 10208 /**
       
 10209  * Schema.js
       
 10210  *
       
 10211  * Copyright, Moxiecode Systems AB
       
 10212  * Released under LGPL License.
       
 10213  *
       
 10214  * License: http://www.tinymce.com/license
       
 10215  * Contributing: http://www.tinymce.com/contributing
       
 10216  */
       
 10217 
       
 10218 /**
       
 10219  * Schema validator class.
       
 10220  *
       
 10221  * @class tinymce.html.Schema
       
 10222  * @example
       
 10223  *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
       
 10224  *    alert('span is valid child of p.');
       
 10225  *
       
 10226  *  if (tinymce.activeEditor.schema.getElementRule('p'))
       
 10227  *    alert('P is a valid element.');
       
 10228  *
       
 10229  * @class tinymce.html.Schema
       
 10230  * @version 3.4
       
 10231  */
       
 10232 define("tinymce/html/Schema", [
       
 10233 	"tinymce/util/Tools"
       
 10234 ], function(Tools) {
       
 10235 	var mapCache = {}, dummyObj = {};
       
 10236 	var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
       
 10237 
       
 10238 	function split(items, delim) {
       
 10239 		return items ? items.split(delim || ' ') : [];
       
 10240 	}
       
 10241 
       
 10242 	/**
       
 10243 	 * Builds a schema lookup table
       
 10244 	 *
       
 10245 	 * @private
       
 10246 	 * @param {String} type html4, html5 or html5-strict schema type.
       
 10247 	 * @return {Object} Schema lookup table.
       
 10248 	 */
       
 10249 	function compileSchema(type) {
       
 10250 		var schema = {}, globalAttributes, blockContent;
       
 10251 		var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
       
 10252 
       
 10253 		function add(name, attributes, children) {
       
 10254 			var ni, i, attributesOrder, args = arguments;
       
 10255 
       
 10256 			function arrayToMap(array, obj) {
       
 10257 				var map = {}, i, l;
       
 10258 
       
 10259 				for (i = 0, l = array.length; i < l; i++) {
       
 10260 					map[array[i]] = obj || {};
       
 10261 				}
       
 10262 
       
 10263 				return map;
       
 10264 			}
       
 10265 
       
 10266 			children = children || [];
       
 10267 			attributes = attributes || "";
       
 10268 
       
 10269 			if (typeof children === "string") {
       
 10270 				children = split(children);
       
 10271 			}
       
 10272 
       
 10273 			// Split string children
       
 10274 			for (i = 3; i < args.length; i++) {
       
 10275 				if (typeof args[i] === "string") {
       
 10276 					args[i] = split(args[i]);
       
 10277 				}
       
 10278 
       
 10279 				children.push.apply(children, args[i]);
       
 10280 			}
       
 10281 
       
 10282 			name = split(name);
       
 10283 			ni = name.length;
       
 10284 			while (ni--) {
       
 10285 				attributesOrder = [].concat(globalAttributes, split(attributes));
       
 10286 				schema[name[ni]] = {
       
 10287 					attributes: arrayToMap(attributesOrder),
       
 10288 					attributesOrder: attributesOrder,
       
 10289 					children: arrayToMap(children, dummyObj)
       
 10290 				};
       
 10291 			}
       
 10292 		}
       
 10293 
       
 10294 		function addAttrs(name, attributes) {
       
 10295 			var ni, schemaItem, i, l;
       
 10296 
       
 10297 			name = split(name);
       
 10298 			ni = name.length;
       
 10299 			attributes = split(attributes);
       
 10300 			while (ni--) {
       
 10301 				schemaItem = schema[name[ni]];
       
 10302 				for (i = 0, l = attributes.length; i < l; i++) {
       
 10303 					schemaItem.attributes[attributes[i]] = {};
       
 10304 					schemaItem.attributesOrder.push(attributes[i]);
       
 10305 				}
       
 10306 			}
       
 10307 		}
       
 10308 
       
 10309 		// Use cached schema
       
 10310 		if (mapCache[type]) {
       
 10311 			return mapCache[type];
       
 10312 		}
       
 10313 
       
 10314 		// Attributes present on all elements
       
 10315 		globalAttributes = split("id accesskey class dir lang style tabindex title");
       
 10316 
       
 10317 		// Event attributes can be opt-in/opt-out
       
 10318 		/*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
       
 10319 				"ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
       
 10320 				"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
       
 10321 				"onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
       
 10322 				"onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
       
 10323 				"onwaiting"
       
 10324 		);*/
       
 10325 
       
 10326 		// Block content elements
       
 10327 		blockContent = split(
       
 10328 			"address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
       
 10329 		);
       
 10330 
       
 10331 		// Phrasing content elements from the HTML5 spec (inline)
       
 10332 		phrasingContent = split(
       
 10333 			"a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
       
 10334 			"label map noscript object q s samp script select small span strong sub sup " +
       
 10335 			"textarea u var #text #comment"
       
 10336 		);
       
 10337 
       
 10338 		// Add HTML5 items to globalAttributes, blockContent, phrasingContent
       
 10339 		if (type != "html4") {
       
 10340 			globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
       
 10341 				"hidden spellcheck translate"));
       
 10342 			blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
       
 10343 			phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output progress time wbr " +
       
 10344 				"video ruby bdi keygen"));
       
 10345 		}
       
 10346 
       
 10347 		// Add HTML4 elements unless it's html5-strict
       
 10348 		if (type != "html5-strict") {
       
 10349 			globalAttributes.push("xml:lang");
       
 10350 
       
 10351 			html4PhrasingContent = split("acronym applet basefont big font strike tt");
       
 10352 			phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
       
 10353 
       
 10354 			each(html4PhrasingContent, function(name) {
       
 10355 				add(name, "", phrasingContent);
       
 10356 			});
       
 10357 
       
 10358 			html4BlockContent = split("center dir isindex noframes");
       
 10359 			blockContent.push.apply(blockContent, html4BlockContent);
       
 10360 
       
 10361 			// Flow content elements from the HTML5 spec (block+inline)
       
 10362 			flowContent = [].concat(blockContent, phrasingContent);
       
 10363 
       
 10364 			each(html4BlockContent, function(name) {
       
 10365 				add(name, "", flowContent);
       
 10366 			});
       
 10367 		}
       
 10368 
       
 10369 		// Flow content elements from the HTML5 spec (block+inline)
       
 10370 		flowContent = flowContent || [].concat(blockContent, phrasingContent);
       
 10371 
       
 10372 		// HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
       
 10373 		// Schema items <element name>, <specific attributes>, <children ..>
       
 10374 		add("html", "manifest", "head body");
       
 10375 		add("head", "", "base command link meta noscript script style title");
       
 10376 		add("title hr noscript br");
       
 10377 		add("base", "href target");
       
 10378 		add("link", "href rel media hreflang type sizes hreflang");
       
 10379 		add("meta", "name http-equiv content charset");
       
 10380 		add("style", "media type scoped");
       
 10381 		add("script", "src async defer type charset");
       
 10382 		add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
       
 10383 				"onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
       
 10384 				"onpopstate onresize onscroll onstorage onunload", flowContent);
       
 10385 		add("address dt dd div caption", "", flowContent);
       
 10386 		add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
       
 10387 		add("blockquote", "cite", flowContent);
       
 10388 		add("ol", "reversed start type", "li");
       
 10389 		add("ul", "", "li");
       
 10390 		add("li", "value", flowContent);
       
 10391 		add("dl", "", "dt dd");
       
 10392 		add("a", "href target rel media hreflang type", phrasingContent);
       
 10393 		add("q", "cite", phrasingContent);
       
 10394 		add("ins del", "cite datetime", flowContent);
       
 10395 		add("img", "src sizes srcset alt usemap ismap width height");
       
 10396 		add("iframe", "src name width height", flowContent);
       
 10397 		add("embed", "src type width height");
       
 10398 		add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
       
 10399 		add("param", "name value");
       
 10400 		add("map", "name", flowContent, "area");
       
 10401 		add("area", "alt coords shape href target rel media hreflang type");
       
 10402 		add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
       
 10403 		add("colgroup", "span", "col");
       
 10404 		add("col", "span");
       
 10405 		add("tbody thead tfoot", "", "tr");
       
 10406 		add("tr", "", "td th");
       
 10407 		add("td", "colspan rowspan headers", flowContent);
       
 10408 		add("th", "colspan rowspan headers scope abbr", flowContent);
       
 10409 		add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
       
 10410 		add("fieldset", "disabled form name", flowContent, "legend");
       
 10411 		add("label", "form for", phrasingContent);
       
 10412 		add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
       
 10413 				"formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
       
 10414 		);
       
 10415 		add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
       
 10416 			type == "html4" ? flowContent : phrasingContent);
       
 10417 		add("select", "disabled form multiple name required size", "option optgroup");
       
 10418 		add("optgroup", "disabled label", "option");
       
 10419 		add("option", "disabled label selected value");
       
 10420 		add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
       
 10421 		add("menu", "type label", flowContent, "li");
       
 10422 		add("noscript", "", flowContent);
       
 10423 
       
 10424 		// Extend with HTML5 elements
       
 10425 		if (type != "html4") {
       
 10426 			add("wbr");
       
 10427 			add("ruby", "", phrasingContent, "rt rp");
       
 10428 			add("figcaption", "", flowContent);
       
 10429 			add("mark rt rp summary bdi", "", phrasingContent);
       
 10430 			add("canvas", "width height", flowContent);
       
 10431 			add("video", "src crossorigin poster preload autoplay mediagroup loop " +
       
 10432 				"muted controls width height buffered", flowContent, "track source");
       
 10433 			add("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", flowContent, "track source");
       
 10434 			add("picture", "", "img source");
       
 10435 			add("source", "src srcset type media sizes");
       
 10436 			add("track", "kind src srclang label default");
       
 10437 			add("datalist", "", phrasingContent, "option");
       
 10438 			add("article section nav aside header footer", "", flowContent);
       
 10439 			add("hgroup", "", "h1 h2 h3 h4 h5 h6");
       
 10440 			add("figure", "", flowContent, "figcaption");
       
 10441 			add("time", "datetime", phrasingContent);
       
 10442 			add("dialog", "open", flowContent);
       
 10443 			add("command", "type label icon disabled checked radiogroup command");
       
 10444 			add("output", "for form name", phrasingContent);
       
 10445 			add("progress", "value max", phrasingContent);
       
 10446 			add("meter", "value min max low high optimum", phrasingContent);
       
 10447 			add("details", "open", flowContent, "summary");
       
 10448 			add("keygen", "autofocus challenge disabled form keytype name");
       
 10449 		}
       
 10450 
       
 10451 		// Extend with HTML4 attributes unless it's html5-strict
       
 10452 		if (type != "html5-strict") {
       
 10453 			addAttrs("script", "language xml:space");
       
 10454 			addAttrs("style", "xml:space");
       
 10455 			addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
       
 10456 			addAttrs("embed", "align name hspace vspace");
       
 10457 			addAttrs("param", "valuetype type");
       
 10458 			addAttrs("a", "charset name rev shape coords");
       
 10459 			addAttrs("br", "clear");
       
 10460 			addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
       
 10461 			addAttrs("img", "name longdesc align border hspace vspace");
       
 10462 			addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
       
 10463 			addAttrs("font basefont", "size color face");
       
 10464 			addAttrs("input", "usemap align");
       
 10465 			addAttrs("select", "onchange");
       
 10466 			addAttrs("textarea");
       
 10467 			addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
       
 10468 			addAttrs("ul", "type compact");
       
 10469 			addAttrs("li", "type");
       
 10470 			addAttrs("ol dl menu dir", "compact");
       
 10471 			addAttrs("pre", "width xml:space");
       
 10472 			addAttrs("hr", "align noshade size width");
       
 10473 			addAttrs("isindex", "prompt");
       
 10474 			addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
       
 10475 			addAttrs("col", "width align char charoff valign");
       
 10476 			addAttrs("colgroup", "width align char charoff valign");
       
 10477 			addAttrs("thead", "align char charoff valign");
       
 10478 			addAttrs("tr", "align char charoff valign bgcolor");
       
 10479 			addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
       
 10480 			addAttrs("form", "accept");
       
 10481 			addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
       
 10482 			addAttrs("tfoot", "align char charoff valign");
       
 10483 			addAttrs("tbody", "align char charoff valign");
       
 10484 			addAttrs("area", "nohref");
       
 10485 			addAttrs("body", "background bgcolor text link vlink alink");
       
 10486 		}
       
 10487 
       
 10488 		// Extend with HTML5 attributes unless it's html4
       
 10489 		if (type != "html4") {
       
 10490 			addAttrs("input button select textarea", "autofocus");
       
 10491 			addAttrs("input textarea", "placeholder");
       
 10492 			addAttrs("a", "download");
       
 10493 			addAttrs("link script img", "crossorigin");
       
 10494 			addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc
       
 10495 		}
       
 10496 
       
 10497 		// Special: iframe, ruby, video, audio, label
       
 10498 
       
 10499 		// Delete children of the same name from it's parent
       
 10500 		// For example: form can't have a child of the name form
       
 10501 		each(split('a form meter progress dfn'), function(name) {
       
 10502 			if (schema[name]) {
       
 10503 				delete schema[name].children[name];
       
 10504 			}
       
 10505 		});
       
 10506 
       
 10507 		// Delete header, footer, sectioning and heading content descendants
       
 10508 		/*each('dt th address', function(name) {
       
 10509 			delete schema[name].children[name];
       
 10510 		});*/
       
 10511 
       
 10512 		// Caption can't have tables
       
 10513 		delete schema.caption.children.table;
       
 10514 
       
 10515 		// TODO: LI:s can only have value if parent is OL
       
 10516 
       
 10517 		// TODO: Handle transparent elements
       
 10518 		// a ins del canvas map
       
 10519 
       
 10520 		mapCache[type] = schema;
       
 10521 
       
 10522 		return schema;
       
 10523 	}
       
 10524 
       
 10525 	function compileElementMap(value, mode) {
       
 10526 		var styles;
       
 10527 
       
 10528 		if (value) {
       
 10529 			styles = {};
       
 10530 
       
 10531 			if (typeof value == 'string') {
       
 10532 				value = {
       
 10533 					'*': value
       
 10534 				};
       
 10535 			}
       
 10536 
       
 10537 			// Convert styles into a rule list
       
 10538 			each(value, function(value, key) {
       
 10539 				styles[key] = styles[key.toUpperCase()] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/);
       
 10540 			});
       
 10541 		}
       
 10542 
       
 10543 		return styles;
       
 10544 	}
       
 10545 
       
 10546 	/**
       
 10547 	 * Constructs a new Schema instance.
       
 10548 	 *
       
 10549 	 * @constructor
       
 10550 	 * @method Schema
       
 10551 	 * @param {Object} settings Name/value settings object.
       
 10552 	 */
       
 10553 	return function(settings) {
       
 10554 		var self = this, elements = {}, children = {}, patternElements = [], validStyles, invalidStyles, schemaItems;
       
 10555 		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses;
       
 10556 		var blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap;
       
 10557 		var customElementsMap = {}, specialElements = {};
       
 10558 
       
 10559 		// Creates an lookup table map object for the specified option or the default value
       
 10560 		function createLookupTable(option, default_value, extendWith) {
       
 10561 			var value = settings[option];
       
 10562 
       
 10563 			if (!value) {
       
 10564 				// Get cached default map or make it if needed
       
 10565 				value = mapCache[option];
       
 10566 
       
 10567 				if (!value) {
       
 10568 					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
       
 10569 					value = extend(value, extendWith);
       
 10570 
       
 10571 					mapCache[option] = value;
       
 10572 				}
       
 10573 			} else {
       
 10574 				// Create custom map
       
 10575 				value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/));
       
 10576 			}
       
 10577 
       
 10578 			return value;
       
 10579 		}
       
 10580 
       
 10581 		settings = settings || {};
       
 10582 		schemaItems = compileSchema(settings.schema);
       
 10583 
       
 10584 		// Allow all elements and attributes if verify_html is set to false
       
 10585 		if (settings.verify_html === false) {
       
 10586 			settings.valid_elements = '*[*]';
       
 10587 		}
       
 10588 
       
 10589 		validStyles = compileElementMap(settings.valid_styles);
       
 10590 		invalidStyles = compileElementMap(settings.invalid_styles, 'map');
       
 10591 		validClasses = compileElementMap(settings.valid_classes, 'map');
       
 10592 
       
 10593 		// Setup map objects
       
 10594 		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
       
 10595 		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
       
 10596 		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
       
 10597 			'meta param embed source wbr track');
       
 10598 		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
       
 10599 			'noshade nowrap readonly selected autoplay loop controls');
       
 10600 		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
       
 10601 		moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap);
       
 10602 		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
       
 10603 						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
       
 10604 		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
       
 10605 						'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
       
 10606 						'datalist select optgroup', textBlockElementsMap);
       
 10607 		textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
       
 10608 										'dfn code mark q sup sub samp');
       
 10609 
       
 10610 		each((settings.special || 'script noscript style textarea').split(' '), function(name) {
       
 10611 			specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
       
 10612 		});
       
 10613 
       
 10614 		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
       
 10615 		function patternToRegExp(str) {
       
 10616 			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
       
 10617 		}
       
 10618 
       
 10619 		// Parses the specified valid_elements string and adds to the current rules
       
 10620 		// This function is a bit hard to read since it's heavily optimized for speed
       
 10621 		function addValidElements(validElements) {
       
 10622 			var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
       
 10623 				prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
       
 10624 				elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
       
 10625 				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
       
 10626 				hasPatternsRegExp = /[*?+]/;
       
 10627 
       
 10628 			if (validElements) {
       
 10629 				// Split valid elements into an array with rules
       
 10630 				validElements = split(validElements, ',');
       
 10631 
       
 10632 				if (elements['@']) {
       
 10633 					globalAttributes = elements['@'].attributes;
       
 10634 					globalAttributesOrder = elements['@'].attributesOrder;
       
 10635 				}
       
 10636 
       
 10637 				// Loop all rules
       
 10638 				for (ei = 0, el = validElements.length; ei < el; ei++) {
       
 10639 					// Parse element rule
       
 10640 					matches = elementRuleRegExp.exec(validElements[ei]);
       
 10641 					if (matches) {
       
 10642 						// Setup local names for matches
       
 10643 						prefix = matches[1];
       
 10644 						elementName = matches[2];
       
 10645 						outputName = matches[3];
       
 10646 						attrData = matches[5];
       
 10647 
       
 10648 						// Create new attributes and attributesOrder
       
 10649 						attributes = {};
       
 10650 						attributesOrder = [];
       
 10651 
       
 10652 						// Create the new element
       
 10653 						element = {
       
 10654 							attributes: attributes,
       
 10655 							attributesOrder: attributesOrder
       
 10656 						};
       
 10657 
       
 10658 						// Padd empty elements prefix
       
 10659 						if (prefix === '#') {
       
 10660 							element.paddEmpty = true;
       
 10661 						}
       
 10662 
       
 10663 						// Remove empty elements prefix
       
 10664 						if (prefix === '-') {
       
 10665 							element.removeEmpty = true;
       
 10666 						}
       
 10667 
       
 10668 						if (matches[4] === '!') {
       
 10669 							element.removeEmptyAttrs = true;
       
 10670 						}
       
 10671 
       
 10672 						// Copy attributes from global rule into current rule
       
 10673 						if (globalAttributes) {
       
 10674 							for (key in globalAttributes) {
       
 10675 								attributes[key] = globalAttributes[key];
       
 10676 							}
       
 10677 
       
 10678 							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
       
 10679 						}
       
 10680 
       
 10681 						// Attributes defined
       
 10682 						if (attrData) {
       
 10683 							attrData = split(attrData, '|');
       
 10684 							for (ai = 0, al = attrData.length; ai < al; ai++) {
       
 10685 								matches = attrRuleRegExp.exec(attrData[ai]);
       
 10686 								if (matches) {
       
 10687 									attr = {};
       
 10688 									attrType = matches[1];
       
 10689 									attrName = matches[2].replace(/::/g, ':');
       
 10690 									prefix = matches[3];
       
 10691 									value = matches[4];
       
 10692 
       
 10693 									// Required
       
 10694 									if (attrType === '!') {
       
 10695 										element.attributesRequired = element.attributesRequired || [];
       
 10696 										element.attributesRequired.push(attrName);
       
 10697 										attr.required = true;
       
 10698 									}
       
 10699 
       
 10700 									// Denied from global
       
 10701 									if (attrType === '-') {
       
 10702 										delete attributes[attrName];
       
 10703 										attributesOrder.splice(inArray(attributesOrder, attrName), 1);
       
 10704 										continue;
       
 10705 									}
       
 10706 
       
 10707 									// Default value
       
 10708 									if (prefix) {
       
 10709 										// Default value
       
 10710 										if (prefix === '=') {
       
 10711 											element.attributesDefault = element.attributesDefault || [];
       
 10712 											element.attributesDefault.push({name: attrName, value: value});
       
 10713 											attr.defaultValue = value;
       
 10714 										}
       
 10715 
       
 10716 										// Forced value
       
 10717 										if (prefix === ':') {
       
 10718 											element.attributesForced = element.attributesForced || [];
       
 10719 											element.attributesForced.push({name: attrName, value: value});
       
 10720 											attr.forcedValue = value;
       
 10721 										}
       
 10722 
       
 10723 										// Required values
       
 10724 										if (prefix === '<') {
       
 10725 											attr.validValues = makeMap(value, '?');
       
 10726 										}
       
 10727 									}
       
 10728 
       
 10729 									// Check for attribute patterns
       
 10730 									if (hasPatternsRegExp.test(attrName)) {
       
 10731 										element.attributePatterns = element.attributePatterns || [];
       
 10732 										attr.pattern = patternToRegExp(attrName);
       
 10733 										element.attributePatterns.push(attr);
       
 10734 									} else {
       
 10735 										// Add attribute to order list if it doesn't already exist
       
 10736 										if (!attributes[attrName]) {
       
 10737 											attributesOrder.push(attrName);
       
 10738 										}
       
 10739 
       
 10740 										attributes[attrName] = attr;
       
 10741 									}
       
 10742 								}
       
 10743 							}
       
 10744 						}
       
 10745 
       
 10746 						// Global rule, store away these for later usage
       
 10747 						if (!globalAttributes && elementName == '@') {
       
 10748 							globalAttributes = attributes;
       
 10749 							globalAttributesOrder = attributesOrder;
       
 10750 						}
       
 10751 
       
 10752 						// Handle substitute elements such as b/strong
       
 10753 						if (outputName) {
       
 10754 							element.outputName = elementName;
       
 10755 							elements[outputName] = element;
       
 10756 						}
       
 10757 
       
 10758 						// Add pattern or exact element
       
 10759 						if (hasPatternsRegExp.test(elementName)) {
       
 10760 							element.pattern = patternToRegExp(elementName);
       
 10761 							patternElements.push(element);
       
 10762 						} else {
       
 10763 							elements[elementName] = element;
       
 10764 						}
       
 10765 					}
       
 10766 				}
       
 10767 			}
       
 10768 		}
       
 10769 
       
 10770 		function setValidElements(validElements) {
       
 10771 			elements = {};
       
 10772 			patternElements = [];
       
 10773 
       
 10774 			addValidElements(validElements);
       
 10775 
       
 10776 			each(schemaItems, function(element, name) {
       
 10777 				children[name] = element.children;
       
 10778 			});
       
 10779 		}
       
 10780 
       
 10781 		// Adds custom non HTML elements to the schema
       
 10782 		function addCustomElements(customElements) {
       
 10783 			var customElementRegExp = /^(~)?(.+)$/;
       
 10784 
       
 10785 			if (customElements) {
       
 10786 				// Flush cached items since we are altering the default maps
       
 10787 				mapCache.text_block_elements = mapCache.block_elements = null;
       
 10788 
       
 10789 				each(split(customElements, ','), function(rule) {
       
 10790 					var matches = customElementRegExp.exec(rule),
       
 10791 						inline = matches[1] === '~',
       
 10792 						cloneName = inline ? 'span' : 'div',
       
 10793 						name = matches[2];
       
 10794 
       
 10795 					children[name] = children[cloneName];
       
 10796 					customElementsMap[name] = cloneName;
       
 10797 
       
 10798 					// If it's not marked as inline then add it to valid block elements
       
 10799 					if (!inline) {
       
 10800 						blockElementsMap[name.toUpperCase()] = {};
       
 10801 						blockElementsMap[name] = {};
       
 10802 					}
       
 10803 
       
 10804 					// Add elements clone if needed
       
 10805 					if (!elements[name]) {
       
 10806 						var customRule = elements[cloneName];
       
 10807 
       
 10808 						customRule = extend({}, customRule);
       
 10809 						delete customRule.removeEmptyAttrs;
       
 10810 						delete customRule.removeEmpty;
       
 10811 
       
 10812 						elements[name] = customRule;
       
 10813 					}
       
 10814 
       
 10815 					// Add custom elements at span/div positions
       
 10816 					each(children, function(element, elmName) {
       
 10817 						if (element[cloneName]) {
       
 10818 							children[elmName] = element = extend({}, children[elmName]);
       
 10819 							element[name] = element[cloneName];
       
 10820 						}
       
 10821 					});
       
 10822 				});
       
 10823 			}
       
 10824 		}
       
 10825 
       
 10826 		// Adds valid children to the schema object
       
 10827 		function addValidChildren(validChildren) {
       
 10828 			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
       
 10829 
       
 10830 			if (validChildren) {
       
 10831 				each(split(validChildren, ','), function(rule) {
       
 10832 					var matches = childRuleRegExp.exec(rule), parent, prefix;
       
 10833 
       
 10834 					if (matches) {
       
 10835 						prefix = matches[1];
       
 10836 
       
 10837 						// Add/remove items from default
       
 10838 						if (prefix) {
       
 10839 							parent = children[matches[2]];
       
 10840 						} else {
       
 10841 							parent = children[matches[2]] = {'#comment': {}};
       
 10842 						}
       
 10843 
       
 10844 						parent = children[matches[2]];
       
 10845 
       
 10846 						each(split(matches[3], '|'), function(child) {
       
 10847 							if (prefix === '-') {
       
 10848 								// Clone the element before we delete
       
 10849 								// things in it to not mess up default schemas
       
 10850 								children[matches[2]] = parent = extend({}, children[matches[2]]);
       
 10851 
       
 10852 								delete parent[child];
       
 10853 							} else {
       
 10854 								parent[child] = {};
       
 10855 							}
       
 10856 						});
       
 10857 					}
       
 10858 				});
       
 10859 			}
       
 10860 		}
       
 10861 
       
 10862 		function getElementRule(name) {
       
 10863 			var element = elements[name], i;
       
 10864 
       
 10865 			// Exact match found
       
 10866 			if (element) {
       
 10867 				return element;
       
 10868 			}
       
 10869 
       
 10870 			// No exact match then try the patterns
       
 10871 			i = patternElements.length;
       
 10872 			while (i--) {
       
 10873 				element = patternElements[i];
       
 10874 
       
 10875 				if (element.pattern.test(name)) {
       
 10876 					return element;
       
 10877 				}
       
 10878 			}
       
 10879 		}
       
 10880 
       
 10881 		if (!settings.valid_elements) {
       
 10882 			// No valid elements defined then clone the elements from the schema spec
       
 10883 			each(schemaItems, function(element, name) {
       
 10884 				elements[name] = {
       
 10885 					attributes: element.attributes,
       
 10886 					attributesOrder: element.attributesOrder
       
 10887 				};
       
 10888 
       
 10889 				children[name] = element.children;
       
 10890 			});
       
 10891 
       
 10892 			// Switch these on HTML4
       
 10893 			if (settings.schema != "html5") {
       
 10894 				each(split('strong/b em/i'), function(item) {
       
 10895 					item = split(item, '/');
       
 10896 					elements[item[1]].outputName = item[0];
       
 10897 				});
       
 10898 			}
       
 10899 
       
 10900 			// Add default alt attribute for images
       
 10901 			elements.img.attributesDefault = [{name: 'alt', value: ''}];
       
 10902 
       
 10903 			// Remove these if they are empty by default
       
 10904 			each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
       
 10905 				if (elements[name]) {
       
 10906 					elements[name].removeEmpty = true;
       
 10907 				}
       
 10908 			});
       
 10909 
       
 10910 			// Padd these by default
       
 10911 			each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
       
 10912 				elements[name].paddEmpty = true;
       
 10913 			});
       
 10914 
       
 10915 			// Remove these if they have no attributes
       
 10916 			each(split('span'), function(name) {
       
 10917 				elements[name].removeEmptyAttrs = true;
       
 10918 			});
       
 10919 
       
 10920 			// Remove these by default
       
 10921 			// TODO: Reenable in 4.1
       
 10922 			/*each(split('script style'), function(name) {
       
 10923 				delete elements[name];
       
 10924 			});*/
       
 10925 		} else {
       
 10926 			setValidElements(settings.valid_elements);
       
 10927 		}
       
 10928 
       
 10929 		addCustomElements(settings.custom_elements);
       
 10930 		addValidChildren(settings.valid_children);
       
 10931 		addValidElements(settings.extended_valid_elements);
       
 10932 
       
 10933 		// Todo: Remove this when we fix list handling to be valid
       
 10934 		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
       
 10935 
       
 10936 		// Delete invalid elements
       
 10937 		if (settings.invalid_elements) {
       
 10938 			each(explode(settings.invalid_elements), function(item) {
       
 10939 				if (elements[item]) {
       
 10940 					delete elements[item];
       
 10941 				}
       
 10942 			});
       
 10943 		}
       
 10944 
       
 10945 		// If the user didn't allow span only allow internal spans
       
 10946 		if (!getElementRule('span')) {
       
 10947 			addValidElements('span[!data-mce-type|*]');
       
 10948 		}
       
 10949 
       
 10950 		/**
       
 10951 		 * Name/value map object with valid parents and children to those parents.
       
 10952 		 *
       
 10953 		 * @example
       
 10954 		 * children = {
       
 10955 		 *    div:{p:{}, h1:{}}
       
 10956 		 * };
       
 10957 		 * @field children
       
 10958 		 * @type Object
       
 10959 		 */
       
 10960 		self.children = children;
       
 10961 
       
 10962 		/**
       
 10963 		 * Name/value map object with valid styles for each element.
       
 10964 		 *
       
 10965 		 * @method getValidStyles
       
 10966 		 * @type Object
       
 10967 		 */
       
 10968 		self.getValidStyles = function() {
       
 10969 			return validStyles;
       
 10970 		};
       
 10971 
       
 10972 		/**
       
 10973 		 * Name/value map object with valid styles for each element.
       
 10974 		 *
       
 10975 		 * @method getInvalidStyles
       
 10976 		 * @type Object
       
 10977 		 */
       
 10978 		self.getInvalidStyles = function() {
       
 10979 			return invalidStyles;
       
 10980 		};
       
 10981 
       
 10982 		/**
       
 10983 		 * Name/value map object with valid classes for each element.
       
 10984 		 *
       
 10985 		 * @method getValidClasses
       
 10986 		 * @type Object
       
 10987 		 */
       
 10988 		self.getValidClasses = function() {
       
 10989 			return validClasses;
       
 10990 		};
       
 10991 
       
 10992 		/**
       
 10993 		 * Returns a map with boolean attributes.
       
 10994 		 *
       
 10995 		 * @method getBoolAttrs
       
 10996 		 * @return {Object} Name/value lookup map for boolean attributes.
       
 10997 		 */
       
 10998 		self.getBoolAttrs = function() {
       
 10999 			return boolAttrMap;
       
 11000 		};
       
 11001 
       
 11002 		/**
       
 11003 		 * Returns a map with block elements.
       
 11004 		 *
       
 11005 		 * @method getBlockElements
       
 11006 		 * @return {Object} Name/value lookup map for block elements.
       
 11007 		 */
       
 11008 		self.getBlockElements = function() {
       
 11009 			return blockElementsMap;
       
 11010 		};
       
 11011 
       
 11012 		/**
       
 11013 		 * Returns a map with text block elements. Such as: p,h1-h6,div,address
       
 11014 		 *
       
 11015 		 * @method getTextBlockElements
       
 11016 		 * @return {Object} Name/value lookup map for block elements.
       
 11017 		 */
       
 11018 		self.getTextBlockElements = function() {
       
 11019 			return textBlockElementsMap;
       
 11020 		};
       
 11021 
       
 11022 		/**
       
 11023 		 * Returns a map of inline text format nodes for example strong/span or ins.
       
 11024 		 *
       
 11025 		 * @method getTextInlineElements
       
 11026 		 * @return {Object} Name/value lookup map for text format elements.
       
 11027 		 */
       
 11028 		self.getTextInlineElements = function() {
       
 11029 			return textInlineElementsMap;
       
 11030 		};
       
 11031 
       
 11032 		/**
       
 11033 		 * Returns a map with short ended elements such as BR or IMG.
       
 11034 		 *
       
 11035 		 * @method getShortEndedElements
       
 11036 		 * @return {Object} Name/value lookup map for short ended elements.
       
 11037 		 */
       
 11038 		self.getShortEndedElements = function() {
       
 11039 			return shortEndedElementsMap;
       
 11040 		};
       
 11041 
       
 11042 		/**
       
 11043 		 * Returns a map with self closing tags such as <li>.
       
 11044 		 *
       
 11045 		 * @method getSelfClosingElements
       
 11046 		 * @return {Object} Name/value lookup map for self closing tags elements.
       
 11047 		 */
       
 11048 		self.getSelfClosingElements = function() {
       
 11049 			return selfClosingElementsMap;
       
 11050 		};
       
 11051 
       
 11052 		/**
       
 11053 		 * Returns a map with elements that should be treated as contents regardless if it has text
       
 11054 		 * content in them or not such as TD, VIDEO or IMG.
       
 11055 		 *
       
 11056 		 * @method getNonEmptyElements
       
 11057 		 * @return {Object} Name/value lookup map for non empty elements.
       
 11058 		 */
       
 11059 		self.getNonEmptyElements = function() {
       
 11060 			return nonEmptyElementsMap;
       
 11061 		};
       
 11062 
       
 11063 		/**
       
 11064 		 * Returns a map with elements that the caret should be moved in front of after enter is
       
 11065 		 * pressed
       
 11066 		 *
       
 11067 		 * @method getMoveCaretBeforeOnEnterElements
       
 11068 		 * @return {Object} Name/value lookup map for elements to place the caret in front of.
       
 11069 		 */
       
 11070 		self.getMoveCaretBeforeOnEnterElements = function() {
       
 11071 			return moveCaretBeforeOnEnterElementsMap;
       
 11072 		};
       
 11073 
       
 11074 		/**
       
 11075 		 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
       
 11076 		 *
       
 11077 		 * @method getWhiteSpaceElements
       
 11078 		 * @return {Object} Name/value lookup map for white space elements.
       
 11079 		 */
       
 11080 		self.getWhiteSpaceElements = function() {
       
 11081 			return whiteSpaceElementsMap;
       
 11082 		};
       
 11083 
       
 11084 		/**
       
 11085 		 * Returns a map with special elements. These are elements that needs to be parsed
       
 11086 		 * in a special way such as script, style, textarea etc. The map object values
       
 11087 		 * are regexps used to find the end of the element.
       
 11088 		 *
       
 11089 		 * @method getSpecialElements
       
 11090 		 * @return {Object} Name/value lookup map for special elements.
       
 11091 		 */
       
 11092 		self.getSpecialElements = function() {
       
 11093 			return specialElements;
       
 11094 		};
       
 11095 
       
 11096 		/**
       
 11097 		 * Returns true/false if the specified element and it's child is valid or not
       
 11098 		 * according to the schema.
       
 11099 		 *
       
 11100 		 * @method isValidChild
       
 11101 		 * @param {String} name Element name to check for.
       
 11102 		 * @param {String} child Element child to verify.
       
 11103 		 * @return {Boolean} True/false if the element is a valid child of the specified parent.
       
 11104 		 */
       
 11105 		self.isValidChild = function(name, child) {
       
 11106 			var parent = children[name];
       
 11107 
       
 11108 			return !!(parent && parent[child]);
       
 11109 		};
       
 11110 
       
 11111 		/**
       
 11112 		 * Returns true/false if the specified element name and optional attribute is
       
 11113 		 * valid according to the schema.
       
 11114 		 *
       
 11115 		 * @method isValid
       
 11116 		 * @param {String} name Name of element to check.
       
 11117 		 * @param {String} attr Optional attribute name to check for.
       
 11118 		 * @return {Boolean} True/false if the element and attribute is valid.
       
 11119 		 */
       
 11120 		self.isValid = function(name, attr) {
       
 11121 			var attrPatterns, i, rule = getElementRule(name);
       
 11122 
       
 11123 			// Check if it's a valid element
       
 11124 			if (rule) {
       
 11125 				if (attr) {
       
 11126 					// Check if attribute name exists
       
 11127 					if (rule.attributes[attr]) {
       
 11128 						return true;
       
 11129 					}
       
 11130 
       
 11131 					// Check if attribute matches a regexp pattern
       
 11132 					attrPatterns = rule.attributePatterns;
       
 11133 					if (attrPatterns) {
       
 11134 						i = attrPatterns.length;
       
 11135 						while (i--) {
       
 11136 							if (attrPatterns[i].pattern.test(name)) {
       
 11137 								return true;
       
 11138 							}
       
 11139 						}
       
 11140 					}
       
 11141 				} else {
       
 11142 					return true;
       
 11143 				}
       
 11144 			}
       
 11145 
       
 11146 			// No match
       
 11147 			return false;
       
 11148 		};
       
 11149 
       
 11150 		/**
       
 11151 		 * Returns true/false if the specified element is valid or not
       
 11152 		 * according to the schema.
       
 11153 		 *
       
 11154 		 * @method getElementRule
       
 11155 		 * @param {String} name Element name to check for.
       
 11156 		 * @return {Object} Element object or undefined if the element isn't valid.
       
 11157 		 */
       
 11158 		self.getElementRule = getElementRule;
       
 11159 
       
 11160 		/**
       
 11161 		 * Returns an map object of all custom elements.
       
 11162 		 *
       
 11163 		 * @method getCustomElements
       
 11164 		 * @return {Object} Name/value map object of all custom elements.
       
 11165 		 */
       
 11166 		self.getCustomElements = function() {
       
 11167 			return customElementsMap;
       
 11168 		};
       
 11169 
       
 11170 		/**
       
 11171 		 * Parses a valid elements string and adds it to the schema. The valid elements
       
 11172 		 * format is for example "element[attr=default|otherattr]".
       
 11173 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
       
 11174 		 *
       
 11175 		 * @method addValidElements
       
 11176 		 * @param {String} valid_elements String in the valid elements format to be parsed.
       
 11177 		 */
       
 11178 		self.addValidElements = addValidElements;
       
 11179 
       
 11180 		/**
       
 11181 		 * Parses a valid elements string and sets it to the schema. The valid elements
       
 11182 		 * format is for example "element[attr=default|otherattr]".
       
 11183 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
       
 11184 		 *
       
 11185 		 * @method setValidElements
       
 11186 		 * @param {String} valid_elements String in the valid elements format to be parsed.
       
 11187 		 */
       
 11188 		self.setValidElements = setValidElements;
       
 11189 
       
 11190 		/**
       
 11191 		 * Adds custom non HTML elements to the schema.
       
 11192 		 *
       
 11193 		 * @method addCustomElements
       
 11194 		 * @param {String} custom_elements Comma separated list of custom elements to add.
       
 11195 		 */
       
 11196 		self.addCustomElements = addCustomElements;
       
 11197 
       
 11198 		/**
       
 11199 		 * Parses a valid children string and adds them to the schema structure. The valid children
       
 11200 		 * format is for example: "element[child1|child2]".
       
 11201 		 *
       
 11202 		 * @method addValidChildren
       
 11203 		 * @param {String} valid_children Valid children elements string to parse
       
 11204 		 */
       
 11205 		self.addValidChildren = addValidChildren;
       
 11206 
       
 11207 		self.elements = elements;
       
 11208 	};
       
 11209 });
       
 11210 
       
 11211 // Included from: js/tinymce/classes/html/SaxParser.js
       
 11212 
       
 11213 /**
       
 11214  * SaxParser.js
       
 11215  *
       
 11216  * Copyright, Moxiecode Systems AB
       
 11217  * Released under LGPL License.
       
 11218  *
       
 11219  * License: http://www.tinymce.com/license
       
 11220  * Contributing: http://www.tinymce.com/contributing
       
 11221  */
       
 11222 
       
 11223 /*eslint max-depth:[2, 9] */
       
 11224 
       
 11225 /**
       
 11226  * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
       
 11227  * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
       
 11228  * and attributes that doesn't fit the schema if the validate setting is enabled.
       
 11229  *
       
 11230  * @example
       
 11231  * var parser = new tinymce.html.SaxParser({
       
 11232  *     validate: true,
       
 11233  *
       
 11234  *     comment: function(text) {
       
 11235  *         console.log('Comment:', text);
       
 11236  *     },
       
 11237  *
       
 11238  *     cdata: function(text) {
       
 11239  *         console.log('CDATA:', text);
       
 11240  *     },
       
 11241  *
       
 11242  *     text: function(text, raw) {
       
 11243  *         console.log('Text:', text, 'Raw:', raw);
       
 11244  *     },
       
 11245  *
       
 11246  *     start: function(name, attrs, empty) {
       
 11247  *         console.log('Start:', name, attrs, empty);
       
 11248  *     },
       
 11249  *
       
 11250  *     end: function(name) {
       
 11251  *         console.log('End:', name);
       
 11252  *     },
       
 11253  *
       
 11254  *     pi: function(name, text) {
       
 11255  *         console.log('PI:', name, text);
       
 11256  *     },
       
 11257  *
       
 11258  *     doctype: function(text) {
       
 11259  *         console.log('DocType:', text);
       
 11260  *     }
       
 11261  * }, schema);
       
 11262  * @class tinymce.html.SaxParser
       
 11263  * @version 3.4
       
 11264  */
       
 11265 define("tinymce/html/SaxParser", [
       
 11266 	"tinymce/html/Schema",
       
 11267 	"tinymce/html/Entities",
       
 11268 	"tinymce/util/Tools"
       
 11269 ], function(Schema, Entities, Tools) {
       
 11270 	var each = Tools.each;
       
 11271 
       
 11272 	/**
       
 11273 	 * Returns the index of the end tag for a specific start tag. This can be
       
 11274 	 * used to skip all children of a parent element from being processed.
       
 11275 	 *
       
 11276 	 * @private
       
 11277 	 * @method findEndTag
       
 11278 	 * @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements.
       
 11279 	 * @param {String} html HTML string to find the end tag in.
       
 11280 	 * @param {Number} startIndex Indext to start searching at should be after the start tag.
       
 11281 	 * @return {Number} Index of the end tag.
       
 11282 	 */
       
 11283 	function findEndTag(schema, html, startIndex) {
       
 11284 		var count = 1, index, matches, tokenRegExp, shortEndedElements;
       
 11285 
       
 11286 		shortEndedElements = schema.getShortEndedElements();
       
 11287 		tokenRegExp = /<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
       
 11288 		tokenRegExp.lastIndex = index = startIndex;
       
 11289 
       
 11290 		while ((matches = tokenRegExp.exec(html))) {
       
 11291 			index = tokenRegExp.lastIndex;
       
 11292 
       
 11293 			if (matches[1] === '/') { // End element
       
 11294 				count--;
       
 11295 			} else if (!matches[1]) { // Start element
       
 11296 				if (matches[2] in shortEndedElements) {
       
 11297 					continue;
       
 11298 				}
       
 11299 
       
 11300 				count++;
       
 11301 			}
       
 11302 
       
 11303 			if (count === 0) {
       
 11304 				break;
       
 11305 			}
       
 11306 		}
       
 11307 
       
 11308 		return index;
       
 11309 	}
       
 11310 
       
 11311 	/**
       
 11312 	 * Constructs a new SaxParser instance.
       
 11313 	 *
       
 11314 	 * @constructor
       
 11315 	 * @method SaxParser
       
 11316 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
       
 11317 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
       
 11318 	 */
       
 11319 	function SaxParser(settings, schema) {
       
 11320 		var self = this;
       
 11321 
       
 11322 		function noop() {}
       
 11323 
       
 11324 		settings = settings || {};
       
 11325 		self.schema = schema = schema || new Schema();
       
 11326 
       
 11327 		if (settings.fix_self_closing !== false) {
       
 11328 			settings.fix_self_closing = true;
       
 11329 		}
       
 11330 
       
 11331 		// Add handler functions from settings and setup default handlers
       
 11332 		each('comment cdata text start end pi doctype'.split(' '), function(name) {
       
 11333 			if (name) {
       
 11334 				self[name] = settings[name] || noop;
       
 11335 			}
       
 11336 		});
       
 11337 
       
 11338 		/**
       
 11339 		 * Parses the specified HTML string and executes the callbacks for each item it finds.
       
 11340 		 *
       
 11341 		 * @example
       
 11342 		 * new SaxParser({...}).parse('<b>text</b>');
       
 11343 		 * @method parse
       
 11344 		 * @param {String} html Html string to sax parse.
       
 11345 		 */
       
 11346 		self.parse = function(html) {
       
 11347 			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
       
 11348 			var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
       
 11349 			var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
       
 11350 			var attributesRequired, attributesDefault, attributesForced;
       
 11351 			var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
       
 11352 			var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster');
       
 11353 			var scriptUriRegExp = /((java|vb)script|mhtml):/i, dataUriRegExp = /^data:/i;
       
 11354 
       
 11355 			function processEndTag(name) {
       
 11356 				var pos, i;
       
 11357 
       
 11358 				// Find position of parent of the same type
       
 11359 				pos = stack.length;
       
 11360 				while (pos--) {
       
 11361 					if (stack[pos].name === name) {
       
 11362 						break;
       
 11363 					}
       
 11364 				}
       
 11365 
       
 11366 				// Found parent
       
 11367 				if (pos >= 0) {
       
 11368 					// Close all the open elements
       
 11369 					for (i = stack.length - 1; i >= pos; i--) {
       
 11370 						name = stack[i];
       
 11371 
       
 11372 						if (name.valid) {
       
 11373 							self.end(name.name);
       
 11374 						}
       
 11375 					}
       
 11376 
       
 11377 					// Remove the open elements from the stack
       
 11378 					stack.length = pos;
       
 11379 				}
       
 11380 			}
       
 11381 
       
 11382 			function parseAttribute(match, name, value, val2, val3) {
       
 11383 				var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
       
 11384 
       
 11385 				name = name.toLowerCase();
       
 11386 				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
       
 11387 
       
 11388 				// Validate name and value pass through all data- attributes
       
 11389 				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
       
 11390 					attrRule = validAttributesMap[name];
       
 11391 
       
 11392 					// Find rule by pattern matching
       
 11393 					if (!attrRule && validAttributePatterns) {
       
 11394 						i = validAttributePatterns.length;
       
 11395 						while (i--) {
       
 11396 							attrRule = validAttributePatterns[i];
       
 11397 							if (attrRule.pattern.test(name)) {
       
 11398 								break;
       
 11399 							}
       
 11400 						}
       
 11401 
       
 11402 						// No rule matched
       
 11403 						if (i === -1) {
       
 11404 							attrRule = null;
       
 11405 						}
       
 11406 					}
       
 11407 
       
 11408 					// No attribute rule found
       
 11409 					if (!attrRule) {
       
 11410 						return;
       
 11411 					}
       
 11412 
       
 11413 					// Validate value
       
 11414 					if (attrRule.validValues && !(value in attrRule.validValues)) {
       
 11415 						return;
       
 11416 					}
       
 11417 				}
       
 11418 
       
 11419 				// Block any javascript: urls or non image data uris
       
 11420 				if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
       
 11421 					var uri = value.replace(trimRegExp, '');
       
 11422 
       
 11423 					try {
       
 11424 						// Might throw malformed URI sequence
       
 11425 						uri = decodeURIComponent(uri);
       
 11426 					} catch (ex) {
       
 11427 						// Fallback to non UTF-8 decoder
       
 11428 						uri = unescape(uri);
       
 11429 					}
       
 11430 
       
 11431 					if (scriptUriRegExp.test(uri)) {
       
 11432 						return;
       
 11433 					}
       
 11434 
       
 11435 					if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) {
       
 11436 						return;
       
 11437 					}
       
 11438 				}
       
 11439 
       
 11440 				// Add attribute to list and map
       
 11441 				attrList.map[name] = value;
       
 11442 				attrList.push({
       
 11443 					name: name,
       
 11444 					value: value
       
 11445 				});
       
 11446 			}
       
 11447 
       
 11448 			// Precompile RegExps and map objects
       
 11449 			tokenRegExp = new RegExp('<(?:' +
       
 11450 				'(?:!--([\\w\\W]*?)-->)|' + // Comment
       
 11451 				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
       
 11452 				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
       
 11453 				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
       
 11454 				'(?:\\/([^>]+)>)|' + // End element
       
 11455 				'(?:([A-Za-z0-9\\-_\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
       
 11456 			')', 'g');
       
 11457 
       
 11458 			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
       
 11459 
       
 11460 			// Setup lookup tables for empty elements and boolean attributes
       
 11461 			shortEndedElements = schema.getShortEndedElements();
       
 11462 			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
       
 11463 			fillAttrsMap = schema.getBoolAttrs();
       
 11464 			validate = settings.validate;
       
 11465 			removeInternalElements = settings.remove_internals;
       
 11466 			fixSelfClosing = settings.fix_self_closing;
       
 11467 			specialElements = schema.getSpecialElements();
       
 11468 
       
 11469 			while ((matches = tokenRegExp.exec(html))) {
       
 11470 				// Text
       
 11471 				if (index < matches.index) {
       
 11472 					self.text(decode(html.substr(index, matches.index - index)));
       
 11473 				}
       
 11474 
       
 11475 				if ((value = matches[6])) { // End element
       
 11476 					value = value.toLowerCase();
       
 11477 
       
 11478 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
       
 11479 					if (value.charAt(0) === ':') {
       
 11480 						value = value.substr(1);
       
 11481 					}
       
 11482 
       
 11483 					processEndTag(value);
       
 11484 				} else if ((value = matches[7])) { // Start element
       
 11485 					value = value.toLowerCase();
       
 11486 
       
 11487 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
       
 11488 					if (value.charAt(0) === ':') {
       
 11489 						value = value.substr(1);
       
 11490 					}
       
 11491 
       
 11492 					isShortEnded = value in shortEndedElements;
       
 11493 
       
 11494 					// Is self closing tag for example an <li> after an open <li>
       
 11495 					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
       
 11496 						processEndTag(value);
       
 11497 					}
       
 11498 
       
 11499 					// Validate element
       
 11500 					if (!validate || (elementRule = schema.getElementRule(value))) {
       
 11501 						isValidElement = true;
       
 11502 
       
 11503 						// Grab attributes map and patters when validation is enabled
       
 11504 						if (validate) {
       
 11505 							validAttributesMap = elementRule.attributes;
       
 11506 							validAttributePatterns = elementRule.attributePatterns;
       
 11507 						}
       
 11508 
       
 11509 						// Parse attributes
       
 11510 						if ((attribsValue = matches[8])) {
       
 11511 							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
       
 11512 
       
 11513 							// If the element has internal attributes then remove it if we are told to do so
       
 11514 							if (isInternalElement && removeInternalElements) {
       
 11515 								isValidElement = false;
       
 11516 							}
       
 11517 
       
 11518 							attrList = [];
       
 11519 							attrList.map = {};
       
 11520 
       
 11521 							attribsValue.replace(attrRegExp, parseAttribute);
       
 11522 						} else {
       
 11523 							attrList = [];
       
 11524 							attrList.map = {};
       
 11525 						}
       
 11526 
       
 11527 						// Process attributes if validation is enabled
       
 11528 						if (validate && !isInternalElement) {
       
 11529 							attributesRequired = elementRule.attributesRequired;
       
 11530 							attributesDefault = elementRule.attributesDefault;
       
 11531 							attributesForced = elementRule.attributesForced;
       
 11532 							anyAttributesRequired = elementRule.removeEmptyAttrs;
       
 11533 
       
 11534 							// Check if any attribute exists
       
 11535 							if (anyAttributesRequired && !attrList.length) {
       
 11536 								isValidElement = false;
       
 11537 							}
       
 11538 
       
 11539 							// Handle forced attributes
       
 11540 							if (attributesForced) {
       
 11541 								i = attributesForced.length;
       
 11542 								while (i--) {
       
 11543 									attr = attributesForced[i];
       
 11544 									name = attr.name;
       
 11545 									attrValue = attr.value;
       
 11546 
       
 11547 									if (attrValue === '{$uid}') {
       
 11548 										attrValue = 'mce_' + idCount++;
       
 11549 									}
       
 11550 
       
 11551 									attrList.map[name] = attrValue;
       
 11552 									attrList.push({name: name, value: attrValue});
       
 11553 								}
       
 11554 							}
       
 11555 
       
 11556 							// Handle default attributes
       
 11557 							if (attributesDefault) {
       
 11558 								i = attributesDefault.length;
       
 11559 								while (i--) {
       
 11560 									attr = attributesDefault[i];
       
 11561 									name = attr.name;
       
 11562 
       
 11563 									if (!(name in attrList.map)) {
       
 11564 										attrValue = attr.value;
       
 11565 
       
 11566 										if (attrValue === '{$uid}') {
       
 11567 											attrValue = 'mce_' + idCount++;
       
 11568 										}
       
 11569 
       
 11570 										attrList.map[name] = attrValue;
       
 11571 										attrList.push({name: name, value: attrValue});
       
 11572 									}
       
 11573 								}
       
 11574 							}
       
 11575 
       
 11576 							// Handle required attributes
       
 11577 							if (attributesRequired) {
       
 11578 								i = attributesRequired.length;
       
 11579 								while (i--) {
       
 11580 									if (attributesRequired[i] in attrList.map) {
       
 11581 										break;
       
 11582 									}
       
 11583 								}
       
 11584 
       
 11585 								// None of the required attributes where found
       
 11586 								if (i === -1) {
       
 11587 									isValidElement = false;
       
 11588 								}
       
 11589 							}
       
 11590 
       
 11591 							// Invalidate element if it's marked as bogus
       
 11592 							if ((attr = attrList.map['data-mce-bogus'])) {
       
 11593 								if (attr === 'all') {
       
 11594 									index = findEndTag(schema, html, tokenRegExp.lastIndex);
       
 11595 									tokenRegExp.lastIndex = index;
       
 11596 									continue;
       
 11597 								}
       
 11598 
       
 11599 								isValidElement = false;
       
 11600 							}
       
 11601 						}
       
 11602 
       
 11603 						if (isValidElement) {
       
 11604 							self.start(value, attrList, isShortEnded);
       
 11605 						}
       
 11606 					} else {
       
 11607 						isValidElement = false;
       
 11608 					}
       
 11609 
       
 11610 					// Treat script, noscript and style a bit different since they may include code that looks like elements
       
 11611 					if ((endRegExp = specialElements[value])) {
       
 11612 						endRegExp.lastIndex = index = matches.index + matches[0].length;
       
 11613 
       
 11614 						if ((matches = endRegExp.exec(html))) {
       
 11615 							if (isValidElement) {
       
 11616 								text = html.substr(index, matches.index - index);
       
 11617 							}
       
 11618 
       
 11619 							index = matches.index + matches[0].length;
       
 11620 						} else {
       
 11621 							text = html.substr(index);
       
 11622 							index = html.length;
       
 11623 						}
       
 11624 
       
 11625 						if (isValidElement) {
       
 11626 							if (text.length > 0) {
       
 11627 								self.text(text, true);
       
 11628 							}
       
 11629 
       
 11630 							self.end(value);
       
 11631 						}
       
 11632 
       
 11633 						tokenRegExp.lastIndex = index;
       
 11634 						continue;
       
 11635 					}
       
 11636 
       
 11637 					// Push value on to stack
       
 11638 					if (!isShortEnded) {
       
 11639 						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
       
 11640 							stack.push({name: value, valid: isValidElement});
       
 11641 						} else if (isValidElement) {
       
 11642 							self.end(value);
       
 11643 						}
       
 11644 					}
       
 11645 				} else if ((value = matches[1])) { // Comment
       
 11646 					// Padd comment value to avoid browsers from parsing invalid comments as HTML
       
 11647 					if (value.charAt(0) === '>') {
       
 11648 						value = ' ' + value;
       
 11649 					}
       
 11650 
       
 11651 					if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
       
 11652 						value = ' ' + value;
       
 11653 					}
       
 11654 
       
 11655 					self.comment(value);
       
 11656 				} else if ((value = matches[2])) { // CDATA
       
 11657 					self.cdata(value);
       
 11658 				} else if ((value = matches[3])) { // DOCTYPE
       
 11659 					self.doctype(value);
       
 11660 				} else if ((value = matches[4])) { // PI
       
 11661 					self.pi(value, matches[5]);
       
 11662 				}
       
 11663 
       
 11664 				index = matches.index + matches[0].length;
       
 11665 			}
       
 11666 
       
 11667 			// Text
       
 11668 			if (index < html.length) {
       
 11669 				self.text(decode(html.substr(index)));
       
 11670 			}
       
 11671 
       
 11672 			// Close any open elements
       
 11673 			for (i = stack.length - 1; i >= 0; i--) {
       
 11674 				value = stack[i];
       
 11675 
       
 11676 				if (value.valid) {
       
 11677 					self.end(value.name);
       
 11678 				}
       
 11679 			}
       
 11680 		};
       
 11681 	}
       
 11682 
       
 11683 	SaxParser.findEndTag = findEndTag;
       
 11684 
       
 11685 	return SaxParser;
       
 11686 });
       
 11687 
       
 11688 // Included from: js/tinymce/classes/html/DomParser.js
       
 11689 
       
 11690 /**
       
 11691  * DomParser.js
       
 11692  *
       
 11693  * Copyright, Moxiecode Systems AB
       
 11694  * Released under LGPL License.
       
 11695  *
       
 11696  * License: http://www.tinymce.com/license
       
 11697  * Contributing: http://www.tinymce.com/contributing
       
 11698  */
       
 11699 
       
 11700 /**
       
 11701  * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
       
 11702  * sure that the node tree is valid according to the specified schema.
       
 11703  * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
       
 11704  *
       
 11705  * @example
       
 11706  * var parser = new tinymce.html.DomParser({validate: true}, schema);
       
 11707  * var rootNode = parser.parse('<h1>content</h1>');
       
 11708  *
       
 11709  * @class tinymce.html.DomParser
       
 11710  * @version 3.4
       
 11711  */
       
 11712 define("tinymce/html/DomParser", [
       
 11713 	"tinymce/html/Node",
       
 11714 	"tinymce/html/Schema",
       
 11715 	"tinymce/html/SaxParser",
       
 11716 	"tinymce/util/Tools"
       
 11717 ], function(Node, Schema, SaxParser, Tools) {
       
 11718 	var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
       
 11719 
       
 11720 	/**
       
 11721 	 * Constructs a new DomParser instance.
       
 11722 	 *
       
 11723 	 * @constructor
       
 11724 	 * @method DomParser
       
 11725 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
       
 11726 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
       
 11727 	 */
       
 11728 	return function(settings, schema) {
       
 11729 		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
       
 11730 
       
 11731 		settings = settings || {};
       
 11732 		settings.validate = "validate" in settings ? settings.validate : true;
       
 11733 		settings.root_name = settings.root_name || 'body';
       
 11734 		self.schema = schema = schema || new Schema();
       
 11735 
       
 11736 		function fixInvalidChildren(nodes) {
       
 11737 			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
       
 11738 			var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
       
 11739 
       
 11740 			nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
       
 11741 			nonEmptyElements = schema.getNonEmptyElements();
       
 11742 			textBlockElements = schema.getTextBlockElements();
       
 11743 
       
 11744 			for (ni = 0; ni < nodes.length; ni++) {
       
 11745 				node = nodes[ni];
       
 11746 
       
 11747 				// Already removed or fixed
       
 11748 				if (!node.parent || node.fixed) {
       
 11749 					continue;
       
 11750 				}
       
 11751 
       
 11752 				// If the invalid element is a text block and the text block is within a parent LI element
       
 11753 				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
       
 11754 				if (textBlockElements[node.name] && node.parent.name == 'li') {
       
 11755 					// Move sibling text blocks after LI element
       
 11756 					sibling = node.next;
       
 11757 					while (sibling) {
       
 11758 						if (textBlockElements[sibling.name]) {
       
 11759 							sibling.name = 'li';
       
 11760 							sibling.fixed = true;
       
 11761 							node.parent.insert(sibling, node.parent);
       
 11762 						} else {
       
 11763 							break;
       
 11764 						}
       
 11765 
       
 11766 						sibling = sibling.next;
       
 11767 					}
       
 11768 
       
 11769 					// Unwrap current text block
       
 11770 					node.unwrap(node);
       
 11771 					continue;
       
 11772 				}
       
 11773 
       
 11774 				// Get list of all parent nodes until we find a valid parent to stick the child into
       
 11775 				parents = [node];
       
 11776 				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
       
 11777 					!nonSplitableElements[parent.name]; parent = parent.parent) {
       
 11778 					parents.push(parent);
       
 11779 				}
       
 11780 
       
 11781 				// Found a suitable parent
       
 11782 				if (parent && parents.length > 1) {
       
 11783 					// Reverse the array since it makes looping easier
       
 11784 					parents.reverse();
       
 11785 
       
 11786 					// Clone the related parent and insert that after the moved node
       
 11787 					newParent = currentNode = self.filterNode(parents[0].clone());
       
 11788 
       
 11789 					// Start cloning and moving children on the left side of the target node
       
 11790 					for (i = 0; i < parents.length - 1; i++) {
       
 11791 						if (schema.isValidChild(currentNode.name, parents[i].name)) {
       
 11792 							tempNode = self.filterNode(parents[i].clone());
       
 11793 							currentNode.append(tempNode);
       
 11794 						} else {
       
 11795 							tempNode = currentNode;
       
 11796 						}
       
 11797 
       
 11798 						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) {
       
 11799 							nextNode = childNode.next;
       
 11800 							tempNode.append(childNode);
       
 11801 							childNode = nextNode;
       
 11802 						}
       
 11803 
       
 11804 						currentNode = tempNode;
       
 11805 					}
       
 11806 
       
 11807 					if (!newParent.isEmpty(nonEmptyElements)) {
       
 11808 						parent.insert(newParent, parents[0], true);
       
 11809 						parent.insert(node, newParent);
       
 11810 					} else {
       
 11811 						parent.insert(node, parents[0], true);
       
 11812 					}
       
 11813 
       
 11814 					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
       
 11815 					parent = parents[0];
       
 11816 					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
       
 11817 						parent.empty().remove();
       
 11818 					}
       
 11819 				} else if (node.parent) {
       
 11820 					// If it's an LI try to find a UL/OL for it or wrap it
       
 11821 					if (node.name === 'li') {
       
 11822 						sibling = node.prev;
       
 11823 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
       
 11824 							sibling.append(node);
       
 11825 							continue;
       
 11826 						}
       
 11827 
       
 11828 						sibling = node.next;
       
 11829 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
       
 11830 							sibling.insert(node, sibling.firstChild, true);
       
 11831 							continue;
       
 11832 						}
       
 11833 
       
 11834 						node.wrap(self.filterNode(new Node('ul', 1)));
       
 11835 						continue;
       
 11836 					}
       
 11837 
       
 11838 					// Try wrapping the element in a DIV
       
 11839 					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
       
 11840 						node.wrap(self.filterNode(new Node('div', 1)));
       
 11841 					} else {
       
 11842 						// We failed wrapping it, then remove or unwrap it
       
 11843 						if (node.name === 'style' || node.name === 'script') {
       
 11844 							node.empty().remove();
       
 11845 						} else {
       
 11846 							node.unwrap();
       
 11847 						}
       
 11848 					}
       
 11849 				}
       
 11850 			}
       
 11851 		}
       
 11852 
       
 11853 		/**
       
 11854 		 * Runs the specified node though the element and attributes filters.
       
 11855 		 *
       
 11856 		 * @method filterNode
       
 11857 		 * @param {tinymce.html.Node} Node the node to run filters on.
       
 11858 		 * @return {tinymce.html.Node} The passed in node.
       
 11859 		 */
       
 11860 		self.filterNode = function(node) {
       
 11861 			var i, name, list;
       
 11862 
       
 11863 			// Run element filters
       
 11864 			if (name in nodeFilters) {
       
 11865 				list = matchedNodes[name];
       
 11866 
       
 11867 				if (list) {
       
 11868 					list.push(node);
       
 11869 				} else {
       
 11870 					matchedNodes[name] = [node];
       
 11871 				}
       
 11872 			}
       
 11873 
       
 11874 			// Run attribute filters
       
 11875 			i = attributeFilters.length;
       
 11876 			while (i--) {
       
 11877 				name = attributeFilters[i].name;
       
 11878 
       
 11879 				if (name in node.attributes.map) {
       
 11880 					list = matchedAttributes[name];
       
 11881 
       
 11882 					if (list) {
       
 11883 						list.push(node);
       
 11884 					} else {
       
 11885 						matchedAttributes[name] = [node];
       
 11886 					}
       
 11887 				}
       
 11888 			}
       
 11889 
       
 11890 			return node;
       
 11891 		};
       
 11892 
       
 11893 		/**
       
 11894 		 * Adds a node filter function to the parser, the parser will collect the specified nodes by name
       
 11895 		 * and then execute the callback ones it has finished parsing the document.
       
 11896 		 *
       
 11897 		 * @example
       
 11898 		 * parser.addNodeFilter('p,h1', function(nodes, name) {
       
 11899 		 *		for (var i = 0; i < nodes.length; i++) {
       
 11900 		 *			console.log(nodes[i].name);
       
 11901 		 *		}
       
 11902 		 * });
       
 11903 		 * @method addNodeFilter
       
 11904 		 * @method {String} name Comma separated list of nodes to collect.
       
 11905 		 * @param {function} callback Callback function to execute once it has collected nodes.
       
 11906 		 */
       
 11907 		self.addNodeFilter = function(name, callback) {
       
 11908 			each(explode(name), function(name) {
       
 11909 				var list = nodeFilters[name];
       
 11910 
       
 11911 				if (!list) {
       
 11912 					nodeFilters[name] = list = [];
       
 11913 				}
       
 11914 
       
 11915 				list.push(callback);
       
 11916 			});
       
 11917 		};
       
 11918 
       
 11919 		/**
       
 11920 		 * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
       
 11921 		 * and then execute the callback ones it has finished parsing the document.
       
 11922 		 *
       
 11923 		 * @example
       
 11924 		 * parser.addAttributeFilter('src,href', function(nodes, name) {
       
 11925 		 *		for (var i = 0; i < nodes.length; i++) {
       
 11926 		 *			console.log(nodes[i].name);
       
 11927 		 *		}
       
 11928 		 * });
       
 11929 		 * @method addAttributeFilter
       
 11930 		 * @method {String} name Comma separated list of nodes to collect.
       
 11931 		 * @param {function} callback Callback function to execute once it has collected nodes.
       
 11932 		 */
       
 11933 		self.addAttributeFilter = function(name, callback) {
       
 11934 			each(explode(name), function(name) {
       
 11935 				var i;
       
 11936 
       
 11937 				for (i = 0; i < attributeFilters.length; i++) {
       
 11938 					if (attributeFilters[i].name === name) {
       
 11939 						attributeFilters[i].callbacks.push(callback);
       
 11940 						return;
       
 11941 					}
       
 11942 				}
       
 11943 
       
 11944 				attributeFilters.push({name: name, callbacks: [callback]});
       
 11945 			});
       
 11946 		};
       
 11947 
       
 11948 		/**
       
 11949 		 * Parses the specified HTML string into a DOM like node tree and returns the result.
       
 11950 		 *
       
 11951 		 * @example
       
 11952 		 * var rootNode = new DomParser({...}).parse('<b>text</b>');
       
 11953 		 * @method parse
       
 11954 		 * @param {String} html Html string to sax parse.
       
 11955 		 * @param {Object} args Optional args object that gets passed to all filter functions.
       
 11956 		 * @return {tinymce.html.Node} Root node containing the tree.
       
 11957 		 */
       
 11958 		self.parse = function(html, args) {
       
 11959 			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
       
 11960 			var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
       
 11961 			var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
       
 11962 			var children, nonEmptyElements, rootBlockName;
       
 11963 
       
 11964 			args = args || {};
       
 11965 			matchedNodes = {};
       
 11966 			matchedAttributes = {};
       
 11967 			blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
       
 11968 			nonEmptyElements = schema.getNonEmptyElements();
       
 11969 			children = schema.children;
       
 11970 			validate = settings.validate;
       
 11971 			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
       
 11972 
       
 11973 			whiteSpaceElements = schema.getWhiteSpaceElements();
       
 11974 			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
       
 11975 			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
       
 11976 			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
       
 11977 			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
       
 11978 
       
 11979 			function addRootBlocks() {
       
 11980 				var node = rootNode.firstChild, next, rootBlockNode;
       
 11981 
       
 11982 				// Removes whitespace at beginning and end of block so:
       
 11983 				// <p> x </p> -> <p>x</p>
       
 11984 				function trim(rootBlockNode) {
       
 11985 					if (rootBlockNode) {
       
 11986 						node = rootBlockNode.firstChild;
       
 11987 						if (node && node.type == 3) {
       
 11988 							node.value = node.value.replace(startWhiteSpaceRegExp, '');
       
 11989 						}
       
 11990 
       
 11991 						node = rootBlockNode.lastChild;
       
 11992 						if (node && node.type == 3) {
       
 11993 							node.value = node.value.replace(endWhiteSpaceRegExp, '');
       
 11994 						}
       
 11995 					}
       
 11996 				}
       
 11997 
       
 11998 				// Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
       
 11999 				if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
       
 12000 					return;
       
 12001 				}
       
 12002 
       
 12003 				while (node) {
       
 12004 					next = node.next;
       
 12005 
       
 12006 					if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
       
 12007 						!blockElements[node.name] && !node.attr('data-mce-type'))) {
       
 12008 						if (!rootBlockNode) {
       
 12009 							// Create a new root block element
       
 12010 							rootBlockNode = createNode(rootBlockName, 1);
       
 12011 							rootBlockNode.attr(settings.forced_root_block_attrs);
       
 12012 							rootNode.insert(rootBlockNode, node);
       
 12013 							rootBlockNode.append(node);
       
 12014 						} else {
       
 12015 							rootBlockNode.append(node);
       
 12016 						}
       
 12017 					} else {
       
 12018 						trim(rootBlockNode);
       
 12019 						rootBlockNode = null;
       
 12020 					}
       
 12021 
       
 12022 					node = next;
       
 12023 				}
       
 12024 
       
 12025 				trim(rootBlockNode);
       
 12026 			}
       
 12027 
       
 12028 			function createNode(name, type) {
       
 12029 				var node = new Node(name, type), list;
       
 12030 
       
 12031 				if (name in nodeFilters) {
       
 12032 					list = matchedNodes[name];
       
 12033 
       
 12034 					if (list) {
       
 12035 						list.push(node);
       
 12036 					} else {
       
 12037 						matchedNodes[name] = [node];
       
 12038 					}
       
 12039 				}
       
 12040 
       
 12041 				return node;
       
 12042 			}
       
 12043 
       
 12044 			function removeWhitespaceBefore(node) {
       
 12045 				var textNode, textVal, sibling;
       
 12046 
       
 12047 				for (textNode = node.prev; textNode && textNode.type === 3;) {
       
 12048 					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
       
 12049 
       
 12050 					if (textVal.length > 0) {
       
 12051 						textNode.value = textVal;
       
 12052 						textNode = textNode.prev;
       
 12053 					} else {
       
 12054 						sibling = textNode.prev;
       
 12055 						textNode.remove();
       
 12056 						textNode = sibling;
       
 12057 					}
       
 12058 				}
       
 12059 			}
       
 12060 
       
 12061 			function cloneAndExcludeBlocks(input) {
       
 12062 				var name, output = {};
       
 12063 
       
 12064 				for (name in input) {
       
 12065 					if (name !== 'li' && name != 'p') {
       
 12066 						output[name] = input[name];
       
 12067 					}
       
 12068 				}
       
 12069 
       
 12070 				return output;
       
 12071 			}
       
 12072 
       
 12073 			parser = new SaxParser({
       
 12074 				validate: validate,
       
 12075 				allow_script_urls: settings.allow_script_urls,
       
 12076 				allow_conditional_comments: settings.allow_conditional_comments,
       
 12077 
       
 12078 				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
       
 12079 				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
       
 12080 
       
 12081 				cdata: function(text) {
       
 12082 					node.append(createNode('#cdata', 4)).value = text;
       
 12083 				},
       
 12084 
       
 12085 				text: function(text, raw) {
       
 12086 					var textNode;
       
 12087 
       
 12088 					// Trim all redundant whitespace on non white space elements
       
 12089 					if (!isInWhiteSpacePreservedElement) {
       
 12090 						text = text.replace(allWhiteSpaceRegExp, ' ');
       
 12091 
       
 12092 						if (node.lastChild && blockElements[node.lastChild.name]) {
       
 12093 							text = text.replace(startWhiteSpaceRegExp, '');
       
 12094 						}
       
 12095 					}
       
 12096 
       
 12097 					// Do we need to create the node
       
 12098 					if (text.length !== 0) {
       
 12099 						textNode = createNode('#text', 3);
       
 12100 						textNode.raw = !!raw;
       
 12101 						node.append(textNode).value = text;
       
 12102 					}
       
 12103 				},
       
 12104 
       
 12105 				comment: function(text) {
       
 12106 					node.append(createNode('#comment', 8)).value = text;
       
 12107 				},
       
 12108 
       
 12109 				pi: function(name, text) {
       
 12110 					node.append(createNode(name, 7)).value = text;
       
 12111 					removeWhitespaceBefore(node);
       
 12112 				},
       
 12113 
       
 12114 				doctype: function(text) {
       
 12115 					var newNode;
       
 12116 
       
 12117 					newNode = node.append(createNode('#doctype', 10));
       
 12118 					newNode.value = text;
       
 12119 					removeWhitespaceBefore(node);
       
 12120 				},
       
 12121 
       
 12122 				start: function(name, attrs, empty) {
       
 12123 					var newNode, attrFiltersLen, elementRule, attrName, parent;
       
 12124 
       
 12125 					elementRule = validate ? schema.getElementRule(name) : {};
       
 12126 					if (elementRule) {
       
 12127 						newNode = createNode(elementRule.outputName || name, 1);
       
 12128 						newNode.attributes = attrs;
       
 12129 						newNode.shortEnded = empty;
       
 12130 
       
 12131 						node.append(newNode);
       
 12132 
       
 12133 						// Check if node is valid child of the parent node is the child is
       
 12134 						// unknown we don't collect it since it's probably a custom element
       
 12135 						parent = children[node.name];
       
 12136 						if (parent && children[newNode.name] && !parent[newNode.name]) {
       
 12137 							invalidChildren.push(newNode);
       
 12138 						}
       
 12139 
       
 12140 						attrFiltersLen = attributeFilters.length;
       
 12141 						while (attrFiltersLen--) {
       
 12142 							attrName = attributeFilters[attrFiltersLen].name;
       
 12143 
       
 12144 							if (attrName in attrs.map) {
       
 12145 								list = matchedAttributes[attrName];
       
 12146 
       
 12147 								if (list) {
       
 12148 									list.push(newNode);
       
 12149 								} else {
       
 12150 									matchedAttributes[attrName] = [newNode];
       
 12151 								}
       
 12152 							}
       
 12153 						}
       
 12154 
       
 12155 						// Trim whitespace before block
       
 12156 						if (blockElements[name]) {
       
 12157 							removeWhitespaceBefore(newNode);
       
 12158 						}
       
 12159 
       
 12160 						// Change current node if the element wasn't empty i.e not <br /> or <img />
       
 12161 						if (!empty) {
       
 12162 							node = newNode;
       
 12163 						}
       
 12164 
       
 12165 						// Check if we are inside a whitespace preserved element
       
 12166 						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
       
 12167 							isInWhiteSpacePreservedElement = true;
       
 12168 						}
       
 12169 					}
       
 12170 				},
       
 12171 
       
 12172 				end: function(name) {
       
 12173 					var textNode, elementRule, text, sibling, tempNode;
       
 12174 
       
 12175 					elementRule = validate ? schema.getElementRule(name) : {};
       
 12176 					if (elementRule) {
       
 12177 						if (blockElements[name]) {
       
 12178 							if (!isInWhiteSpacePreservedElement) {
       
 12179 								// Trim whitespace of the first node in a block
       
 12180 								textNode = node.firstChild;
       
 12181 								if (textNode && textNode.type === 3) {
       
 12182 									text = textNode.value.replace(startWhiteSpaceRegExp, '');
       
 12183 
       
 12184 									// Any characters left after trim or should we remove it
       
 12185 									if (text.length > 0) {
       
 12186 										textNode.value = text;
       
 12187 										textNode = textNode.next;
       
 12188 									} else {
       
 12189 										sibling = textNode.next;
       
 12190 										textNode.remove();
       
 12191 										textNode = sibling;
       
 12192 
       
 12193 										// Remove any pure whitespace siblings
       
 12194 										while (textNode && textNode.type === 3) {
       
 12195 											text = textNode.value;
       
 12196 											sibling = textNode.next;
       
 12197 
       
 12198 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
       
 12199 												textNode.remove();
       
 12200 												textNode = sibling;
       
 12201 											}
       
 12202 
       
 12203 											textNode = sibling;
       
 12204 										}
       
 12205 									}
       
 12206 								}
       
 12207 
       
 12208 								// Trim whitespace of the last node in a block
       
 12209 								textNode = node.lastChild;
       
 12210 								if (textNode && textNode.type === 3) {
       
 12211 									text = textNode.value.replace(endWhiteSpaceRegExp, '');
       
 12212 
       
 12213 									// Any characters left after trim or should we remove it
       
 12214 									if (text.length > 0) {
       
 12215 										textNode.value = text;
       
 12216 										textNode = textNode.prev;
       
 12217 									} else {
       
 12218 										sibling = textNode.prev;
       
 12219 										textNode.remove();
       
 12220 										textNode = sibling;
       
 12221 
       
 12222 										// Remove any pure whitespace siblings
       
 12223 										while (textNode && textNode.type === 3) {
       
 12224 											text = textNode.value;
       
 12225 											sibling = textNode.prev;
       
 12226 
       
 12227 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
       
 12228 												textNode.remove();
       
 12229 												textNode = sibling;
       
 12230 											}
       
 12231 
       
 12232 											textNode = sibling;
       
 12233 										}
       
 12234 									}
       
 12235 								}
       
 12236 							}
       
 12237 
       
 12238 							// Trim start white space
       
 12239 							// Removed due to: #5424
       
 12240 							/*textNode = node.prev;
       
 12241 							if (textNode && textNode.type === 3) {
       
 12242 								text = textNode.value.replace(startWhiteSpaceRegExp, '');
       
 12243 
       
 12244 								if (text.length > 0)
       
 12245 									textNode.value = text;
       
 12246 								else
       
 12247 									textNode.remove();
       
 12248 							}*/
       
 12249 						}
       
 12250 
       
 12251 						// Check if we exited a whitespace preserved element
       
 12252 						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
       
 12253 							isInWhiteSpacePreservedElement = false;
       
 12254 						}
       
 12255 
       
 12256 						// Handle empty nodes
       
 12257 						if (elementRule.removeEmpty || elementRule.paddEmpty) {
       
 12258 							if (node.isEmpty(nonEmptyElements)) {
       
 12259 								if (elementRule.paddEmpty) {
       
 12260 									node.empty().append(new Node('#text', '3')).value = '\u00a0';
       
 12261 								} else {
       
 12262 									// Leave nodes that have a name like <a name="name">
       
 12263 									if (!node.attributes.map.name && !node.attributes.map.id) {
       
 12264 										tempNode = node.parent;
       
 12265 
       
 12266 										if (blockElements[node.name]) {
       
 12267 											node.empty().remove();
       
 12268 										} else {
       
 12269 											node.unwrap();
       
 12270 										}
       
 12271 
       
 12272 										node = tempNode;
       
 12273 										return;
       
 12274 									}
       
 12275 								}
       
 12276 							}
       
 12277 						}
       
 12278 
       
 12279 						node = node.parent;
       
 12280 					}
       
 12281 				}
       
 12282 			}, schema);
       
 12283 
       
 12284 			rootNode = node = new Node(args.context || settings.root_name, 11);
       
 12285 
       
 12286 			parser.parse(html);
       
 12287 
       
 12288 			// Fix invalid children or report invalid children in a contextual parsing
       
 12289 			if (validate && invalidChildren.length) {
       
 12290 				if (!args.context) {
       
 12291 					fixInvalidChildren(invalidChildren);
       
 12292 				} else {
       
 12293 					args.invalid = true;
       
 12294 				}
       
 12295 			}
       
 12296 
       
 12297 			// Wrap nodes in the root into block elements if the root is body
       
 12298 			if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
       
 12299 				addRootBlocks();
       
 12300 			}
       
 12301 
       
 12302 			// Run filters only when the contents is valid
       
 12303 			if (!args.invalid) {
       
 12304 				// Run node filters
       
 12305 				for (name in matchedNodes) {
       
 12306 					list = nodeFilters[name];
       
 12307 					nodes = matchedNodes[name];
       
 12308 
       
 12309 					// Remove already removed children
       
 12310 					fi = nodes.length;
       
 12311 					while (fi--) {
       
 12312 						if (!nodes[fi].parent) {
       
 12313 							nodes.splice(fi, 1);
       
 12314 						}
       
 12315 					}
       
 12316 
       
 12317 					for (i = 0, l = list.length; i < l; i++) {
       
 12318 						list[i](nodes, name, args);
       
 12319 					}
       
 12320 				}
       
 12321 
       
 12322 				// Run attribute filters
       
 12323 				for (i = 0, l = attributeFilters.length; i < l; i++) {
       
 12324 					list = attributeFilters[i];
       
 12325 
       
 12326 					if (list.name in matchedAttributes) {
       
 12327 						nodes = matchedAttributes[list.name];
       
 12328 
       
 12329 						// Remove already removed children
       
 12330 						fi = nodes.length;
       
 12331 						while (fi--) {
       
 12332 							if (!nodes[fi].parent) {
       
 12333 								nodes.splice(fi, 1);
       
 12334 							}
       
 12335 						}
       
 12336 
       
 12337 						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
       
 12338 							list.callbacks[fi](nodes, list.name, args);
       
 12339 						}
       
 12340 					}
       
 12341 				}
       
 12342 			}
       
 12343 
       
 12344 			return rootNode;
       
 12345 		};
       
 12346 
       
 12347 		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
       
 12348 		// make it possible to place the caret inside empty blocks. This logic tries to remove
       
 12349 		// these elements and keep br elements that where intended to be there intact
       
 12350 		if (settings.remove_trailing_brs) {
       
 12351 			self.addNodeFilter('br', function(nodes) {
       
 12352 				var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
       
 12353 				var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
       
 12354 				var elementRule, textNode;
       
 12355 
       
 12356 				// Remove brs from body element as well
       
 12357 				blockElements.body = 1;
       
 12358 
       
 12359 				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
       
 12360 				for (i = 0; i < l; i++) {
       
 12361 					node = nodes[i];
       
 12362 					parent = node.parent;
       
 12363 
       
 12364 					if (blockElements[node.parent.name] && node === parent.lastChild) {
       
 12365 						// Loop all nodes to the left of the current node and check for other BR elements
       
 12366 						// excluding bookmarks since they are invisible
       
 12367 						prev = node.prev;
       
 12368 						while (prev) {
       
 12369 							prevName = prev.name;
       
 12370 
       
 12371 							// Ignore bookmarks
       
 12372 							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
       
 12373 								// Found a non BR element
       
 12374 								if (prevName !== "br") {
       
 12375 									break;
       
 12376 								}
       
 12377 
       
 12378 								// Found another br it's a <br><br> structure then don't remove anything
       
 12379 								if (prevName === 'br') {
       
 12380 									node = null;
       
 12381 									break;
       
 12382 								}
       
 12383 							}
       
 12384 
       
 12385 							prev = prev.prev;
       
 12386 						}
       
 12387 
       
 12388 						if (node) {
       
 12389 							node.remove();
       
 12390 
       
 12391 							// Is the parent to be considered empty after we removed the BR
       
 12392 							if (parent.isEmpty(nonEmptyElements)) {
       
 12393 								elementRule = schema.getElementRule(parent.name);
       
 12394 
       
 12395 								// Remove or padd the element depending on schema rule
       
 12396 								if (elementRule) {
       
 12397 									if (elementRule.removeEmpty) {
       
 12398 										parent.remove();
       
 12399 									} else if (elementRule.paddEmpty) {
       
 12400 										parent.empty().append(new Node('#text', 3)).value = '\u00a0';
       
 12401 									}
       
 12402 								}
       
 12403 							}
       
 12404 						}
       
 12405 					} else {
       
 12406 						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
       
 12407 						// so they become <p><b><i>&nbsp;</i></b></p>
       
 12408 						lastParent = node;
       
 12409 						while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
       
 12410 							lastParent = parent;
       
 12411 
       
 12412 							if (blockElements[parent.name]) {
       
 12413 								break;
       
 12414 							}
       
 12415 
       
 12416 							parent = parent.parent;
       
 12417 						}
       
 12418 
       
 12419 						if (lastParent === parent) {
       
 12420 							textNode = new Node('#text', 3);
       
 12421 							textNode.value = '\u00a0';
       
 12422 							node.replace(textNode);
       
 12423 						}
       
 12424 					}
       
 12425 				}
       
 12426 			});
       
 12427 		}
       
 12428 
       
 12429 		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
       
 12430 		if (!settings.allow_html_in_named_anchor) {
       
 12431 			self.addAttributeFilter('id,name', function(nodes) {
       
 12432 				var i = nodes.length, sibling, prevSibling, parent, node;
       
 12433 
       
 12434 				while (i--) {
       
 12435 					node = nodes[i];
       
 12436 					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
       
 12437 						parent = node.parent;
       
 12438 
       
 12439 						// Move children after current node
       
 12440 						sibling = node.lastChild;
       
 12441 						do {
       
 12442 							prevSibling = sibling.prev;
       
 12443 							parent.insert(sibling, node);
       
 12444 							sibling = prevSibling;
       
 12445 						} while (sibling);
       
 12446 					}
       
 12447 				}
       
 12448 			});
       
 12449 		}
       
 12450 
       
 12451 		if (settings.validate && schema.getValidClasses()) {
       
 12452 			self.addAttributeFilter('class', function(nodes) {
       
 12453 				var i = nodes.length, node, classList, ci, className, classValue;
       
 12454 				var validClasses = schema.getValidClasses(), validClassesMap, valid;
       
 12455 
       
 12456 				while (i--) {
       
 12457 					node = nodes[i];
       
 12458 					classList = node.attr('class').split(' ');
       
 12459 					classValue = '';
       
 12460 
       
 12461 					for (ci = 0; ci < classList.length; ci++) {
       
 12462 						className = classList[ci];
       
 12463 						valid = false;
       
 12464 
       
 12465 						validClassesMap = validClasses['*'];
       
 12466 						if (validClassesMap && validClassesMap[className]) {
       
 12467 							valid = true;
       
 12468 						}
       
 12469 
       
 12470 						validClassesMap = validClasses[node.name];
       
 12471 						if (!valid && validClassesMap && validClassesMap[className]) {
       
 12472 							valid = true;
       
 12473 						}
       
 12474 
       
 12475 						if (valid) {
       
 12476 							if (classValue) {
       
 12477 								classValue += ' ';
       
 12478 							}
       
 12479 
       
 12480 							classValue += className;
       
 12481 						}
       
 12482 					}
       
 12483 
       
 12484 					if (!classValue.length) {
       
 12485 						classValue = null;
       
 12486 					}
       
 12487 
       
 12488 					node.attr('class', classValue);
       
 12489 				}
       
 12490 			});
       
 12491 		}
       
 12492 	};
       
 12493 });
       
 12494 
       
 12495 // Included from: js/tinymce/classes/html/Writer.js
       
 12496 
       
 12497 /**
       
 12498  * Writer.js
       
 12499  *
       
 12500  * Copyright, Moxiecode Systems AB
       
 12501  * Released under LGPL License.
       
 12502  *
       
 12503  * License: http://www.tinymce.com/license
       
 12504  * Contributing: http://www.tinymce.com/contributing
       
 12505  */
       
 12506 
       
 12507 /**
       
 12508  * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
       
 12509  *
       
 12510  * @class tinymce.html.Writer
       
 12511  * @example
       
 12512  * var writer = new tinymce.html.Writer({indent: true});
       
 12513  * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
       
 12514  * console.log(writer.getContent());
       
 12515  *
       
 12516  * @class tinymce.html.Writer
       
 12517  * @version 3.4
       
 12518  */
       
 12519 define("tinymce/html/Writer", [
       
 12520 	"tinymce/html/Entities",
       
 12521 	"tinymce/util/Tools"
       
 12522 ], function(Entities, Tools) {
       
 12523 	var makeMap = Tools.makeMap;
       
 12524 
       
 12525 	/**
       
 12526 	 * Constructs a new Writer instance.
       
 12527 	 *
       
 12528 	 * @constructor
       
 12529 	 * @method Writer
       
 12530 	 * @param {Object} settings Name/value settings object.
       
 12531 	 */
       
 12532 	return function(settings) {
       
 12533 		var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
       
 12534 
       
 12535 		settings = settings || {};
       
 12536 		indent = settings.indent;
       
 12537 		indentBefore = makeMap(settings.indent_before || '');
       
 12538 		indentAfter = makeMap(settings.indent_after || '');
       
 12539 		encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
       
 12540 		htmlOutput = settings.element_format == "html";
       
 12541 
       
 12542 		return {
       
 12543 			/**
       
 12544 			 * Writes the a start element such as <p id="a">.
       
 12545 			 *
       
 12546 			 * @method start
       
 12547 			 * @param {String} name Name of the element.
       
 12548 			 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
       
 12549 			 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
       
 12550 			 */
       
 12551 			start: function(name, attrs, empty) {
       
 12552 				var i, l, attr, value;
       
 12553 
       
 12554 				if (indent && indentBefore[name] && html.length > 0) {
       
 12555 					value = html[html.length - 1];
       
 12556 
       
 12557 					if (value.length > 0 && value !== '\n') {
       
 12558 						html.push('\n');
       
 12559 					}
       
 12560 				}
       
 12561 
       
 12562 				html.push('<', name);
       
 12563 
       
 12564 				if (attrs) {
       
 12565 					for (i = 0, l = attrs.length; i < l; i++) {
       
 12566 						attr = attrs[i];
       
 12567 						html.push(' ', attr.name, '="', encode(attr.value, true), '"');
       
 12568 					}
       
 12569 				}
       
 12570 
       
 12571 				if (!empty || htmlOutput) {
       
 12572 					html[html.length] = '>';
       
 12573 				} else {
       
 12574 					html[html.length] = ' />';
       
 12575 				}
       
 12576 
       
 12577 				if (empty && indent && indentAfter[name] && html.length > 0) {
       
 12578 					value = html[html.length - 1];
       
 12579 
       
 12580 					if (value.length > 0 && value !== '\n') {
       
 12581 						html.push('\n');
       
 12582 					}
       
 12583 				}
       
 12584 			},
       
 12585 
       
 12586 			/**
       
 12587 			 * Writes the a end element such as </p>.
       
 12588 			 *
       
 12589 			 * @method end
       
 12590 			 * @param {String} name Name of the element.
       
 12591 			 */
       
 12592 			end: function(name) {
       
 12593 				var value;
       
 12594 
       
 12595 				/*if (indent && indentBefore[name] && html.length > 0) {
       
 12596 					value = html[html.length - 1];
       
 12597 
       
 12598 					if (value.length > 0 && value !== '\n')
       
 12599 						html.push('\n');
       
 12600 				}*/
       
 12601 
       
 12602 				html.push('</', name, '>');
       
 12603 
       
 12604 				if (indent && indentAfter[name] && html.length > 0) {
       
 12605 					value = html[html.length - 1];
       
 12606 
       
 12607 					if (value.length > 0 && value !== '\n') {
       
 12608 						html.push('\n');
       
 12609 					}
       
 12610 				}
       
 12611 			},
       
 12612 
       
 12613 			/**
       
 12614 			 * Writes a text node.
       
 12615 			 *
       
 12616 			 * @method text
       
 12617 			 * @param {String} text String to write out.
       
 12618 			 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
       
 12619 			 */
       
 12620 			text: function(text, raw) {
       
 12621 				if (text.length > 0) {
       
 12622 					html[html.length] = raw ? text : encode(text);
       
 12623 				}
       
 12624 			},
       
 12625 
       
 12626 			/**
       
 12627 			 * Writes a cdata node such as <![CDATA[data]]>.
       
 12628 			 *
       
 12629 			 * @method cdata
       
 12630 			 * @param {String} text String to write out inside the cdata.
       
 12631 			 */
       
 12632 			cdata: function(text) {
       
 12633 				html.push('<![CDATA[', text, ']]>');
       
 12634 			},
       
 12635 
       
 12636 			/**
       
 12637 			 * Writes a comment node such as <!-- Comment -->.
       
 12638 			 *
       
 12639 			 * @method cdata
       
 12640 			 * @param {String} text String to write out inside the comment.
       
 12641 			 */
       
 12642 			comment: function(text) {
       
 12643 				html.push('<!--', text, '-->');
       
 12644 			},
       
 12645 
       
 12646 			/**
       
 12647 			 * Writes a PI node such as <?xml attr="value" ?>.
       
 12648 			 *
       
 12649 			 * @method pi
       
 12650 			 * @param {String} name Name of the pi.
       
 12651 			 * @param {String} text String to write out inside the pi.
       
 12652 			 */
       
 12653 			pi: function(name, text) {
       
 12654 				if (text) {
       
 12655 					html.push('<?', name, ' ', encode(text), '?>');
       
 12656 				} else {
       
 12657 					html.push('<?', name, '?>');
       
 12658 				}
       
 12659 
       
 12660 				if (indent) {
       
 12661 					html.push('\n');
       
 12662 				}
       
 12663 			},
       
 12664 
       
 12665 			/**
       
 12666 			 * Writes a doctype node such as <!DOCTYPE data>.
       
 12667 			 *
       
 12668 			 * @method doctype
       
 12669 			 * @param {String} text String to write out inside the doctype.
       
 12670 			 */
       
 12671 			doctype: function(text) {
       
 12672 				html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
       
 12673 			},
       
 12674 
       
 12675 			/**
       
 12676 			 * Resets the internal buffer if one wants to reuse the writer.
       
 12677 			 *
       
 12678 			 * @method reset
       
 12679 			 */
       
 12680 			reset: function() {
       
 12681 				html.length = 0;
       
 12682 			},
       
 12683 
       
 12684 			/**
       
 12685 			 * Returns the contents that got serialized.
       
 12686 			 *
       
 12687 			 * @method getContent
       
 12688 			 * @return {String} HTML contents that got written down.
       
 12689 			 */
       
 12690 			getContent: function() {
       
 12691 				return html.join('').replace(/\n$/, '');
       
 12692 			}
       
 12693 		};
       
 12694 	};
       
 12695 });
       
 12696 
       
 12697 // Included from: js/tinymce/classes/html/Serializer.js
       
 12698 
       
 12699 /**
       
 12700  * Serializer.js
       
 12701  *
       
 12702  * Copyright, Moxiecode Systems AB
       
 12703  * Released under LGPL License.
       
 12704  *
       
 12705  * License: http://www.tinymce.com/license
       
 12706  * Contributing: http://www.tinymce.com/contributing
       
 12707  */
       
 12708 
       
 12709 /**
       
 12710  * This class is used to serialize down the DOM tree into a string using a Writer instance.
       
 12711  *
       
 12712  *
       
 12713  * @example
       
 12714  * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
       
 12715  * @class tinymce.html.Serializer
       
 12716  * @version 3.4
       
 12717  */
       
 12718 define("tinymce/html/Serializer", [
       
 12719 	"tinymce/html/Writer",
       
 12720 	"tinymce/html/Schema"
       
 12721 ], function(Writer, Schema) {
       
 12722 	/**
       
 12723 	 * Constructs a new Serializer instance.
       
 12724 	 *
       
 12725 	 * @constructor
       
 12726 	 * @method Serializer
       
 12727 	 * @param {Object} settings Name/value settings object.
       
 12728 	 * @param {tinymce.html.Schema} schema Schema instance to use.
       
 12729 	 */
       
 12730 	return function(settings, schema) {
       
 12731 		var self = this, writer = new Writer(settings);
       
 12732 
       
 12733 		settings = settings || {};
       
 12734 		settings.validate = "validate" in settings ? settings.validate : true;
       
 12735 
       
 12736 		self.schema = schema = schema || new Schema();
       
 12737 		self.writer = writer;
       
 12738 
       
 12739 		/**
       
 12740 		 * Serializes the specified node into a string.
       
 12741 		 *
       
 12742 		 * @example
       
 12743 		 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
       
 12744 		 * @method serialize
       
 12745 		 * @param {tinymce.html.Node} node Node instance to serialize.
       
 12746 		 * @return {String} String with HTML based on DOM tree.
       
 12747 		 */
       
 12748 		self.serialize = function(node) {
       
 12749 			var handlers, validate;
       
 12750 
       
 12751 			validate = settings.validate;
       
 12752 
       
 12753 			handlers = {
       
 12754 				// #text
       
 12755 				3: function(node) {
       
 12756 					writer.text(node.value, node.raw);
       
 12757 				},
       
 12758 
       
 12759 				// #comment
       
 12760 				8: function(node) {
       
 12761 					writer.comment(node.value);
       
 12762 				},
       
 12763 
       
 12764 				// Processing instruction
       
 12765 				7: function(node) {
       
 12766 					writer.pi(node.name, node.value);
       
 12767 				},
       
 12768 
       
 12769 				// Doctype
       
 12770 				10: function(node) {
       
 12771 					writer.doctype(node.value);
       
 12772 				},
       
 12773 
       
 12774 				// CDATA
       
 12775 				4: function(node) {
       
 12776 					writer.cdata(node.value);
       
 12777 				},
       
 12778 
       
 12779 				// Document fragment
       
 12780 				11: function(node) {
       
 12781 					if ((node = node.firstChild)) {
       
 12782 						do {
       
 12783 							walk(node);
       
 12784 						} while ((node = node.next));
       
 12785 					}
       
 12786 				}
       
 12787 			};
       
 12788 
       
 12789 			writer.reset();
       
 12790 
       
 12791 			function walk(node) {
       
 12792 				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
       
 12793 
       
 12794 				if (!handler) {
       
 12795 					name = node.name;
       
 12796 					isEmpty = node.shortEnded;
       
 12797 					attrs = node.attributes;
       
 12798 
       
 12799 					// Sort attributes
       
 12800 					if (validate && attrs && attrs.length > 1) {
       
 12801 						sortedAttrs = [];
       
 12802 						sortedAttrs.map = {};
       
 12803 
       
 12804 						elementRule = schema.getElementRule(node.name);
       
 12805 						for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
       
 12806 							attrName = elementRule.attributesOrder[i];
       
 12807 
       
 12808 							if (attrName in attrs.map) {
       
 12809 								attrValue = attrs.map[attrName];
       
 12810 								sortedAttrs.map[attrName] = attrValue;
       
 12811 								sortedAttrs.push({name: attrName, value: attrValue});
       
 12812 							}
       
 12813 						}
       
 12814 
       
 12815 						for (i = 0, l = attrs.length; i < l; i++) {
       
 12816 							attrName = attrs[i].name;
       
 12817 
       
 12818 							if (!(attrName in sortedAttrs.map)) {
       
 12819 								attrValue = attrs.map[attrName];
       
 12820 								sortedAttrs.map[attrName] = attrValue;
       
 12821 								sortedAttrs.push({name: attrName, value: attrValue});
       
 12822 							}
       
 12823 						}
       
 12824 
       
 12825 						attrs = sortedAttrs;
       
 12826 					}
       
 12827 
       
 12828 					writer.start(node.name, attrs, isEmpty);
       
 12829 
       
 12830 					if (!isEmpty) {
       
 12831 						if ((node = node.firstChild)) {
       
 12832 							do {
       
 12833 								walk(node);
       
 12834 							} while ((node = node.next));
       
 12835 						}
       
 12836 
       
 12837 						writer.end(name);
       
 12838 					}
       
 12839 				} else {
       
 12840 					handler(node);
       
 12841 				}
       
 12842 			}
       
 12843 
       
 12844 			// Serialize element and treat all non elements as fragments
       
 12845 			if (node.type == 1 && !settings.inner) {
       
 12846 				walk(node);
       
 12847 			} else {
       
 12848 				handlers[11](node);
       
 12849 			}
       
 12850 
       
 12851 			return writer.getContent();
       
 12852 		};
       
 12853 	};
       
 12854 });
       
 12855 
       
 12856 // Included from: js/tinymce/classes/dom/Serializer.js
       
 12857 
       
 12858 /**
       
 12859  * Serializer.js
       
 12860  *
       
 12861  * Copyright, Moxiecode Systems AB
       
 12862  * Released under LGPL License.
       
 12863  *
       
 12864  * License: http://www.tinymce.com/license
       
 12865  * Contributing: http://www.tinymce.com/contributing
       
 12866  */
       
 12867 
       
 12868 /**
       
 12869  * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
       
 12870  * more details and examples on how to use this class.
       
 12871  *
       
 12872  * @class tinymce.dom.Serializer
       
 12873  */
       
 12874 define("tinymce/dom/Serializer", [
       
 12875 	"tinymce/dom/DOMUtils",
       
 12876 	"tinymce/html/DomParser",
       
 12877 	"tinymce/html/Entities",
       
 12878 	"tinymce/html/Serializer",
       
 12879 	"tinymce/html/Node",
       
 12880 	"tinymce/html/Schema",
       
 12881 	"tinymce/Env",
       
 12882 	"tinymce/util/Tools"
       
 12883 ], function(DOMUtils, DomParser, Entities, Serializer, Node, Schema, Env, Tools) {
       
 12884 	var each = Tools.each, trim = Tools.trim;
       
 12885 	var DOM = DOMUtils.DOM;
       
 12886 
       
 12887 	/**
       
 12888 	 * Constructs a new DOM serializer class.
       
 12889 	 *
       
 12890 	 * @constructor
       
 12891 	 * @method Serializer
       
 12892 	 * @param {Object} settings Serializer settings object.
       
 12893 	 * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
       
 12894 	 */
       
 12895 	return function(settings, editor) {
       
 12896 		var dom, schema, htmlParser;
       
 12897 
       
 12898 		if (editor) {
       
 12899 			dom = editor.dom;
       
 12900 			schema = editor.schema;
       
 12901 		}
       
 12902 
       
 12903 		// Default DOM and Schema if they are undefined
       
 12904 		dom = dom || DOM;
       
 12905 		schema = schema || new Schema(settings);
       
 12906 		settings.entity_encoding = settings.entity_encoding || 'named';
       
 12907 		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
       
 12908 
       
 12909 		htmlParser = new DomParser(settings, schema);
       
 12910 
       
 12911 		// Convert tabindex back to elements when serializing contents
       
 12912 		htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) {
       
 12913 			var i = nodes.length, node;
       
 12914 
       
 12915 			while (i--) {
       
 12916 				node = nodes[i];
       
 12917 				node.attr('tabindex', node.attributes.map['data-mce-tabindex']);
       
 12918 				node.attr(name, null);
       
 12919 			}
       
 12920 		});
       
 12921 
       
 12922 		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
       
 12923 		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
       
 12924 			var i = nodes.length, node, value, internalName = 'data-mce-' + name;
       
 12925 			var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
       
 12926 
       
 12927 			while (i--) {
       
 12928 				node = nodes[i];
       
 12929 
       
 12930 				value = node.attributes.map[internalName];
       
 12931 				if (value !== undef) {
       
 12932 					// Set external name to internal value and remove internal
       
 12933 					node.attr(name, value.length > 0 ? value : null);
       
 12934 					node.attr(internalName, null);
       
 12935 				} else {
       
 12936 					// No internal attribute found then convert the value we have in the DOM
       
 12937 					value = node.attributes.map[name];
       
 12938 
       
 12939 					if (name === "style") {
       
 12940 						value = dom.serializeStyle(dom.parseStyle(value), node.name);
       
 12941 					} else if (urlConverter) {
       
 12942 						value = urlConverter.call(urlConverterScope, value, name, node.name);
       
 12943 					}
       
 12944 
       
 12945 					node.attr(name, value.length > 0 ? value : null);
       
 12946 				}
       
 12947 			}
       
 12948 		});
       
 12949 
       
 12950 		// Remove internal classes mceItem<..> or mceSelected
       
 12951 		htmlParser.addAttributeFilter('class', function(nodes) {
       
 12952 			var i = nodes.length, node, value;
       
 12953 
       
 12954 			while (i--) {
       
 12955 				node = nodes[i];
       
 12956 				value = node.attr('class');
       
 12957 
       
 12958 				if (value) {
       
 12959 					value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
       
 12960 					node.attr('class', value.length > 0 ? value : null);
       
 12961 				}
       
 12962 			}
       
 12963 		});
       
 12964 
       
 12965 		// Remove bookmark elements
       
 12966 		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
       
 12967 			var i = nodes.length, node;
       
 12968 
       
 12969 			while (i--) {
       
 12970 				node = nodes[i];
       
 12971 
       
 12972 				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
       
 12973 					node.remove();
       
 12974 				}
       
 12975 			}
       
 12976 		});
       
 12977 
       
 12978 		htmlParser.addNodeFilter('noscript', function(nodes) {
       
 12979 			var i = nodes.length, node;
       
 12980 
       
 12981 			while (i--) {
       
 12982 				node = nodes[i].firstChild;
       
 12983 
       
 12984 				if (node) {
       
 12985 					node.value = Entities.decode(node.value);
       
 12986 				}
       
 12987 			}
       
 12988 		});
       
 12989 
       
 12990 		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
       
 12991 		htmlParser.addNodeFilter('script,style', function(nodes, name) {
       
 12992 			var i = nodes.length, node, value, type;
       
 12993 
       
 12994 			function trim(value) {
       
 12995 				/*jshint maxlen:255 */
       
 12996 				/*eslint max-len:0 */
       
 12997 				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
       
 12998 						.replace(/^[\r\n]*|[\r\n]*$/g, '')
       
 12999 						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
       
 13000 						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
       
 13001 			}
       
 13002 
       
 13003 			while (i--) {
       
 13004 				node = nodes[i];
       
 13005 				value = node.firstChild ? node.firstChild.value : '';
       
 13006 
       
 13007 				if (name === "script") {
       
 13008 					// Remove mce- prefix from script elements and remove default type since the user specified
       
 13009 					// a script element without type attribute
       
 13010 					type = node.attr('type');
       
 13011 					if (type) {
       
 13012 						node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, ''));
       
 13013 					}
       
 13014 
       
 13015 					if (value.length > 0) {
       
 13016 						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
       
 13017 					}
       
 13018 				} else {
       
 13019 					if (value.length > 0) {
       
 13020 						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
       
 13021 					}
       
 13022 				}
       
 13023 			}
       
 13024 		});
       
 13025 
       
 13026 		// Convert comments to cdata and handle protected comments
       
 13027 		htmlParser.addNodeFilter('#comment', function(nodes) {
       
 13028 			var i = nodes.length, node;
       
 13029 
       
 13030 			while (i--) {
       
 13031 				node = nodes[i];
       
 13032 
       
 13033 				if (node.value.indexOf('[CDATA[') === 0) {
       
 13034 					node.name = '#cdata';
       
 13035 					node.type = 4;
       
 13036 					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
       
 13037 				} else if (node.value.indexOf('mce:protected ') === 0) {
       
 13038 					node.name = "#text";
       
 13039 					node.type = 3;
       
 13040 					node.raw = true;
       
 13041 					node.value = unescape(node.value).substr(14);
       
 13042 				}
       
 13043 			}
       
 13044 		});
       
 13045 
       
 13046 		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
       
 13047 			var i = nodes.length, node;
       
 13048 
       
 13049 			while (i--) {
       
 13050 				node = nodes[i];
       
 13051 				if (node.type === 7) {
       
 13052 					node.remove();
       
 13053 				} else if (node.type === 1) {
       
 13054 					if (name === "input" && !("type" in node.attributes.map)) {
       
 13055 						node.attr('type', 'text');
       
 13056 					}
       
 13057 				}
       
 13058 			}
       
 13059 		});
       
 13060 
       
 13061 		// Fix list elements, TODO: Replace this later
       
 13062 		if (settings.fix_list_elements) {
       
 13063 			htmlParser.addNodeFilter('ul,ol', function(nodes) {
       
 13064 				var i = nodes.length, node, parentNode;
       
 13065 
       
 13066 				while (i--) {
       
 13067 					node = nodes[i];
       
 13068 					parentNode = node.parent;
       
 13069 
       
 13070 					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
       
 13071 						if (node.prev && node.prev.name === 'li') {
       
 13072 							node.prev.append(node);
       
 13073 						}
       
 13074 					}
       
 13075 				}
       
 13076 			});
       
 13077 		}
       
 13078 
       
 13079 		// Remove internal data attributes
       
 13080 		htmlParser.addAttributeFilter(
       
 13081 			'data-mce-src,data-mce-href,data-mce-style,' +
       
 13082 			'data-mce-selected,data-mce-expando,' +
       
 13083 			'data-mce-type,data-mce-resize',
       
 13084 
       
 13085 			function(nodes, name) {
       
 13086 				var i = nodes.length;
       
 13087 
       
 13088 				while (i--) {
       
 13089 					nodes[i].attr(name, null);
       
 13090 				}
       
 13091 			}
       
 13092 		);
       
 13093 
       
 13094 		// Return public methods
       
 13095 		return {
       
 13096 			/**
       
 13097 			 * Schema instance that was used to when the Serializer was constructed.
       
 13098 			 *
       
 13099 			 * @field {tinymce.html.Schema} schema
       
 13100 			 */
       
 13101 			schema: schema,
       
 13102 
       
 13103 			/**
       
 13104 			 * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
       
 13105 			 * and then execute the callback ones it has finished parsing the document.
       
 13106 			 *
       
 13107 			 * @example
       
 13108 			 * parser.addNodeFilter('p,h1', function(nodes, name) {
       
 13109 			 *		for (var i = 0; i < nodes.length; i++) {
       
 13110 			 *			console.log(nodes[i].name);
       
 13111 			 *		}
       
 13112 			 * });
       
 13113 			 * @method addNodeFilter
       
 13114 			 * @method {String} name Comma separated list of nodes to collect.
       
 13115 			 * @param {function} callback Callback function to execute once it has collected nodes.
       
 13116 			 */
       
 13117 			addNodeFilter: htmlParser.addNodeFilter,
       
 13118 
       
 13119 			/**
       
 13120 			 * Adds a attribute filter function to the parser used by the serializer, the parser will
       
 13121 			 * collect nodes that has the specified attributes
       
 13122 			 * and then execute the callback ones it has finished parsing the document.
       
 13123 			 *
       
 13124 			 * @example
       
 13125 			 * parser.addAttributeFilter('src,href', function(nodes, name) {
       
 13126 			 *		for (var i = 0; i < nodes.length; i++) {
       
 13127 			 *			console.log(nodes[i].name);
       
 13128 			 *		}
       
 13129 			 * });
       
 13130 			 * @method addAttributeFilter
       
 13131 			 * @method {String} name Comma separated list of nodes to collect.
       
 13132 			 * @param {function} callback Callback function to execute once it has collected nodes.
       
 13133 			 */
       
 13134 			addAttributeFilter: htmlParser.addAttributeFilter,
       
 13135 
       
 13136 			/**
       
 13137 			 * Serializes the specified browser DOM node into a HTML string.
       
 13138 			 *
       
 13139 			 * @method serialize
       
 13140 			 * @param {DOMNode} node DOM node to serialize.
       
 13141 			 * @param {Object} args Arguments option that gets passed to event handlers.
       
 13142 			 */
       
 13143 			serialize: function(node, args) {
       
 13144 				var self = this, impl, doc, oldDoc, htmlSerializer, content;
       
 13145 
       
 13146 				// Explorer won't clone contents of script and style and the
       
 13147 				// selected index of select elements are cleared on a clone operation.
       
 13148 				if (Env.ie && dom.select('script,style,select,map').length > 0) {
       
 13149 					content = node.innerHTML;
       
 13150 					node = node.cloneNode(false);
       
 13151 					dom.setHTML(node, content);
       
 13152 				} else {
       
 13153 					node = node.cloneNode(true);
       
 13154 				}
       
 13155 
       
 13156 				// Nodes needs to be attached to something in WebKit/Opera
       
 13157 				// This fix will make DOM ranges and make Sizzle happy!
       
 13158 				impl = node.ownerDocument.implementation;
       
 13159 				if (impl.createHTMLDocument) {
       
 13160 					// Create an empty HTML document
       
 13161 					doc = impl.createHTMLDocument("");
       
 13162 
       
 13163 					// Add the element or it's children if it's a body element to the new document
       
 13164 					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
       
 13165 						doc.body.appendChild(doc.importNode(node, true));
       
 13166 					});
       
 13167 
       
 13168 					// Grab first child or body element for serialization
       
 13169 					if (node.nodeName != 'BODY') {
       
 13170 						node = doc.body.firstChild;
       
 13171 					} else {
       
 13172 						node = doc.body;
       
 13173 					}
       
 13174 
       
 13175 					// set the new document in DOMUtils so createElement etc works
       
 13176 					oldDoc = dom.doc;
       
 13177 					dom.doc = doc;
       
 13178 				}
       
 13179 
       
 13180 				args = args || {};
       
 13181 				args.format = args.format || 'html';
       
 13182 
       
 13183 				// Don't wrap content if we want selected html
       
 13184 				if (args.selection) {
       
 13185 					args.forced_root_block = '';
       
 13186 				}
       
 13187 
       
 13188 				// Pre process
       
 13189 				if (!args.no_events) {
       
 13190 					args.node = node;
       
 13191 					self.onPreProcess(args);
       
 13192 				}
       
 13193 
       
 13194 				// Setup serializer
       
 13195 				htmlSerializer = new Serializer(settings, schema);
       
 13196 
       
 13197 				// Parse and serialize HTML
       
 13198 				args.content = htmlSerializer.serialize(
       
 13199 					htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
       
 13200 				);
       
 13201 
       
 13202 				// Replace all BOM characters for now until we can find a better solution
       
 13203 				if (!args.cleanup) {
       
 13204 					args.content = args.content.replace(/\uFEFF/g, '');
       
 13205 				}
       
 13206 
       
 13207 				// Post process
       
 13208 				if (!args.no_events) {
       
 13209 					self.onPostProcess(args);
       
 13210 				}
       
 13211 
       
 13212 				// Restore the old document if it was changed
       
 13213 				if (oldDoc) {
       
 13214 					dom.doc = oldDoc;
       
 13215 				}
       
 13216 
       
 13217 				args.node = null;
       
 13218 
       
 13219 				return args.content;
       
 13220 			},
       
 13221 
       
 13222 			/**
       
 13223 			 * Adds valid elements rules to the serializers schema instance this enables you to specify things
       
 13224 			 * like what elements should be outputted and what attributes specific elements might have.
       
 13225 			 * Consult the Wiki for more details on this format.
       
 13226 			 *
       
 13227 			 * @method addRules
       
 13228 			 * @param {String} rules Valid elements rules string to add to schema.
       
 13229 			 */
       
 13230 			addRules: function(rules) {
       
 13231 				schema.addValidElements(rules);
       
 13232 			},
       
 13233 
       
 13234 			/**
       
 13235 			 * Sets the valid elements rules to the serializers schema instance this enables you to specify things
       
 13236 			 * like what elements should be outputted and what attributes specific elements might have.
       
 13237 			 * Consult the Wiki for more details on this format.
       
 13238 			 *
       
 13239 			 * @method setRules
       
 13240 			 * @param {String} rules Valid elements rules string.
       
 13241 			 */
       
 13242 			setRules: function(rules) {
       
 13243 				schema.setValidElements(rules);
       
 13244 			},
       
 13245 
       
 13246 			onPreProcess: function(args) {
       
 13247 				if (editor) {
       
 13248 					editor.fire('PreProcess', args);
       
 13249 				}
       
 13250 			},
       
 13251 
       
 13252 			onPostProcess: function(args) {
       
 13253 				if (editor) {
       
 13254 					editor.fire('PostProcess', args);
       
 13255 				}
       
 13256 			}
       
 13257 		};
       
 13258 	};
       
 13259 });
       
 13260 
       
 13261 // Included from: js/tinymce/classes/dom/TridentSelection.js
       
 13262 
       
 13263 /**
       
 13264  * TridentSelection.js
       
 13265  *
       
 13266  * Copyright, Moxiecode Systems AB
       
 13267  * Released under LGPL License.
       
 13268  *
       
 13269  * License: http://www.tinymce.com/license
       
 13270  * Contributing: http://www.tinymce.com/contributing
       
 13271  */
       
 13272 
       
 13273 /**
       
 13274  * Selection class for old explorer versions. This one fakes the
       
 13275  * native selection object available on modern browsers.
       
 13276  *
       
 13277  * @class tinymce.dom.TridentSelection
       
 13278  */
       
 13279 define("tinymce/dom/TridentSelection", [], function() {
       
 13280 	function Selection(selection) {
       
 13281 		var self = this, dom = selection.dom, FALSE = false;
       
 13282 
       
 13283 		function getPosition(rng, start) {
       
 13284 			var checkRng, startIndex = 0, endIndex, inside,
       
 13285 				children, child, offset, index, position = -1, parent;
       
 13286 
       
 13287 			// Setup test range, collapse it and get the parent
       
 13288 			checkRng = rng.duplicate();
       
 13289 			checkRng.collapse(start);
       
 13290 			parent = checkRng.parentElement();
       
 13291 
       
 13292 			// Check if the selection is within the right document
       
 13293 			if (parent.ownerDocument !== selection.dom.doc) {
       
 13294 				return;
       
 13295 			}
       
 13296 
       
 13297 			// IE will report non editable elements as it's parent so look for an editable one
       
 13298 			while (parent.contentEditable === "false") {
       
 13299 				parent = parent.parentNode;
       
 13300 			}
       
 13301 
       
 13302 			// If parent doesn't have any children then return that we are inside the element
       
 13303 			if (!parent.hasChildNodes()) {
       
 13304 				return {node: parent, inside: 1};
       
 13305 			}
       
 13306 
       
 13307 			// Setup node list and endIndex
       
 13308 			children = parent.children;
       
 13309 			endIndex = children.length - 1;
       
 13310 
       
 13311 			// Perform a binary search for the position
       
 13312 			while (startIndex <= endIndex) {
       
 13313 				index = Math.floor((startIndex + endIndex) / 2);
       
 13314 
       
 13315 				// Move selection to node and compare the ranges
       
 13316 				child = children[index];
       
 13317 				checkRng.moveToElementText(child);
       
 13318 				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
       
 13319 
       
 13320 				// Before/after or an exact match
       
 13321 				if (position > 0) {
       
 13322 					endIndex = index - 1;
       
 13323 				} else if (position < 0) {
       
 13324 					startIndex = index + 1;
       
 13325 				} else {
       
 13326 					return {node: child};
       
 13327 				}
       
 13328 			}
       
 13329 
       
 13330 			// Check if child position is before or we didn't find a position
       
 13331 			if (position < 0) {
       
 13332 				// No element child was found use the parent element and the offset inside that
       
 13333 				if (!child) {
       
 13334 					checkRng.moveToElementText(parent);
       
 13335 					checkRng.collapse(true);
       
 13336 					child = parent;
       
 13337 					inside = true;
       
 13338 				} else {
       
 13339 					checkRng.collapse(false);
       
 13340 				}
       
 13341 
       
 13342 				// Walk character by character in text node until we hit the selected range endpoint,
       
 13343 				// hit the end of document or parent isn't the right one
       
 13344 				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
       
 13345 				offset = 0;
       
 13346 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
       
 13347 					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
       
 13348 						break;
       
 13349 					}
       
 13350 
       
 13351 					offset++;
       
 13352 				}
       
 13353 			} else {
       
 13354 				// Child position is after the selection endpoint
       
 13355 				checkRng.collapse(true);
       
 13356 
       
 13357 				// Walk character by character in text node until we hit the selected range endpoint, hit
       
 13358 				// the end of document or parent isn't the right one
       
 13359 				offset = 0;
       
 13360 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
       
 13361 					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
       
 13362 						break;
       
 13363 					}
       
 13364 
       
 13365 					offset++;
       
 13366 				}
       
 13367 			}
       
 13368 
       
 13369 			return {node: child, position: position, offset: offset, inside: inside};
       
 13370 		}
       
 13371 
       
 13372 		// Returns a W3C DOM compatible range object by using the IE Range API
       
 13373 		function getRange() {
       
 13374 			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
       
 13375 
       
 13376 			// If selection is outside the current document just return an empty range
       
 13377 			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
       
 13378 			if (element.ownerDocument != dom.doc) {
       
 13379 				return domRange;
       
 13380 			}
       
 13381 
       
 13382 			collapsed = selection.isCollapsed();
       
 13383 
       
 13384 			// Handle control selection
       
 13385 			if (ieRange.item) {
       
 13386 				domRange.setStart(element.parentNode, dom.nodeIndex(element));
       
 13387 				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
       
 13388 
       
 13389 				return domRange;
       
 13390 			}
       
 13391 
       
 13392 			function findEndPoint(start) {
       
 13393 				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
       
 13394 
       
 13395 				container = endPoint.node;
       
 13396 				offset = endPoint.offset;
       
 13397 
       
 13398 				if (endPoint.inside && !container.hasChildNodes()) {
       
 13399 					domRange[start ? 'setStart' : 'setEnd'](container, 0);
       
 13400 					return;
       
 13401 				}
       
 13402 
       
 13403 				if (offset === undef) {
       
 13404 					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
       
 13405 					return;
       
 13406 				}
       
 13407 
       
 13408 				if (endPoint.position < 0) {
       
 13409 					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
       
 13410 
       
 13411 					if (!sibling) {
       
 13412 						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
       
 13413 						return;
       
 13414 					}
       
 13415 
       
 13416 					if (!offset) {
       
 13417 						if (sibling.nodeType == 3) {
       
 13418 							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
       
 13419 						} else {
       
 13420 							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
       
 13421 						}
       
 13422 
       
 13423 						return;
       
 13424 					}
       
 13425 
       
 13426 					// Find the text node and offset
       
 13427 					while (sibling) {
       
 13428 						if (sibling.nodeType == 3) {
       
 13429 							nodeValue = sibling.nodeValue;
       
 13430 							textNodeOffset += nodeValue.length;
       
 13431 
       
 13432 							// We are at or passed the position we where looking for
       
 13433 							if (textNodeOffset >= offset) {
       
 13434 								container = sibling;
       
 13435 								textNodeOffset -= offset;
       
 13436 								textNodeOffset = nodeValue.length - textNodeOffset;
       
 13437 								break;
       
 13438 							}
       
 13439 						}
       
 13440 
       
 13441 						sibling = sibling.nextSibling;
       
 13442 					}
       
 13443 				} else {
       
 13444 					// Find the text node and offset
       
 13445 					sibling = container.previousSibling;
       
 13446 
       
 13447 					if (!sibling) {
       
 13448 						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
       
 13449 					}
       
 13450 
       
 13451 					// If there isn't any text to loop then use the first position
       
 13452 					if (!offset) {
       
 13453 						if (container.nodeType == 3) {
       
 13454 							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
       
 13455 						} else {
       
 13456 							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
       
 13457 						}
       
 13458 
       
 13459 						return;
       
 13460 					}
       
 13461 
       
 13462 					while (sibling) {
       
 13463 						if (sibling.nodeType == 3) {
       
 13464 							textNodeOffset += sibling.nodeValue.length;
       
 13465 
       
 13466 							// We are at or passed the position we where looking for
       
 13467 							if (textNodeOffset >= offset) {
       
 13468 								container = sibling;
       
 13469 								textNodeOffset -= offset;
       
 13470 								break;
       
 13471 							}
       
 13472 						}
       
 13473 
       
 13474 						sibling = sibling.previousSibling;
       
 13475 					}
       
 13476 				}
       
 13477 
       
 13478 				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
       
 13479 			}
       
 13480 
       
 13481 			try {
       
 13482 				// Find start point
       
 13483 				findEndPoint(true);
       
 13484 
       
 13485 				// Find end point if needed
       
 13486 				if (!collapsed) {
       
 13487 					findEndPoint();
       
 13488 				}
       
 13489 			} catch (ex) {
       
 13490 				// IE has a nasty bug where text nodes might throw "invalid argument" when you
       
 13491 				// access the nodeValue or other properties of text nodes. This seems to happend when
       
 13492 				// text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
       
 13493 				if (ex.number == -2147024809) {
       
 13494 					// Get the current selection
       
 13495 					bookmark = self.getBookmark(2);
       
 13496 
       
 13497 					// Get start element
       
 13498 					tmpRange = ieRange.duplicate();
       
 13499 					tmpRange.collapse(true);
       
 13500 					element = tmpRange.parentElement();
       
 13501 
       
 13502 					// Get end element
       
 13503 					if (!collapsed) {
       
 13504 						tmpRange = ieRange.duplicate();
       
 13505 						tmpRange.collapse(false);
       
 13506 						element2 = tmpRange.parentElement();
       
 13507 						element2.innerHTML = element2.innerHTML;
       
 13508 					}
       
 13509 
       
 13510 					// Remove the broken elements
       
 13511 					element.innerHTML = element.innerHTML;
       
 13512 
       
 13513 					// Restore the selection
       
 13514 					self.moveToBookmark(bookmark);
       
 13515 
       
 13516 					// Since the range has moved we need to re-get it
       
 13517 					ieRange = selection.getRng();
       
 13518 
       
 13519 					// Find start point
       
 13520 					findEndPoint(true);
       
 13521 
       
 13522 					// Find end point if needed
       
 13523 					if (!collapsed) {
       
 13524 						findEndPoint();
       
 13525 					}
       
 13526 				} else {
       
 13527 					throw ex; // Throw other errors
       
 13528 				}
       
 13529 			}
       
 13530 
       
 13531 			return domRange;
       
 13532 		}
       
 13533 
       
 13534 		this.getBookmark = function(type) {
       
 13535 			var rng = selection.getRng(), bookmark = {};
       
 13536 
       
 13537 			function getIndexes(node) {
       
 13538 				var parent, root, children, i, indexes = [];
       
 13539 
       
 13540 				parent = node.parentNode;
       
 13541 				root = dom.getRoot().parentNode;
       
 13542 
       
 13543 				while (parent != root && parent.nodeType !== 9) {
       
 13544 					children = parent.children;
       
 13545 
       
 13546 					i = children.length;
       
 13547 					while (i--) {
       
 13548 						if (node === children[i]) {
       
 13549 							indexes.push(i);
       
 13550 							break;
       
 13551 						}
       
 13552 					}
       
 13553 
       
 13554 					node = parent;
       
 13555 					parent = parent.parentNode;
       
 13556 				}
       
 13557 
       
 13558 				return indexes;
       
 13559 			}
       
 13560 
       
 13561 			function getBookmarkEndPoint(start) {
       
 13562 				var position;
       
 13563 
       
 13564 				position = getPosition(rng, start);
       
 13565 				if (position) {
       
 13566 					return {
       
 13567 						position: position.position,
       
 13568 						offset: position.offset,
       
 13569 						indexes: getIndexes(position.node),
       
 13570 						inside: position.inside
       
 13571 					};
       
 13572 				}
       
 13573 			}
       
 13574 
       
 13575 			// Non ubstructive bookmark
       
 13576 			if (type === 2) {
       
 13577 				// Handle text selection
       
 13578 				if (!rng.item) {
       
 13579 					bookmark.start = getBookmarkEndPoint(true);
       
 13580 
       
 13581 					if (!selection.isCollapsed()) {
       
 13582 						bookmark.end = getBookmarkEndPoint();
       
 13583 					}
       
 13584 				} else {
       
 13585 					bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
       
 13586 				}
       
 13587 			}
       
 13588 
       
 13589 			return bookmark;
       
 13590 		};
       
 13591 
       
 13592 		this.moveToBookmark = function(bookmark) {
       
 13593 			var rng, body = dom.doc.body;
       
 13594 
       
 13595 			function resolveIndexes(indexes) {
       
 13596 				var node, i, idx, children;
       
 13597 
       
 13598 				node = dom.getRoot();
       
 13599 				for (i = indexes.length - 1; i >= 0; i--) {
       
 13600 					children = node.children;
       
 13601 					idx = indexes[i];
       
 13602 
       
 13603 					if (idx <= children.length - 1) {
       
 13604 						node = children[idx];
       
 13605 					}
       
 13606 				}
       
 13607 
       
 13608 				return node;
       
 13609 			}
       
 13610 
       
 13611 			function setBookmarkEndPoint(start) {
       
 13612 				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
       
 13613 
       
 13614 				if (endPoint) {
       
 13615 					moveLeft = endPoint.position > 0;
       
 13616 
       
 13617 					moveRng = body.createTextRange();
       
 13618 					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
       
 13619 
       
 13620 					offset = endPoint.offset;
       
 13621 					if (offset !== undef) {
       
 13622 						moveRng.collapse(endPoint.inside || moveLeft);
       
 13623 						moveRng.moveStart('character', moveLeft ? -offset : offset);
       
 13624 					} else {
       
 13625 						moveRng.collapse(start);
       
 13626 					}
       
 13627 
       
 13628 					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
       
 13629 
       
 13630 					if (start) {
       
 13631 						rng.collapse(true);
       
 13632 					}
       
 13633 				}
       
 13634 			}
       
 13635 
       
 13636 			if (bookmark.start) {
       
 13637 				if (bookmark.start.ctrl) {
       
 13638 					rng = body.createControlRange();
       
 13639 					rng.addElement(resolveIndexes(bookmark.start.indexes));
       
 13640 					rng.select();
       
 13641 				} else {
       
 13642 					rng = body.createTextRange();
       
 13643 					setBookmarkEndPoint(true);
       
 13644 					setBookmarkEndPoint();
       
 13645 					rng.select();
       
 13646 				}
       
 13647 			}
       
 13648 		};
       
 13649 
       
 13650 		this.addRange = function(rng) {
       
 13651 			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
       
 13652 				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
       
 13653 
       
 13654 			function setEndPoint(start) {
       
 13655 				var container, offset, marker, tmpRng, nodes;
       
 13656 
       
 13657 				marker = dom.create('a');
       
 13658 				container = start ? startContainer : endContainer;
       
 13659 				offset = start ? startOffset : endOffset;
       
 13660 				tmpRng = ieRng.duplicate();
       
 13661 
       
 13662 				if (container == doc || container == doc.documentElement) {
       
 13663 					container = body;
       
 13664 					offset = 0;
       
 13665 				}
       
 13666 
       
 13667 				if (container.nodeType == 3) {
       
 13668 					container.parentNode.insertBefore(marker, container);
       
 13669 					tmpRng.moveToElementText(marker);
       
 13670 					tmpRng.moveStart('character', offset);
       
 13671 					dom.remove(marker);
       
 13672 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
       
 13673 				} else {
       
 13674 					nodes = container.childNodes;
       
 13675 
       
 13676 					if (nodes.length) {
       
 13677 						if (offset >= nodes.length) {
       
 13678 							dom.insertAfter(marker, nodes[nodes.length - 1]);
       
 13679 						} else {
       
 13680 							container.insertBefore(marker, nodes[offset]);
       
 13681 						}
       
 13682 
       
 13683 						tmpRng.moveToElementText(marker);
       
 13684 					} else if (container.canHaveHTML) {
       
 13685 						// Empty node selection for example <div>|</div>
       
 13686 						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
       
 13687 						container.innerHTML = '<span>&#xFEFF;</span>';
       
 13688 						marker = container.firstChild;
       
 13689 						tmpRng.moveToElementText(marker);
       
 13690 						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
       
 13691 					}
       
 13692 
       
 13693 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
       
 13694 					dom.remove(marker);
       
 13695 				}
       
 13696 			}
       
 13697 
       
 13698 			// Setup some shorter versions
       
 13699 			startContainer = rng.startContainer;
       
 13700 			startOffset = rng.startOffset;
       
 13701 			endContainer = rng.endContainer;
       
 13702 			endOffset = rng.endOffset;
       
 13703 			ieRng = body.createTextRange();
       
 13704 
       
 13705 			// If single element selection then try making a control selection out of it
       
 13706 			if (startContainer == endContainer && startContainer.nodeType == 1) {
       
 13707 				// Trick to place the caret inside an empty block element like <p></p>
       
 13708 				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
       
 13709 					if (startContainer.canHaveHTML) {
       
 13710 						// Check if previous sibling is an empty block if it is then we need to render it
       
 13711 						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
       
 13712 						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
       
 13713 						sibling = startContainer.previousSibling;
       
 13714 						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
       
 13715 							sibling.innerHTML = '&#xFEFF;';
       
 13716 						} else {
       
 13717 							sibling = null;
       
 13718 						}
       
 13719 
       
 13720 						startContainer.innerHTML = '<span>&#xFEFF;</span><span>&#xFEFF;</span>';
       
 13721 						ieRng.moveToElementText(startContainer.lastChild);
       
 13722 						ieRng.select();
       
 13723 						dom.doc.selection.clear();
       
 13724 						startContainer.innerHTML = '';
       
 13725 
       
 13726 						if (sibling) {
       
 13727 							sibling.innerHTML = '';
       
 13728 						}
       
 13729 						return;
       
 13730 					} else {
       
 13731 						startOffset = dom.nodeIndex(startContainer);
       
 13732 						startContainer = startContainer.parentNode;
       
 13733 					}
       
 13734 				}
       
 13735 
       
 13736 				if (startOffset == endOffset - 1) {
       
 13737 					try {
       
 13738 						ctrlElm = startContainer.childNodes[startOffset];
       
 13739 						ctrlRng = body.createControlRange();
       
 13740 						ctrlRng.addElement(ctrlElm);
       
 13741 						ctrlRng.select();
       
 13742 
       
 13743 						// Check if the range produced is on the correct element and is a control range
       
 13744 						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
       
 13745 						nativeRng = selection.getRng();
       
 13746 						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
       
 13747 							return;
       
 13748 						}
       
 13749 					} catch (ex) {
       
 13750 						// Ignore
       
 13751 					}
       
 13752 				}
       
 13753 			}
       
 13754 
       
 13755 			// Set start/end point of selection
       
 13756 			setEndPoint(true);
       
 13757 			setEndPoint();
       
 13758 
       
 13759 			// Select the new range and scroll it into view
       
 13760 			ieRng.select();
       
 13761 		};
       
 13762 
       
 13763 		// Expose range method
       
 13764 		this.getRangeAt = getRange;
       
 13765 	}
       
 13766 
       
 13767 	return Selection;
       
 13768 });
       
 13769 
       
 13770 // Included from: js/tinymce/classes/util/VK.js
       
 13771 
       
 13772 /**
       
 13773  * VK.js
       
 13774  *
       
 13775  * Copyright, Moxiecode Systems AB
       
 13776  * Released under LGPL License.
       
 13777  *
       
 13778  * License: http://www.tinymce.com/license
       
 13779  * Contributing: http://www.tinymce.com/contributing
       
 13780  */
       
 13781 
       
 13782 /**
       
 13783  * This file exposes a set of the common KeyCodes for use.  Please grow it as needed.
       
 13784  */
       
 13785 define("tinymce/util/VK", [
       
 13786 	"tinymce/Env"
       
 13787 ], function(Env) {
       
 13788 	return {
       
 13789 		BACKSPACE: 8,
       
 13790 		DELETE: 46,
       
 13791 		DOWN: 40,
       
 13792 		ENTER: 13,
       
 13793 		LEFT: 37,
       
 13794 		RIGHT: 39,
       
 13795 		SPACEBAR: 32,
       
 13796 		TAB: 9,
       
 13797 		UP: 38,
       
 13798 
       
 13799 		modifierPressed: function(e) {
       
 13800 			return e.shiftKey || e.ctrlKey || e.altKey || this.metaKeyPressed(e);
       
 13801 		},
       
 13802 
       
 13803 		metaKeyPressed: function(e) {
       
 13804 			// Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states
       
 13805 			return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey);
       
 13806 		}
       
 13807 	};
       
 13808 });
       
 13809 
       
 13810 // Included from: js/tinymce/classes/dom/ControlSelection.js
       
 13811 
       
 13812 /**
       
 13813  * ControlSelection.js
       
 13814  *
       
 13815  * Copyright, Moxiecode Systems AB
       
 13816  * Released under LGPL License.
       
 13817  *
       
 13818  * License: http://www.tinymce.com/license
       
 13819  * Contributing: http://www.tinymce.com/contributing
       
 13820  */
       
 13821 
       
 13822 /**
       
 13823  * This class handles control selection of elements. Controls are elements
       
 13824  * that can be resized and needs to be selected as a whole. It adds custom resize handles
       
 13825  * to all browser engines that support properly disabling the built in resize logic.
       
 13826  *
       
 13827  * @class tinymce.dom.ControlSelection
       
 13828  */
       
 13829 define("tinymce/dom/ControlSelection", [
       
 13830 	"tinymce/util/VK",
       
 13831 	"tinymce/util/Tools",
       
 13832 	"tinymce/Env"
       
 13833 ], function(VK, Tools, Env) {
       
 13834 	return function(selection, editor) {
       
 13835 		var dom = editor.dom, each = Tools.each;
       
 13836 		var selectedElm, selectedElmGhost, resizeHelper, resizeHandles, selectedHandle, lastMouseDownEvent;
       
 13837 		var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
       
 13838 		var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
       
 13839 		var abs = Math.abs, round = Math.round, rootElement = editor.getBody(), startScrollWidth, startScrollHeight;
       
 13840 
       
 13841 		// Details about each resize handle how to scale etc
       
 13842 		resizeHandles = {
       
 13843 			// Name: x multiplier, y multiplier, delta size x, delta size y
       
 13844 			n: [0.5, 0, 0, -1],
       
 13845 			e: [1, 0.5, 1, 0],
       
 13846 			s: [0.5, 1, 0, 1],
       
 13847 			w: [0, 0.5, -1, 0],
       
 13848 			nw: [0, 0, -1, -1],
       
 13849 			ne: [1, 0, 1, -1],
       
 13850 			se: [1, 1, 1, 1],
       
 13851 			sw: [0, 1, -1, 1]
       
 13852 		};
       
 13853 
       
 13854 		// Add CSS for resize handles, cloned element and selected
       
 13855 		var rootClass = '.mce-content-body';
       
 13856 		editor.contentStyles.push(
       
 13857 			rootClass + ' div.mce-resizehandle {' +
       
 13858 				'position: absolute;' +
       
 13859 				'border: 1px solid black;' +
       
 13860 				'background: #FFF;' +
       
 13861 				'width: 5px;' +
       
 13862 				'height: 5px;' +
       
 13863 				'z-index: 10000' +
       
 13864 			'}' +
       
 13865 			rootClass + ' .mce-resizehandle:hover {' +
       
 13866 				'background: #000' +
       
 13867 			'}' +
       
 13868 			rootClass + ' img[data-mce-selected], hr[data-mce-selected] {' +
       
 13869 				'outline: 1px solid black;' +
       
 13870 				'resize: none' + // Have been talks about implementing this in browsers
       
 13871 			'}' +
       
 13872 			rootClass + ' .mce-clonedresizable {' +
       
 13873 				'position: absolute;' +
       
 13874 				(Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
       
 13875 				'opacity: .5;' +
       
 13876 				'filter: alpha(opacity=50);' +
       
 13877 				'z-index: 10000' +
       
 13878 			'}' +
       
 13879 			rootClass + ' .mce-resize-helper {' +
       
 13880 				'background: #555;' +
       
 13881 				'background: rgba(0,0,0,0.75);' +
       
 13882 				'border-radius: 3px;' +
       
 13883 				'border: 1px;' +
       
 13884 				'color: white;' +
       
 13885 				'display: none;' +
       
 13886 				'font-family: sans-serif;' +
       
 13887 				'font-size: 12px;' +
       
 13888 				'white-space: nowrap;' +
       
 13889 				'line-height: 14px;' +
       
 13890 				'margin: 5px 10px;' +
       
 13891 				'padding: 5px;' +
       
 13892 				'position: absolute;' +
       
 13893 				'z-index: 10001' +
       
 13894 			'}'
       
 13895 		);
       
 13896 
       
 13897 		function isResizable(elm) {
       
 13898 			var selector = editor.settings.object_resizing;
       
 13899 
       
 13900 			if (selector === false || Env.iOS) {
       
 13901 				return false;
       
 13902 			}
       
 13903 
       
 13904 			if (typeof selector != 'string') {
       
 13905 				selector = 'table,img,div';
       
 13906 			}
       
 13907 
       
 13908 			if (elm.getAttribute('data-mce-resize') === 'false') {
       
 13909 				return false;
       
 13910 			}
       
 13911 
       
 13912 			return editor.dom.is(elm, selector);
       
 13913 		}
       
 13914 
       
 13915 		function resizeGhostElement(e) {
       
 13916 			var deltaX, deltaY, proportional;
       
 13917 			var resizeHelperX, resizeHelperY;
       
 13918 
       
 13919 			// Calc new width/height
       
 13920 			deltaX = e.screenX - startX;
       
 13921 			deltaY = e.screenY - startY;
       
 13922 
       
 13923 			// Calc new size
       
 13924 			width = deltaX * selectedHandle[2] + startW;
       
 13925 			height = deltaY * selectedHandle[3] + startH;
       
 13926 
       
 13927 			// Never scale down lower than 5 pixels
       
 13928 			width = width < 5 ? 5 : width;
       
 13929 			height = height < 5 ? 5 : height;
       
 13930 
       
 13931 			if (selectedElm.nodeName == "IMG" && editor.settings.resize_img_proportional !== false) {
       
 13932 				proportional = !VK.modifierPressed(e);
       
 13933 			} else {
       
 13934 				proportional = VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0);
       
 13935 			}
       
 13936 
       
 13937 			// Constrain proportions
       
 13938 			if (proportional) {
       
 13939 				if (abs(deltaX) > abs(deltaY)) {
       
 13940 					height = round(width * ratio);
       
 13941 					width = round(height / ratio);
       
 13942 				} else {
       
 13943 					width = round(height / ratio);
       
 13944 					height = round(width * ratio);
       
 13945 				}
       
 13946 			}
       
 13947 
       
 13948 			// Update ghost size
       
 13949 			dom.setStyles(selectedElmGhost, {
       
 13950 				width: width,
       
 13951 				height: height
       
 13952 			});
       
 13953 
       
 13954 			// Update resize helper position
       
 13955 			resizeHelperX = selectedHandle.startPos.x + deltaX;
       
 13956 			resizeHelperY = selectedHandle.startPos.y + deltaY;
       
 13957 			resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0;
       
 13958 			resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0;
       
 13959 
       
 13960 			dom.setStyles(resizeHelper, {
       
 13961 				left: resizeHelperX,
       
 13962 				top: resizeHelperY,
       
 13963 				display: 'block'
       
 13964 			});
       
 13965 
       
 13966 			resizeHelper.innerHTML = width + ' &times; ' + height;
       
 13967 
       
 13968 			// Update ghost X position if needed
       
 13969 			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
       
 13970 				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
       
 13971 			}
       
 13972 
       
 13973 			// Update ghost Y position if needed
       
 13974 			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
       
 13975 				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
       
 13976 			}
       
 13977 
       
 13978 			// Calculate how must overflow we got
       
 13979 			deltaX = rootElement.scrollWidth - startScrollWidth;
       
 13980 			deltaY = rootElement.scrollHeight - startScrollHeight;
       
 13981 
       
 13982 			// Re-position the resize helper based on the overflow
       
 13983 			if (deltaX + deltaY !== 0) {
       
 13984 				dom.setStyles(resizeHelper, {
       
 13985 					left: resizeHelperX - deltaX,
       
 13986 					top: resizeHelperY - deltaY
       
 13987 				});
       
 13988 			}
       
 13989 
       
 13990 			if (!resizeStarted) {
       
 13991 				editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
       
 13992 				resizeStarted = true;
       
 13993 			}
       
 13994 		}
       
 13995 
       
 13996 		function endGhostResize() {
       
 13997 			resizeStarted = false;
       
 13998 
       
 13999 			function setSizeProp(name, value) {
       
 14000 				if (value) {
       
 14001 					// Resize by using style or attribute
       
 14002 					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
       
 14003 						dom.setStyle(selectedElm, name, value);
       
 14004 					} else {
       
 14005 						dom.setAttrib(selectedElm, name, value);
       
 14006 					}
       
 14007 				}
       
 14008 			}
       
 14009 
       
 14010 			// Set width/height properties
       
 14011 			setSizeProp('width', width);
       
 14012 			setSizeProp('height', height);
       
 14013 
       
 14014 			dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
       
 14015 			dom.unbind(editableDoc, 'mouseup', endGhostResize);
       
 14016 
       
 14017 			if (rootDocument != editableDoc) {
       
 14018 				dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
       
 14019 				dom.unbind(rootDocument, 'mouseup', endGhostResize);
       
 14020 			}
       
 14021 
       
 14022 			// Remove ghost/helper and update resize handle positions
       
 14023 			dom.remove(selectedElmGhost);
       
 14024 			dom.remove(resizeHelper);
       
 14025 
       
 14026 			if (!isIE || selectedElm.nodeName == "TABLE") {
       
 14027 				showResizeRect(selectedElm);
       
 14028 			}
       
 14029 
       
 14030 			editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
       
 14031 			dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));
       
 14032 			editor.nodeChanged();
       
 14033 		}
       
 14034 
       
 14035 		function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
       
 14036 			var position, targetWidth, targetHeight, e, rect;
       
 14037 
       
 14038 			unbindResizeHandleEvents();
       
 14039 
       
 14040 			// Get position and size of target
       
 14041 			position = dom.getPos(targetElm, rootElement);
       
 14042 			selectedElmX = position.x;
       
 14043 			selectedElmY = position.y;
       
 14044 			rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
       
 14045 			targetWidth = rect.width || (rect.right - rect.left);
       
 14046 			targetHeight = rect.height || (rect.bottom - rect.top);
       
 14047 
       
 14048 			// Reset width/height if user selects a new image/table
       
 14049 			if (selectedElm != targetElm) {
       
 14050 				detachResizeStartListener();
       
 14051 				selectedElm = targetElm;
       
 14052 				width = height = 0;
       
 14053 			}
       
 14054 
       
 14055 			// Makes it possible to disable resizing
       
 14056 			e = editor.fire('ObjectSelected', {target: targetElm});
       
 14057 
       
 14058 			if (isResizable(targetElm) && !e.isDefaultPrevented()) {
       
 14059 				each(resizeHandles, function(handle, name) {
       
 14060 					var handleElm, handlerContainerElm;
       
 14061 
       
 14062 					function startDrag(e) {
       
 14063 						startX = e.screenX;
       
 14064 						startY = e.screenY;
       
 14065 						startW = selectedElm.clientWidth;
       
 14066 						startH = selectedElm.clientHeight;
       
 14067 						ratio = startH / startW;
       
 14068 						selectedHandle = handle;
       
 14069 
       
 14070 						handle.startPos = {
       
 14071 							x: targetWidth * handle[0] + selectedElmX,
       
 14072 							y: targetHeight * handle[1] + selectedElmY
       
 14073 						};
       
 14074 
       
 14075 						startScrollWidth = rootElement.scrollWidth;
       
 14076 						startScrollHeight = rootElement.scrollHeight;
       
 14077 
       
 14078 						selectedElmGhost = selectedElm.cloneNode(true);
       
 14079 						dom.addClass(selectedElmGhost, 'mce-clonedresizable');
       
 14080 						dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all');
       
 14081 						selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
       
 14082 						selectedElmGhost.unSelectabe = true;
       
 14083 						dom.setStyles(selectedElmGhost, {
       
 14084 							left: selectedElmX,
       
 14085 							top: selectedElmY,
       
 14086 							margin: 0
       
 14087 						});
       
 14088 
       
 14089 						selectedElmGhost.removeAttribute('data-mce-selected');
       
 14090 						rootElement.appendChild(selectedElmGhost);
       
 14091 
       
 14092 						dom.bind(editableDoc, 'mousemove', resizeGhostElement);
       
 14093 						dom.bind(editableDoc, 'mouseup', endGhostResize);
       
 14094 
       
 14095 						if (rootDocument != editableDoc) {
       
 14096 							dom.bind(rootDocument, 'mousemove', resizeGhostElement);
       
 14097 							dom.bind(rootDocument, 'mouseup', endGhostResize);
       
 14098 						}
       
 14099 
       
 14100 						resizeHelper = dom.add(rootElement, 'div', {
       
 14101 							'class': 'mce-resize-helper',
       
 14102 							'data-mce-bogus': 'all'
       
 14103 						}, startW + ' &times; ' + startH);
       
 14104 					}
       
 14105 
       
 14106 					if (mouseDownHandleName) {
       
 14107 						// Drag started by IE native resizestart
       
 14108 						if (name == mouseDownHandleName) {
       
 14109 							startDrag(mouseDownEvent);
       
 14110 						}
       
 14111 
       
 14112 						return;
       
 14113 					}
       
 14114 
       
 14115 					// Get existing or render resize handle
       
 14116 					handleElm = dom.get('mceResizeHandle' + name);
       
 14117 					if (!handleElm) {
       
 14118 						handlerContainerElm = rootElement;
       
 14119 
       
 14120 						handleElm = dom.add(handlerContainerElm, 'div', {
       
 14121 							id: 'mceResizeHandle' + name,
       
 14122 							'data-mce-bogus': 'all',
       
 14123 							'class': 'mce-resizehandle',
       
 14124 							unselectable: true,
       
 14125 							style: 'cursor:' + name + '-resize; margin:0; padding:0'
       
 14126 						});
       
 14127 
       
 14128 						// Hides IE move layer cursor
       
 14129 						// If we set it on Chrome we get this wounderful bug: #6725
       
 14130 						if (Env.ie) {
       
 14131 							handleElm.contentEditable = false;
       
 14132 						}
       
 14133 					} else {
       
 14134 						dom.show(handleElm);
       
 14135 					}
       
 14136 
       
 14137 					if (!handle.elm) {
       
 14138 						dom.bind(handleElm, 'mousedown', function(e) {
       
 14139 							e.stopImmediatePropagation();
       
 14140 							e.preventDefault();
       
 14141 							startDrag(e);
       
 14142 						});
       
 14143 
       
 14144 						handle.elm = handleElm;
       
 14145 					}
       
 14146 
       
 14147 					// Position element
       
 14148 					dom.setStyles(handleElm, {
       
 14149 						left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
       
 14150 						top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
       
 14151 					});
       
 14152 				});
       
 14153 			} else {
       
 14154 				hideResizeRect();
       
 14155 			}
       
 14156 
       
 14157 			selectedElm.setAttribute('data-mce-selected', '1');
       
 14158 		}
       
 14159 
       
 14160 		function hideResizeRect() {
       
 14161 			var name, handleElm;
       
 14162 
       
 14163 			unbindResizeHandleEvents();
       
 14164 
       
 14165 			if (selectedElm) {
       
 14166 				selectedElm.removeAttribute('data-mce-selected');
       
 14167 			}
       
 14168 
       
 14169 			for (name in resizeHandles) {
       
 14170 				handleElm = dom.get('mceResizeHandle' + name);
       
 14171 				if (handleElm) {
       
 14172 					dom.unbind(handleElm);
       
 14173 					dom.remove(handleElm);
       
 14174 				}
       
 14175 			}
       
 14176 		}
       
 14177 
       
 14178 		function updateResizeRect(e) {
       
 14179 			var startElm, controlElm;
       
 14180 
       
 14181 			function isChildOrEqual(node, parent) {
       
 14182 				if (node) {
       
 14183 					do {
       
 14184 						if (node === parent) {
       
 14185 							return true;
       
 14186 						}
       
 14187 					} while ((node = node.parentNode));
       
 14188 				}
       
 14189 			}
       
 14190 
       
 14191 			// Ignore all events while resizing
       
 14192 			if (resizeStarted) {
       
 14193 				return;
       
 14194 			}
       
 14195 
       
 14196 			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
       
 14197 			each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
       
 14198 				img.removeAttribute('data-mce-selected');
       
 14199 			});
       
 14200 
       
 14201 			controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
       
 14202 			controlElm = dom.$(controlElm).closest(isIE ? 'table' : 'table,img,hr')[0];
       
 14203 
       
 14204 			if (isChildOrEqual(controlElm, rootElement)) {
       
 14205 				disableGeckoResize();
       
 14206 				startElm = selection.getStart(true);
       
 14207 
       
 14208 				if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
       
 14209 					if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) {
       
 14210 						showResizeRect(controlElm);
       
 14211 						return;
       
 14212 					}
       
 14213 				}
       
 14214 			}
       
 14215 
       
 14216 			hideResizeRect();
       
 14217 		}
       
 14218 
       
 14219 		function attachEvent(elm, name, func) {
       
 14220 			if (elm && elm.attachEvent) {
       
 14221 				elm.attachEvent('on' + name, func);
       
 14222 			}
       
 14223 		}
       
 14224 
       
 14225 		function detachEvent(elm, name, func) {
       
 14226 			if (elm && elm.detachEvent) {
       
 14227 				elm.detachEvent('on' + name, func);
       
 14228 			}
       
 14229 		}
       
 14230 
       
 14231 		function resizeNativeStart(e) {
       
 14232 			var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
       
 14233 
       
 14234 			pos = target.getBoundingClientRect();
       
 14235 			relativeX = lastMouseDownEvent.clientX - pos.left;
       
 14236 			relativeY = lastMouseDownEvent.clientY - pos.top;
       
 14237 
       
 14238 			// Figure out what corner we are draging on
       
 14239 			for (name in resizeHandles) {
       
 14240 				corner = resizeHandles[name];
       
 14241 
       
 14242 				cornerX = target.offsetWidth * corner[0];
       
 14243 				cornerY = target.offsetHeight * corner[1];
       
 14244 
       
 14245 				if (abs(cornerX - relativeX) < 8 && abs(cornerY - relativeY) < 8) {
       
 14246 					selectedHandle = corner;
       
 14247 					break;
       
 14248 				}
       
 14249 			}
       
 14250 
       
 14251 			// Remove native selection and let the magic begin
       
 14252 			resizeStarted = true;
       
 14253 			editor.fire('ObjectResizeStart', {
       
 14254 				target: selectedElm,
       
 14255 				width: selectedElm.clientWidth,
       
 14256 				height: selectedElm.clientHeight
       
 14257 			});
       
 14258 			editor.getDoc().selection.empty();
       
 14259 			showResizeRect(target, name, lastMouseDownEvent);
       
 14260 		}
       
 14261 
       
 14262 		function nativeControlSelect(e) {
       
 14263 			var target = e.srcElement;
       
 14264 
       
 14265 			if (target != selectedElm) {
       
 14266 				editor.fire('ObjectSelected', {target: target});
       
 14267 				detachResizeStartListener();
       
 14268 
       
 14269 				if (target.id.indexOf('mceResizeHandle') === 0) {
       
 14270 					e.returnValue = false;
       
 14271 					return;
       
 14272 				}
       
 14273 
       
 14274 				if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
       
 14275 					hideResizeRect();
       
 14276 					selectedElm = target;
       
 14277 					attachEvent(target, 'resizestart', resizeNativeStart);
       
 14278 				}
       
 14279 			}
       
 14280 		}
       
 14281 
       
 14282 		function detachResizeStartListener() {
       
 14283 			detachEvent(selectedElm, 'resizestart', resizeNativeStart);
       
 14284 		}
       
 14285 
       
 14286 		function unbindResizeHandleEvents() {
       
 14287 			for (var name in resizeHandles) {
       
 14288 				var handle = resizeHandles[name];
       
 14289 
       
 14290 				if (handle.elm) {
       
 14291 					dom.unbind(handle.elm);
       
 14292 					delete handle.elm;
       
 14293 				}
       
 14294 			}
       
 14295 		}
       
 14296 
       
 14297 		function disableGeckoResize() {
       
 14298 			try {
       
 14299 				// Disable object resizing on Gecko
       
 14300 				editor.getDoc().execCommand('enableObjectResizing', false, false);
       
 14301 			} catch (ex) {
       
 14302 				// Ignore
       
 14303 			}
       
 14304 		}
       
 14305 
       
 14306 		function controlSelect(elm) {
       
 14307 			var ctrlRng;
       
 14308 
       
 14309 			if (!isIE) {
       
 14310 				return;
       
 14311 			}
       
 14312 
       
 14313 			ctrlRng = editableDoc.body.createControlRange();
       
 14314 
       
 14315 			try {
       
 14316 				ctrlRng.addElement(elm);
       
 14317 				ctrlRng.select();
       
 14318 				return true;
       
 14319 			} catch (ex) {
       
 14320 				// Ignore since the element can't be control selected for example a P tag
       
 14321 			}
       
 14322 		}
       
 14323 
       
 14324 		editor.on('init', function() {
       
 14325 			if (isIE) {
       
 14326 				// Hide the resize rect on resize and reselect the image
       
 14327 				editor.on('ObjectResized', function(e) {
       
 14328 					if (e.target.nodeName != 'TABLE') {
       
 14329 						hideResizeRect();
       
 14330 						controlSelect(e.target);
       
 14331 					}
       
 14332 				});
       
 14333 
       
 14334 				attachEvent(rootElement, 'controlselect', nativeControlSelect);
       
 14335 
       
 14336 				editor.on('mousedown', function(e) {
       
 14337 					lastMouseDownEvent = e;
       
 14338 				});
       
 14339 			} else {
       
 14340 				disableGeckoResize();
       
 14341 
       
 14342 				if (Env.ie >= 11) {
       
 14343 					// TODO: Drag/drop doesn't work
       
 14344 					editor.on('mouseup', function(e) {
       
 14345 						var nodeName = e.target.nodeName;
       
 14346 
       
 14347 						if (!resizeStarted && /^(TABLE|IMG|HR)$/.test(nodeName)) {
       
 14348 							editor.selection.select(e.target, nodeName == 'TABLE');
       
 14349 							editor.nodeChanged();
       
 14350 						}
       
 14351 					});
       
 14352 
       
 14353 					editor.dom.bind(rootElement, 'mscontrolselect', function(e) {
       
 14354 						if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
       
 14355 							e.preventDefault();
       
 14356 
       
 14357 							// This moves the selection from being a control selection to a text like selection like in WebKit #6753
       
 14358 							// TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections.
       
 14359 							if (e.target.tagName == 'IMG') {
       
 14360 								window.setTimeout(function() {
       
 14361 									editor.selection.select(e.target);
       
 14362 								}, 0);
       
 14363 							}
       
 14364 						}
       
 14365 					});
       
 14366 				}
       
 14367 			}
       
 14368 
       
 14369 			editor.on('nodechange ResizeEditor', updateResizeRect);
       
 14370 
       
 14371 			// Update resize rect while typing in a table
       
 14372 			editor.on('keydown keyup', function(e) {
       
 14373 				if (selectedElm && selectedElm.nodeName == "TABLE") {
       
 14374 					updateResizeRect(e);
       
 14375 				}
       
 14376 			});
       
 14377 
       
 14378 			editor.on('hide', hideResizeRect);
       
 14379 
       
 14380 			// Hide rect on focusout since it would float on top of windows otherwise
       
 14381 			//editor.on('focusout', hideResizeRect);
       
 14382 		});
       
 14383 
       
 14384 		editor.on('remove', unbindResizeHandleEvents);
       
 14385 
       
 14386 		function destroy() {
       
 14387 			selectedElm = selectedElmGhost = null;
       
 14388 
       
 14389 			if (isIE) {
       
 14390 				detachResizeStartListener();
       
 14391 				detachEvent(rootElement, 'controlselect', nativeControlSelect);
       
 14392 			}
       
 14393 		}
       
 14394 
       
 14395 		return {
       
 14396 			isResizable: isResizable,
       
 14397 			showResizeRect: showResizeRect,
       
 14398 			hideResizeRect: hideResizeRect,
       
 14399 			updateResizeRect: updateResizeRect,
       
 14400 			controlSelect: controlSelect,
       
 14401 			destroy: destroy
       
 14402 		};
       
 14403 	};
       
 14404 });
       
 14405 
       
 14406 // Included from: js/tinymce/classes/dom/BookmarkManager.js
       
 14407 
       
 14408 /**
       
 14409  * BookmarkManager.js
       
 14410  *
       
 14411  * Copyright, Moxiecode Systems AB
       
 14412  * Released under LGPL License.
       
 14413  *
       
 14414  * License: http://www.tinymce.com/license
       
 14415  * Contributing: http://www.tinymce.com/contributing
       
 14416  */
       
 14417 
       
 14418 /**
       
 14419  * This class handles selection bookmarks.
       
 14420  *
       
 14421  * @class tinymce.dom.BookmarkManager
       
 14422  */
       
 14423 define("tinymce/dom/BookmarkManager", [
       
 14424 	"tinymce/Env",
       
 14425 	"tinymce/util/Tools"
       
 14426 ], function(Env, Tools) {
       
 14427 	/**
       
 14428 	 * Constructs a new BookmarkManager instance for a specific selection instance.
       
 14429 	 *
       
 14430 	 * @constructor
       
 14431 	 * @method BookmarkManager
       
 14432 	 * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for.
       
 14433 	 */
       
 14434 	function BookmarkManager(selection) {
       
 14435 		var dom = selection.dom;
       
 14436 
       
 14437 		/**
       
 14438 		 * Returns a bookmark location for the current selection. This bookmark object
       
 14439 		 * can then be used to restore the selection after some content modification to the document.
       
 14440 		 *
       
 14441 		 * @method getBookmark
       
 14442 		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
       
 14443 		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
       
 14444 		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
       
 14445 		 * @example
       
 14446 		 * // Stores a bookmark of the current selection
       
 14447 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 14448 		 *
       
 14449 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 14450 		 *
       
 14451 		 * // Restore the selection bookmark
       
 14452 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 14453 		 */
       
 14454 		this.getBookmark = function(type, normalized) {
       
 14455 			var rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
       
 14456 
       
 14457 			function findIndex(name, element) {
       
 14458 				var index = 0;
       
 14459 
       
 14460 				Tools.each(dom.select(name), function(node, i) {
       
 14461 					if (node == element) {
       
 14462 						index = i;
       
 14463 					}
       
 14464 				});
       
 14465 
       
 14466 				return index;
       
 14467 			}
       
 14468 
       
 14469 			function normalizeTableCellSelection(rng) {
       
 14470 				function moveEndPoint(start) {
       
 14471 					var container, offset, childNodes, prefix = start ? 'start' : 'end';
       
 14472 
       
 14473 					container = rng[prefix + 'Container'];
       
 14474 					offset = rng[prefix + 'Offset'];
       
 14475 
       
 14476 					if (container.nodeType == 1 && container.nodeName == "TR") {
       
 14477 						childNodes = container.childNodes;
       
 14478 						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
       
 14479 						if (container) {
       
 14480 							offset = start ? 0 : container.childNodes.length;
       
 14481 							rng['set' + (start ? 'Start' : 'End')](container, offset);
       
 14482 						}
       
 14483 					}
       
 14484 				}
       
 14485 
       
 14486 				moveEndPoint(true);
       
 14487 				moveEndPoint();
       
 14488 
       
 14489 				return rng;
       
 14490 			}
       
 14491 
       
 14492 			function getLocation() {
       
 14493 				var rng = selection.getRng(true), root = dom.getRoot(), bookmark = {};
       
 14494 
       
 14495 				function getPoint(rng, start) {
       
 14496 					var container = rng[start ? 'startContainer' : 'endContainer'],
       
 14497 						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
       
 14498 
       
 14499 					if (container.nodeType == 3) {
       
 14500 						if (normalized) {
       
 14501 							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
       
 14502 								offset += node.nodeValue.length;
       
 14503 							}
       
 14504 						}
       
 14505 
       
 14506 						point.push(offset);
       
 14507 					} else {
       
 14508 						childNodes = container.childNodes;
       
 14509 
       
 14510 						if (offset >= childNodes.length && childNodes.length) {
       
 14511 							after = 1;
       
 14512 							offset = Math.max(0, childNodes.length - 1);
       
 14513 						}
       
 14514 
       
 14515 						point.push(dom.nodeIndex(childNodes[offset], normalized) + after);
       
 14516 					}
       
 14517 
       
 14518 					for (; container && container != root; container = container.parentNode) {
       
 14519 						point.push(dom.nodeIndex(container, normalized));
       
 14520 					}
       
 14521 
       
 14522 					return point;
       
 14523 				}
       
 14524 
       
 14525 				bookmark.start = getPoint(rng, true);
       
 14526 
       
 14527 				if (!selection.isCollapsed()) {
       
 14528 					bookmark.end = getPoint(rng);
       
 14529 				}
       
 14530 
       
 14531 				return bookmark;
       
 14532 			}
       
 14533 
       
 14534 			if (type == 2) {
       
 14535 				element = selection.getNode();
       
 14536 				name = element ? element.nodeName : null;
       
 14537 
       
 14538 				if (name == 'IMG') {
       
 14539 					return {name: name, index: findIndex(name, element)};
       
 14540 				}
       
 14541 
       
 14542 				if (selection.tridentSel) {
       
 14543 					return selection.tridentSel.getBookmark(type);
       
 14544 				}
       
 14545 
       
 14546 				return getLocation();
       
 14547 			}
       
 14548 
       
 14549 			// Handle simple range
       
 14550 			if (type) {
       
 14551 				return {rng: selection.getRng()};
       
 14552 			}
       
 14553 
       
 14554 			rng = selection.getRng();
       
 14555 			id = dom.uniqueId();
       
 14556 			collapsed = selection.isCollapsed();
       
 14557 			styles = 'overflow:hidden;line-height:0px';
       
 14558 
       
 14559 			// Explorer method
       
 14560 			if (rng.duplicate || rng.item) {
       
 14561 				// Text selection
       
 14562 				if (!rng.item) {
       
 14563 					rng2 = rng.duplicate();
       
 14564 
       
 14565 					try {
       
 14566 						// Insert start marker
       
 14567 						rng.collapse();
       
 14568 						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
       
 14569 
       
 14570 						// Insert end marker
       
 14571 						if (!collapsed) {
       
 14572 							rng2.collapse(false);
       
 14573 
       
 14574 							// Detect the empty space after block elements in IE and move the
       
 14575 							// end back one character <p></p>] becomes <p>]</p>
       
 14576 							rng.moveToElementText(rng2.parentElement());
       
 14577 							if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
       
 14578 								rng2.move('character', -1);
       
 14579 							}
       
 14580 
       
 14581 							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
       
 14582 						}
       
 14583 					} catch (ex) {
       
 14584 						// IE might throw unspecified error so lets ignore it
       
 14585 						return null;
       
 14586 					}
       
 14587 				} else {
       
 14588 					// Control selection
       
 14589 					element = rng.item(0);
       
 14590 					name = element.nodeName;
       
 14591 
       
 14592 					return {name: name, index: findIndex(name, element)};
       
 14593 				}
       
 14594 			} else {
       
 14595 				element = selection.getNode();
       
 14596 				name = element.nodeName;
       
 14597 				if (name == 'IMG') {
       
 14598 					return {name: name, index: findIndex(name, element)};
       
 14599 				}
       
 14600 
       
 14601 				// W3C method
       
 14602 				rng2 = normalizeTableCellSelection(rng.cloneRange());
       
 14603 
       
 14604 				// Insert end marker
       
 14605 				if (!collapsed) {
       
 14606 					rng2.collapse(false);
       
 14607 					rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
       
 14608 				}
       
 14609 
       
 14610 				rng = normalizeTableCellSelection(rng);
       
 14611 				rng.collapse(true);
       
 14612 				rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
       
 14613 			}
       
 14614 
       
 14615 			selection.moveToBookmark({id: id, keep: 1});
       
 14616 
       
 14617 			return {id: id};
       
 14618 		};
       
 14619 
       
 14620 		/**
       
 14621 		 * Restores the selection to the specified bookmark.
       
 14622 		 *
       
 14623 		 * @method moveToBookmark
       
 14624 		 * @param {Object} bookmark Bookmark to restore selection from.
       
 14625 		 * @return {Boolean} true/false if it was successful or not.
       
 14626 		 * @example
       
 14627 		 * // Stores a bookmark of the current selection
       
 14628 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 14629 		 *
       
 14630 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 14631 		 *
       
 14632 		 * // Restore the selection bookmark
       
 14633 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 14634 		 */
       
 14635 		this.moveToBookmark = function(bookmark) {
       
 14636 			var rng, root, startContainer, endContainer, startOffset, endOffset;
       
 14637 
       
 14638 			function setEndPoint(start) {
       
 14639 				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
       
 14640 
       
 14641 				if (point) {
       
 14642 					offset = point[0];
       
 14643 
       
 14644 					// Find container node
       
 14645 					for (node = root, i = point.length - 1; i >= 1; i--) {
       
 14646 						children = node.childNodes;
       
 14647 
       
 14648 						if (point[i] > children.length - 1) {
       
 14649 							return;
       
 14650 						}
       
 14651 
       
 14652 						node = children[point[i]];
       
 14653 					}
       
 14654 
       
 14655 					// Move text offset to best suitable location
       
 14656 					if (node.nodeType === 3) {
       
 14657 						offset = Math.min(point[0], node.nodeValue.length);
       
 14658 					}
       
 14659 
       
 14660 					// Move element offset to best suitable location
       
 14661 					if (node.nodeType === 1) {
       
 14662 						offset = Math.min(point[0], node.childNodes.length);
       
 14663 					}
       
 14664 
       
 14665 					// Set offset within container node
       
 14666 					if (start) {
       
 14667 						rng.setStart(node, offset);
       
 14668 					} else {
       
 14669 						rng.setEnd(node, offset);
       
 14670 					}
       
 14671 				}
       
 14672 
       
 14673 				return true;
       
 14674 			}
       
 14675 
       
 14676 			function restoreEndPoint(suffix) {
       
 14677 				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
       
 14678 
       
 14679 				if (marker) {
       
 14680 					node = marker.parentNode;
       
 14681 
       
 14682 					if (suffix == 'start') {
       
 14683 						if (!keep) {
       
 14684 							idx = dom.nodeIndex(marker);
       
 14685 						} else {
       
 14686 							node = marker.firstChild;
       
 14687 							idx = 1;
       
 14688 						}
       
 14689 
       
 14690 						startContainer = endContainer = node;
       
 14691 						startOffset = endOffset = idx;
       
 14692 					} else {
       
 14693 						if (!keep) {
       
 14694 							idx = dom.nodeIndex(marker);
       
 14695 						} else {
       
 14696 							node = marker.firstChild;
       
 14697 							idx = 1;
       
 14698 						}
       
 14699 
       
 14700 						endContainer = node;
       
 14701 						endOffset = idx;
       
 14702 					}
       
 14703 
       
 14704 					if (!keep) {
       
 14705 						prev = marker.previousSibling;
       
 14706 						next = marker.nextSibling;
       
 14707 
       
 14708 						// Remove all marker text nodes
       
 14709 						Tools.each(Tools.grep(marker.childNodes), function(node) {
       
 14710 							if (node.nodeType == 3) {
       
 14711 								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
       
 14712 							}
       
 14713 						});
       
 14714 
       
 14715 						// Remove marker but keep children if for example contents where inserted into the marker
       
 14716 						// Also remove duplicated instances of the marker for example by a
       
 14717 						// split operation or by WebKit auto split on paste feature
       
 14718 						while ((marker = dom.get(bookmark.id + '_' + suffix))) {
       
 14719 							dom.remove(marker, 1);
       
 14720 						}
       
 14721 
       
 14722 						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
       
 14723 						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market
       
 14724 						// isn't worth the effort. Sorry, Opera but it's just a fact
       
 14725 						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) {
       
 14726 							idx = prev.nodeValue.length;
       
 14727 							prev.appendData(next.nodeValue);
       
 14728 							dom.remove(next);
       
 14729 
       
 14730 							if (suffix == 'start') {
       
 14731 								startContainer = endContainer = prev;
       
 14732 								startOffset = endOffset = idx;
       
 14733 							} else {
       
 14734 								endContainer = prev;
       
 14735 								endOffset = idx;
       
 14736 							}
       
 14737 						}
       
 14738 					}
       
 14739 				}
       
 14740 			}
       
 14741 
       
 14742 			function addBogus(node) {
       
 14743 				// Adds a bogus BR element for empty block elements
       
 14744 				if (dom.isBlock(node) && !node.innerHTML && !Env.ie) {
       
 14745 					node.innerHTML = '<br data-mce-bogus="1" />';
       
 14746 				}
       
 14747 
       
 14748 				return node;
       
 14749 			}
       
 14750 
       
 14751 			if (bookmark) {
       
 14752 				if (bookmark.start) {
       
 14753 					rng = dom.createRng();
       
 14754 					root = dom.getRoot();
       
 14755 
       
 14756 					if (selection.tridentSel) {
       
 14757 						return selection.tridentSel.moveToBookmark(bookmark);
       
 14758 					}
       
 14759 
       
 14760 					if (setEndPoint(true) && setEndPoint()) {
       
 14761 						selection.setRng(rng);
       
 14762 					}
       
 14763 				} else if (bookmark.id) {
       
 14764 					// Restore start/end points
       
 14765 					restoreEndPoint('start');
       
 14766 					restoreEndPoint('end');
       
 14767 
       
 14768 					if (startContainer) {
       
 14769 						rng = dom.createRng();
       
 14770 						rng.setStart(addBogus(startContainer), startOffset);
       
 14771 						rng.setEnd(addBogus(endContainer), endOffset);
       
 14772 						selection.setRng(rng);
       
 14773 					}
       
 14774 				} else if (bookmark.name) {
       
 14775 					selection.select(dom.select(bookmark.name)[bookmark.index]);
       
 14776 				} else if (bookmark.rng) {
       
 14777 					selection.setRng(bookmark.rng);
       
 14778 				}
       
 14779 			}
       
 14780 		};
       
 14781 	}
       
 14782 
       
 14783 	/**
       
 14784 	 * Returns true/false if the specified node is a bookmark node or not.
       
 14785 	 *
       
 14786 	 * @static
       
 14787 	 * @method isBookmarkNode
       
 14788 	 * @param {DOMNode} node DOM Node to check if it's a bookmark node or not.
       
 14789 	 * @return {Boolean} true/false if the node is a bookmark node or not.
       
 14790 	 */
       
 14791 	BookmarkManager.isBookmarkNode = function(node) {
       
 14792 		return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark';
       
 14793 	};
       
 14794 
       
 14795 	return BookmarkManager;
       
 14796 });
       
 14797 
       
 14798 // Included from: js/tinymce/classes/dom/Selection.js
       
 14799 
       
 14800 /**
       
 14801  * Selection.js
       
 14802  *
       
 14803  * Copyright, Moxiecode Systems AB
       
 14804  * Released under LGPL License.
       
 14805  *
       
 14806  * License: http://www.tinymce.com/license
       
 14807  * Contributing: http://www.tinymce.com/contributing
       
 14808  */
       
 14809 
       
 14810 /**
       
 14811  * This class handles text and control selection it's an crossbrowser utility class.
       
 14812  * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
       
 14813  *
       
 14814  * @class tinymce.dom.Selection
       
 14815  * @example
       
 14816  * // Getting the currently selected node for the active editor
       
 14817  * alert(tinymce.activeEditor.selection.getNode().nodeName);
       
 14818  */
       
 14819 define("tinymce/dom/Selection", [
       
 14820 	"tinymce/dom/TreeWalker",
       
 14821 	"tinymce/dom/TridentSelection",
       
 14822 	"tinymce/dom/ControlSelection",
       
 14823 	"tinymce/dom/RangeUtils",
       
 14824 	"tinymce/dom/BookmarkManager",
       
 14825 	"tinymce/Env",
       
 14826 	"tinymce/util/Tools"
       
 14827 ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, Env, Tools) {
       
 14828 	var each = Tools.each, trim = Tools.trim;
       
 14829 	var isIE = Env.ie;
       
 14830 
       
 14831 	/**
       
 14832 	 * Constructs a new selection instance.
       
 14833 	 *
       
 14834 	 * @constructor
       
 14835 	 * @method Selection
       
 14836 	 * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
       
 14837 	 * @param {Window} win Window to bind the selection object to.
       
 14838 	 * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
       
 14839 	 */
       
 14840 	function Selection(dom, win, serializer, editor) {
       
 14841 		var self = this;
       
 14842 
       
 14843 		self.dom = dom;
       
 14844 		self.win = win;
       
 14845 		self.serializer = serializer;
       
 14846 		self.editor = editor;
       
 14847 		self.bookmarkManager = new BookmarkManager(self);
       
 14848 		self.controlSelection = new ControlSelection(self, editor);
       
 14849 
       
 14850 		// No W3C Range support
       
 14851 		if (!self.win.getSelection) {
       
 14852 			self.tridentSel = new TridentSelection(self);
       
 14853 		}
       
 14854 	}
       
 14855 
       
 14856 	Selection.prototype = {
       
 14857 		/**
       
 14858 		 * Move the selection cursor range to the specified node and offset.
       
 14859 		 * If there is no node specified it will move it to the first suitable location within the body.
       
 14860 		 *
       
 14861 		 * @method setCursorLocation
       
 14862 		 * @param {Node} node Optional node to put the cursor in.
       
 14863 		 * @param {Number} offset Optional offset from the start of the node to put the cursor at.
       
 14864 		 */
       
 14865 		setCursorLocation: function(node, offset) {
       
 14866 			var self = this, rng = self.dom.createRng();
       
 14867 
       
 14868 			if (!node) {
       
 14869 				self._moveEndPoint(rng, self.editor.getBody(), true);
       
 14870 				self.setRng(rng);
       
 14871 			} else {
       
 14872 				rng.setStart(node, offset);
       
 14873 				rng.setEnd(node, offset);
       
 14874 				self.setRng(rng);
       
 14875 				self.collapse(false);
       
 14876 			}
       
 14877 		},
       
 14878 
       
 14879 		/**
       
 14880 		 * Returns the selected contents using the DOM serializer passed in to this class.
       
 14881 		 *
       
 14882 		 * @method getContent
       
 14883 		 * @param {Object} s Optional settings class with for example output format text or html.
       
 14884 		 * @return {String} Selected contents in for example HTML format.
       
 14885 		 * @example
       
 14886 		 * // Alerts the currently selected contents
       
 14887 		 * alert(tinymce.activeEditor.selection.getContent());
       
 14888 		 *
       
 14889 		 * // Alerts the currently selected contents as plain text
       
 14890 		 * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
       
 14891 		 */
       
 14892 		getContent: function(args) {
       
 14893 			var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
       
 14894 			var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
       
 14895 
       
 14896 			args = args || {};
       
 14897 			whiteSpaceBefore = whiteSpaceAfter = '';
       
 14898 			args.get = true;
       
 14899 			args.format = args.format || 'html';
       
 14900 			args.selection = true;
       
 14901 			self.editor.fire('BeforeGetContent', args);
       
 14902 
       
 14903 			if (args.format == 'text') {
       
 14904 				return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
       
 14905 			}
       
 14906 
       
 14907 			if (rng.cloneContents) {
       
 14908 				fragment = rng.cloneContents();
       
 14909 
       
 14910 				if (fragment) {
       
 14911 					tmpElm.appendChild(fragment);
       
 14912 				}
       
 14913 			} else if (rng.item !== undefined || rng.htmlText !== undefined) {
       
 14914 				// IE will produce invalid markup if elements are present that
       
 14915 				// it doesn't understand like custom elements or HTML5 elements.
       
 14916 				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
       
 14917 				tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
       
 14918 				tmpElm.removeChild(tmpElm.firstChild);
       
 14919 			} else {
       
 14920 				tmpElm.innerHTML = rng.toString();
       
 14921 			}
       
 14922 
       
 14923 			// Keep whitespace before and after
       
 14924 			if (/^\s/.test(tmpElm.innerHTML)) {
       
 14925 				whiteSpaceBefore = ' ';
       
 14926 			}
       
 14927 
       
 14928 			if (/\s+$/.test(tmpElm.innerHTML)) {
       
 14929 				whiteSpaceAfter = ' ';
       
 14930 			}
       
 14931 
       
 14932 			args.getInner = true;
       
 14933 
       
 14934 			args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
       
 14935 			self.editor.fire('GetContent', args);
       
 14936 
       
 14937 			return args.content;
       
 14938 		},
       
 14939 
       
 14940 		/**
       
 14941 		 * Sets the current selection to the specified content. If any contents is selected it will be replaced
       
 14942 		 * with the contents passed in to this function. If there is no selection the contents will be inserted
       
 14943 		 * where the caret is placed in the editor/page.
       
 14944 		 *
       
 14945 		 * @method setContent
       
 14946 		 * @param {String} content HTML contents to set could also be other formats depending on settings.
       
 14947 		 * @param {Object} args Optional settings object with for example data format.
       
 14948 		 * @example
       
 14949 		 * // Inserts some HTML contents at the current selection
       
 14950 		 * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
       
 14951 		 */
       
 14952 		setContent: function(content, args) {
       
 14953 			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
       
 14954 
       
 14955 			args = args || {format: 'html'};
       
 14956 			args.set = true;
       
 14957 			args.selection = true;
       
 14958 			content = args.content = content;
       
 14959 
       
 14960 			// Dispatch before set content event
       
 14961 			if (!args.no_events) {
       
 14962 				self.editor.fire('BeforeSetContent', args);
       
 14963 			}
       
 14964 
       
 14965 			content = args.content;
       
 14966 
       
 14967 			if (rng.insertNode) {
       
 14968 				// Make caret marker since insertNode places the caret in the beginning of text after insert
       
 14969 				content += '<span id="__caret">_</span>';
       
 14970 
       
 14971 				// Delete and insert new node
       
 14972 				if (rng.startContainer == doc && rng.endContainer == doc) {
       
 14973 					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
       
 14974 					doc.body.innerHTML = content;
       
 14975 				} else {
       
 14976 					rng.deleteContents();
       
 14977 
       
 14978 					if (doc.body.childNodes.length === 0) {
       
 14979 						doc.body.innerHTML = content;
       
 14980 					} else {
       
 14981 						// createContextualFragment doesn't exists in IE 9 DOMRanges
       
 14982 						if (rng.createContextualFragment) {
       
 14983 							rng.insertNode(rng.createContextualFragment(content));
       
 14984 						} else {
       
 14985 							// Fake createContextualFragment call in IE 9
       
 14986 							frag = doc.createDocumentFragment();
       
 14987 							temp = doc.createElement('div');
       
 14988 
       
 14989 							frag.appendChild(temp);
       
 14990 							temp.outerHTML = content;
       
 14991 
       
 14992 							rng.insertNode(frag);
       
 14993 						}
       
 14994 					}
       
 14995 				}
       
 14996 
       
 14997 				// Move to caret marker
       
 14998 				caretNode = self.dom.get('__caret');
       
 14999 
       
 15000 				// Make sure we wrap it compleatly, Opera fails with a simple select call
       
 15001 				rng = doc.createRange();
       
 15002 				rng.setStartBefore(caretNode);
       
 15003 				rng.setEndBefore(caretNode);
       
 15004 				self.setRng(rng);
       
 15005 
       
 15006 				// Remove the caret position
       
 15007 				self.dom.remove('__caret');
       
 15008 
       
 15009 				try {
       
 15010 					self.setRng(rng);
       
 15011 				} catch (ex) {
       
 15012 					// Might fail on Opera for some odd reason
       
 15013 				}
       
 15014 			} else {
       
 15015 				if (rng.item) {
       
 15016 					// Delete content and get caret text selection
       
 15017 					doc.execCommand('Delete', false, null);
       
 15018 					rng = self.getRng();
       
 15019 				}
       
 15020 
       
 15021 				// Explorer removes spaces from the beginning of pasted contents
       
 15022 				if (/^\s+/.test(content)) {
       
 15023 					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
       
 15024 					self.dom.remove('__mce_tmp');
       
 15025 				} else {
       
 15026 					rng.pasteHTML(content);
       
 15027 				}
       
 15028 			}
       
 15029 
       
 15030 			// Dispatch set content event
       
 15031 			if (!args.no_events) {
       
 15032 				self.editor.fire('SetContent', args);
       
 15033 			}
       
 15034 		},
       
 15035 
       
 15036 		/**
       
 15037 		 * Returns the start element of a selection range. If the start is in a text
       
 15038 		 * node the parent element will be returned.
       
 15039 		 *
       
 15040 		 * @method getStart
       
 15041 		 * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
       
 15042 		 * @return {Element} Start element of selection range.
       
 15043 		 */
       
 15044 		getStart: function(real) {
       
 15045 			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
       
 15046 
       
 15047 			if (rng.duplicate || rng.item) {
       
 15048 				// Control selection, return first item
       
 15049 				if (rng.item) {
       
 15050 					return rng.item(0);
       
 15051 				}
       
 15052 
       
 15053 				// Get start element
       
 15054 				checkRng = rng.duplicate();
       
 15055 				checkRng.collapse(1);
       
 15056 				startElement = checkRng.parentElement();
       
 15057 				if (startElement.ownerDocument !== self.dom.doc) {
       
 15058 					startElement = self.dom.getRoot();
       
 15059 				}
       
 15060 
       
 15061 				// Check if range parent is inside the start element, then return the inner parent element
       
 15062 				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
       
 15063 				parentElement = node = rng.parentElement();
       
 15064 				while ((node = node.parentNode)) {
       
 15065 					if (node == startElement) {
       
 15066 						startElement = parentElement;
       
 15067 						break;
       
 15068 					}
       
 15069 				}
       
 15070 
       
 15071 				return startElement;
       
 15072 			} else {
       
 15073 				startElement = rng.startContainer;
       
 15074 
       
 15075 				if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
       
 15076 					if (!real || !rng.collapsed) {
       
 15077 						startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
       
 15078 					}
       
 15079 				}
       
 15080 
       
 15081 				if (startElement && startElement.nodeType == 3) {
       
 15082 					return startElement.parentNode;
       
 15083 				}
       
 15084 
       
 15085 				return startElement;
       
 15086 			}
       
 15087 		},
       
 15088 
       
 15089 		/**
       
 15090 		 * Returns the end element of a selection range. If the end is in a text
       
 15091 		 * node the parent element will be returned.
       
 15092 		 *
       
 15093 		 * @method getEnd
       
 15094 		 * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
       
 15095 		 * @return {Element} End element of selection range.
       
 15096 		 */
       
 15097 		getEnd: function(real) {
       
 15098 			var self = this, rng = self.getRng(), endElement, endOffset;
       
 15099 
       
 15100 			if (rng.duplicate || rng.item) {
       
 15101 				if (rng.item) {
       
 15102 					return rng.item(0);
       
 15103 				}
       
 15104 
       
 15105 				rng = rng.duplicate();
       
 15106 				rng.collapse(0);
       
 15107 				endElement = rng.parentElement();
       
 15108 				if (endElement.ownerDocument !== self.dom.doc) {
       
 15109 					endElement = self.dom.getRoot();
       
 15110 				}
       
 15111 
       
 15112 				if (endElement && endElement.nodeName == 'BODY') {
       
 15113 					return endElement.lastChild || endElement;
       
 15114 				}
       
 15115 
       
 15116 				return endElement;
       
 15117 			} else {
       
 15118 				endElement = rng.endContainer;
       
 15119 				endOffset = rng.endOffset;
       
 15120 
       
 15121 				if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
       
 15122 					if (!real || !rng.collapsed) {
       
 15123 						endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
       
 15124 					}
       
 15125 				}
       
 15126 
       
 15127 				if (endElement && endElement.nodeType == 3) {
       
 15128 					return endElement.parentNode;
       
 15129 				}
       
 15130 
       
 15131 				return endElement;
       
 15132 			}
       
 15133 		},
       
 15134 
       
 15135 		/**
       
 15136 		 * Returns a bookmark location for the current selection. This bookmark object
       
 15137 		 * can then be used to restore the selection after some content modification to the document.
       
 15138 		 *
       
 15139 		 * @method getBookmark
       
 15140 		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
       
 15141 		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
       
 15142 		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
       
 15143 		 * @example
       
 15144 		 * // Stores a bookmark of the current selection
       
 15145 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 15146 		 *
       
 15147 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 15148 		 *
       
 15149 		 * // Restore the selection bookmark
       
 15150 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 15151 		 */
       
 15152 		getBookmark: function(type, normalized) {
       
 15153 			return this.bookmarkManager.getBookmark(type, normalized);
       
 15154 		},
       
 15155 
       
 15156 		/**
       
 15157 		 * Restores the selection to the specified bookmark.
       
 15158 		 *
       
 15159 		 * @method moveToBookmark
       
 15160 		 * @param {Object} bookmark Bookmark to restore selection from.
       
 15161 		 * @return {Boolean} true/false if it was successful or not.
       
 15162 		 * @example
       
 15163 		 * // Stores a bookmark of the current selection
       
 15164 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 15165 		 *
       
 15166 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 15167 		 *
       
 15168 		 * // Restore the selection bookmark
       
 15169 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 15170 		 */
       
 15171 		moveToBookmark: function(bookmark) {
       
 15172 			return this.bookmarkManager.moveToBookmark(bookmark);
       
 15173 		},
       
 15174 
       
 15175 		/**
       
 15176 		 * Selects the specified element. This will place the start and end of the selection range around the element.
       
 15177 		 *
       
 15178 		 * @method select
       
 15179 		 * @param {Element} node HMTL DOM element to select.
       
 15180 		 * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser.
       
 15181 		 * @return {Element} Selected element the same element as the one that got passed in.
       
 15182 		 * @example
       
 15183 		 * // Select the first paragraph in the active editor
       
 15184 		 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
       
 15185 		 */
       
 15186 		select: function(node, content) {
       
 15187 			var self = this, dom = self.dom, rng = dom.createRng(), idx;
       
 15188 
       
 15189 			// Clear stored range set by FocusManager
       
 15190 			self.lastFocusBookmark = null;
       
 15191 
       
 15192 			if (node) {
       
 15193 				if (!content && self.controlSelection.controlSelect(node)) {
       
 15194 					return;
       
 15195 				}
       
 15196 
       
 15197 				idx = dom.nodeIndex(node);
       
 15198 				rng.setStart(node.parentNode, idx);
       
 15199 				rng.setEnd(node.parentNode, idx + 1);
       
 15200 
       
 15201 				// Find first/last text node or BR element
       
 15202 				if (content) {
       
 15203 					self._moveEndPoint(rng, node, true);
       
 15204 					self._moveEndPoint(rng, node);
       
 15205 				}
       
 15206 
       
 15207 				self.setRng(rng);
       
 15208 			}
       
 15209 
       
 15210 			return node;
       
 15211 		},
       
 15212 
       
 15213 		/**
       
 15214 		 * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection.
       
 15215 		 *
       
 15216 		 * @method isCollapsed
       
 15217 		 * @return {Boolean} true/false state if the selection range is collapsed or not.
       
 15218 		 * Collapsed means if it's a caret or a larger selection.
       
 15219 		 */
       
 15220 		isCollapsed: function() {
       
 15221 			var self = this, rng = self.getRng(), sel = self.getSel();
       
 15222 
       
 15223 			if (!rng || rng.item) {
       
 15224 				return false;
       
 15225 			}
       
 15226 
       
 15227 			if (rng.compareEndPoints) {
       
 15228 				return rng.compareEndPoints('StartToEnd', rng) === 0;
       
 15229 			}
       
 15230 
       
 15231 			return !sel || rng.collapsed;
       
 15232 		},
       
 15233 
       
 15234 		/**
       
 15235 		 * Collapse the selection to start or end of range.
       
 15236 		 *
       
 15237 		 * @method collapse
       
 15238 		 * @param {Boolean} toStart Optional boolean state if to collapse to end or not. Defaults to start.
       
 15239 		 */
       
 15240 		collapse: function(toStart) {
       
 15241 			var self = this, rng = self.getRng(), node;
       
 15242 
       
 15243 			// Control range on IE
       
 15244 			if (rng.item) {
       
 15245 				node = rng.item(0);
       
 15246 				rng = self.win.document.body.createTextRange();
       
 15247 				rng.moveToElementText(node);
       
 15248 			}
       
 15249 
       
 15250 			rng.collapse(!!toStart);
       
 15251 			self.setRng(rng);
       
 15252 		},
       
 15253 
       
 15254 		/**
       
 15255 		 * Returns the browsers internal selection object.
       
 15256 		 *
       
 15257 		 * @method getSel
       
 15258 		 * @return {Selection} Internal browser selection object.
       
 15259 		 */
       
 15260 		getSel: function() {
       
 15261 			var win = this.win;
       
 15262 
       
 15263 			return win.getSelection ? win.getSelection() : win.document.selection;
       
 15264 		},
       
 15265 
       
 15266 		/**
       
 15267 		 * Returns the browsers internal range object.
       
 15268 		 *
       
 15269 		 * @method getRng
       
 15270 		 * @param {Boolean} w3c Forces a compatible W3C range on IE.
       
 15271 		 * @return {Range} Internal browser range object.
       
 15272 		 * @see http://www.quirksmode.org/dom/range_intro.html
       
 15273 		 * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/
       
 15274 		 */
       
 15275 		getRng: function(w3c) {
       
 15276 			var self = this, selection, rng, elm, doc = self.win.document, ieRng;
       
 15277 
       
 15278 			function tryCompareBoundaryPoints(how, sourceRange, destinationRange) {
       
 15279 				try {
       
 15280 					return sourceRange.compareBoundaryPoints(how, destinationRange);
       
 15281 				} catch (ex) {
       
 15282 					// Gecko throws wrong document exception if the range points
       
 15283 					// to nodes that where removed from the dom #6690
       
 15284 					// Browsers should mutate existing DOMRange instances so that they always point
       
 15285 					// to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink
       
 15286 					// For performance reasons just return -1
       
 15287 					return -1;
       
 15288 				}
       
 15289 			}
       
 15290 
       
 15291 			// Use last rng passed from FocusManager if it's available this enables
       
 15292 			// calls to editor.selection.getStart() to work when caret focus is lost on IE
       
 15293 			if (!w3c && self.lastFocusBookmark) {
       
 15294 				var bookmark = self.lastFocusBookmark;
       
 15295 
       
 15296 				// Convert bookmark to range IE 11 fix
       
 15297 				if (bookmark.startContainer) {
       
 15298 					rng = doc.createRange();
       
 15299 					rng.setStart(bookmark.startContainer, bookmark.startOffset);
       
 15300 					rng.setEnd(bookmark.endContainer, bookmark.endOffset);
       
 15301 				} else {
       
 15302 					rng = bookmark;
       
 15303 				}
       
 15304 
       
 15305 				return rng;
       
 15306 			}
       
 15307 
       
 15308 			// Found tridentSel object then we need to use that one
       
 15309 			if (w3c && self.tridentSel) {
       
 15310 				return self.tridentSel.getRangeAt(0);
       
 15311 			}
       
 15312 
       
 15313 			try {
       
 15314 				if ((selection = self.getSel())) {
       
 15315 					if (selection.rangeCount > 0) {
       
 15316 						rng = selection.getRangeAt(0);
       
 15317 					} else {
       
 15318 						rng = selection.createRange ? selection.createRange() : doc.createRange();
       
 15319 					}
       
 15320 				}
       
 15321 			} catch (ex) {
       
 15322 				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
       
 15323 			}
       
 15324 
       
 15325 			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
       
 15326 			// IE 11 doesn't support the selection object so we check for that as well
       
 15327 			if (isIE && rng && rng.setStart && doc.selection) {
       
 15328 				try {
       
 15329 					// IE will sometimes throw an exception here
       
 15330 					ieRng = doc.selection.createRange();
       
 15331 				} catch (ex) {
       
 15332 
       
 15333 				}
       
 15334 
       
 15335 				if (ieRng && ieRng.item) {
       
 15336 					elm = ieRng.item(0);
       
 15337 					rng = doc.createRange();
       
 15338 					rng.setStartBefore(elm);
       
 15339 					rng.setEndAfter(elm);
       
 15340 				}
       
 15341 			}
       
 15342 
       
 15343 			// No range found then create an empty one
       
 15344 			// This can occur when the editor is placed in a hidden container element on Gecko
       
 15345 			// Or on IE when there was an exception
       
 15346 			if (!rng) {
       
 15347 				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
       
 15348 			}
       
 15349 
       
 15350 			// If range is at start of document then move it to start of body
       
 15351 			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
       
 15352 				elm = self.dom.getRoot();
       
 15353 				rng.setStart(elm, 0);
       
 15354 				rng.setEnd(elm, 0);
       
 15355 			}
       
 15356 
       
 15357 			if (self.selectedRange && self.explicitRange) {
       
 15358 				if (tryCompareBoundaryPoints(rng.START_TO_START, rng, self.selectedRange) === 0 &&
       
 15359 					tryCompareBoundaryPoints(rng.END_TO_END, rng, self.selectedRange) === 0) {
       
 15360 					// Safari, Opera and Chrome only ever select text which causes the range to change.
       
 15361 					// This lets us use the originally set range if the selection hasn't been changed by the user.
       
 15362 					rng = self.explicitRange;
       
 15363 				} else {
       
 15364 					self.selectedRange = null;
       
 15365 					self.explicitRange = null;
       
 15366 				}
       
 15367 			}
       
 15368 
       
 15369 			return rng;
       
 15370 		},
       
 15371 
       
 15372 		/**
       
 15373 		 * Changes the selection to the specified DOM range.
       
 15374 		 *
       
 15375 		 * @method setRng
       
 15376 		 * @param {Range} rng Range to select.
       
 15377 		 */
       
 15378 		setRng: function(rng, forward) {
       
 15379 			var self = this, sel;
       
 15380 
       
 15381 			if (!rng) {
       
 15382 				return;
       
 15383 			}
       
 15384 
       
 15385 			// Is IE specific range
       
 15386 			if (rng.select) {
       
 15387 				try {
       
 15388 					rng.select();
       
 15389 				} catch (ex) {
       
 15390 					// Needed for some odd IE bug #1843306
       
 15391 				}
       
 15392 
       
 15393 				return;
       
 15394 			}
       
 15395 
       
 15396 			if (!self.tridentSel) {
       
 15397 				sel = self.getSel();
       
 15398 
       
 15399 				if (sel) {
       
 15400 					self.explicitRange = rng;
       
 15401 
       
 15402 					try {
       
 15403 						sel.removeAllRanges();
       
 15404 						sel.addRange(rng);
       
 15405 					} catch (ex) {
       
 15406 						// IE might throw errors here if the editor is within a hidden container and selection is changed
       
 15407 					}
       
 15408 
       
 15409 					// Forward is set to false and we have an extend function
       
 15410 					if (forward === false && sel.extend) {
       
 15411 						sel.collapse(rng.endContainer, rng.endOffset);
       
 15412 						sel.extend(rng.startContainer, rng.startOffset);
       
 15413 					}
       
 15414 
       
 15415 					// adding range isn't always successful so we need to check range count otherwise an exception can occur
       
 15416 					self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
       
 15417 				}
       
 15418 			} else {
       
 15419 				// Is W3C Range fake range on IE
       
 15420 				if (rng.cloneRange) {
       
 15421 					try {
       
 15422 						self.tridentSel.addRange(rng);
       
 15423 						return;
       
 15424 					} catch (ex) {
       
 15425 						//IE9 throws an error here if called before selection is placed in the editor
       
 15426 					}
       
 15427 				}
       
 15428 			}
       
 15429 		},
       
 15430 
       
 15431 		/**
       
 15432 		 * Sets the current selection to the specified DOM element.
       
 15433 		 *
       
 15434 		 * @method setNode
       
 15435 		 * @param {Element} elm Element to set as the contents of the selection.
       
 15436 		 * @return {Element} Returns the element that got passed in.
       
 15437 		 * @example
       
 15438 		 * // Inserts a DOM node at current selection/caret location
       
 15439 		 * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'}));
       
 15440 		 */
       
 15441 		setNode: function(elm) {
       
 15442 			var self = this;
       
 15443 
       
 15444 			self.setContent(self.dom.getOuterHTML(elm));
       
 15445 
       
 15446 			return elm;
       
 15447 		},
       
 15448 
       
 15449 		/**
       
 15450 		 * Returns the currently selected element or the common ancestor element for both start and end of the selection.
       
 15451 		 *
       
 15452 		 * @method getNode
       
 15453 		 * @return {Element} Currently selected element or common ancestor element.
       
 15454 		 * @example
       
 15455 		 * // Alerts the currently selected elements node name
       
 15456 		 * alert(tinymce.activeEditor.selection.getNode().nodeName);
       
 15457 		 */
       
 15458 		getNode: function() {
       
 15459 			var self = this, rng = self.getRng(), elm;
       
 15460 			var startContainer = rng.startContainer, endContainer = rng.endContainer;
       
 15461 			var startOffset = rng.startOffset, endOffset = rng.endOffset, root = self.dom.getRoot();
       
 15462 
       
 15463 			function skipEmptyTextNodes(node, forwards) {
       
 15464 				var orig = node;
       
 15465 
       
 15466 				while (node && node.nodeType === 3 && node.length === 0) {
       
 15467 					node = forwards ? node.nextSibling : node.previousSibling;
       
 15468 				}
       
 15469 
       
 15470 				return node || orig;
       
 15471 			}
       
 15472 
       
 15473 			// Range maybe lost after the editor is made visible again
       
 15474 			if (!rng) {
       
 15475 				return root;
       
 15476 			}
       
 15477 
       
 15478 			if (rng.setStart) {
       
 15479 				elm = rng.commonAncestorContainer;
       
 15480 
       
 15481 				// Handle selection a image or other control like element such as anchors
       
 15482 				if (!rng.collapsed) {
       
 15483 					if (startContainer == endContainer) {
       
 15484 						if (endOffset - startOffset < 2) {
       
 15485 							if (startContainer.hasChildNodes()) {
       
 15486 								elm = startContainer.childNodes[startOffset];
       
 15487 							}
       
 15488 						}
       
 15489 					}
       
 15490 
       
 15491 					// If the anchor node is a element instead of a text node then return this element
       
 15492 					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
       
 15493 					//	return sel.anchorNode.childNodes[sel.anchorOffset];
       
 15494 
       
 15495 					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
       
 15496 					// This happens when you double click an underlined word in FireFox.
       
 15497 					if (startContainer.nodeType === 3 && endContainer.nodeType === 3) {
       
 15498 						if (startContainer.length === startOffset) {
       
 15499 							startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
       
 15500 						} else {
       
 15501 							startContainer = startContainer.parentNode;
       
 15502 						}
       
 15503 
       
 15504 						if (endOffset === 0) {
       
 15505 							endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
       
 15506 						} else {
       
 15507 							endContainer = endContainer.parentNode;
       
 15508 						}
       
 15509 
       
 15510 						if (startContainer && startContainer === endContainer) {
       
 15511 							return startContainer;
       
 15512 						}
       
 15513 					}
       
 15514 				}
       
 15515 
       
 15516 				if (elm && elm.nodeType == 3) {
       
 15517 					return elm.parentNode;
       
 15518 				}
       
 15519 
       
 15520 				return elm;
       
 15521 			}
       
 15522 
       
 15523 			elm = rng.item ? rng.item(0) : rng.parentElement();
       
 15524 
       
 15525 			// IE 7 might return elements outside the iframe
       
 15526 			if (elm.ownerDocument !== self.win.document) {
       
 15527 				elm = root;
       
 15528 			}
       
 15529 
       
 15530 			return elm;
       
 15531 		},
       
 15532 
       
 15533 		getSelectedBlocks: function(startElm, endElm) {
       
 15534 			var self = this, dom = self.dom, node, root, selectedBlocks = [];
       
 15535 
       
 15536 			root = dom.getRoot();
       
 15537 			startElm = dom.getParent(startElm || self.getStart(), dom.isBlock);
       
 15538 			endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock);
       
 15539 
       
 15540 			if (startElm && startElm != root) {
       
 15541 				selectedBlocks.push(startElm);
       
 15542 			}
       
 15543 
       
 15544 			if (startElm && endElm && startElm != endElm) {
       
 15545 				node = startElm;
       
 15546 
       
 15547 				var walker = new TreeWalker(startElm, root);
       
 15548 				while ((node = walker.next()) && node != endElm) {
       
 15549 					if (dom.isBlock(node)) {
       
 15550 						selectedBlocks.push(node);
       
 15551 					}
       
 15552 				}
       
 15553 			}
       
 15554 
       
 15555 			if (endElm && startElm != endElm && endElm != root) {
       
 15556 				selectedBlocks.push(endElm);
       
 15557 			}
       
 15558 
       
 15559 			return selectedBlocks;
       
 15560 		},
       
 15561 
       
 15562 		isForward: function() {
       
 15563 			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
       
 15564 
       
 15565 			// No support for selection direction then always return true
       
 15566 			if (!sel || !sel.anchorNode || !sel.focusNode) {
       
 15567 				return true;
       
 15568 			}
       
 15569 
       
 15570 			anchorRange = dom.createRng();
       
 15571 			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
       
 15572 			anchorRange.collapse(true);
       
 15573 
       
 15574 			focusRange = dom.createRng();
       
 15575 			focusRange.setStart(sel.focusNode, sel.focusOffset);
       
 15576 			focusRange.collapse(true);
       
 15577 
       
 15578 			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
       
 15579 		},
       
 15580 
       
 15581 		normalize: function() {
       
 15582 			var self = this, rng = self.getRng();
       
 15583 
       
 15584 			if (Env.range && new RangeUtils(self.dom).normalize(rng)) {
       
 15585 				self.setRng(rng, self.isForward());
       
 15586 			}
       
 15587 
       
 15588 			return rng;
       
 15589 		},
       
 15590 
       
 15591 		/**
       
 15592 		 * Executes callback when the current selection starts/stops matching the specified selector. The current
       
 15593 		 * state will be passed to the callback as it's first argument.
       
 15594 		 *
       
 15595 		 * @method selectorChanged
       
 15596 		 * @param {String} selector CSS selector to check for.
       
 15597 		 * @param {function} callback Callback with state and args when the selector is matches or not.
       
 15598 		 */
       
 15599 		selectorChanged: function(selector, callback) {
       
 15600 			var self = this, currentSelectors;
       
 15601 
       
 15602 			if (!self.selectorChangedData) {
       
 15603 				self.selectorChangedData = {};
       
 15604 				currentSelectors = {};
       
 15605 
       
 15606 				self.editor.on('NodeChange', function(e) {
       
 15607 					var node = e.element, dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
       
 15608 
       
 15609 					// Check for new matching selectors
       
 15610 					each(self.selectorChangedData, function(callbacks, selector) {
       
 15611 						each(parents, function(node) {
       
 15612 							if (dom.is(node, selector)) {
       
 15613 								if (!currentSelectors[selector]) {
       
 15614 									// Execute callbacks
       
 15615 									each(callbacks, function(callback) {
       
 15616 										callback(true, {node: node, selector: selector, parents: parents});
       
 15617 									});
       
 15618 
       
 15619 									currentSelectors[selector] = callbacks;
       
 15620 								}
       
 15621 
       
 15622 								matchedSelectors[selector] = callbacks;
       
 15623 								return false;
       
 15624 							}
       
 15625 						});
       
 15626 					});
       
 15627 
       
 15628 					// Check if current selectors still match
       
 15629 					each(currentSelectors, function(callbacks, selector) {
       
 15630 						if (!matchedSelectors[selector]) {
       
 15631 							delete currentSelectors[selector];
       
 15632 
       
 15633 							each(callbacks, function(callback) {
       
 15634 								callback(false, {node: node, selector: selector, parents: parents});
       
 15635 							});
       
 15636 						}
       
 15637 					});
       
 15638 				});
       
 15639 			}
       
 15640 
       
 15641 			// Add selector listeners
       
 15642 			if (!self.selectorChangedData[selector]) {
       
 15643 				self.selectorChangedData[selector] = [];
       
 15644 			}
       
 15645 
       
 15646 			self.selectorChangedData[selector].push(callback);
       
 15647 
       
 15648 			return self;
       
 15649 		},
       
 15650 
       
 15651 		getScrollContainer: function() {
       
 15652 			var scrollContainer, node = this.dom.getRoot();
       
 15653 
       
 15654 			while (node && node.nodeName != 'BODY') {
       
 15655 				if (node.scrollHeight > node.clientHeight) {
       
 15656 					scrollContainer = node;
       
 15657 					break;
       
 15658 				}
       
 15659 
       
 15660 				node = node.parentNode;
       
 15661 			}
       
 15662 
       
 15663 			return scrollContainer;
       
 15664 		},
       
 15665 
       
 15666 		scrollIntoView: function(elm) {
       
 15667 			var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH;
       
 15668 
       
 15669 			function getPos(elm) {
       
 15670 				var x = 0, y = 0;
       
 15671 
       
 15672 				var offsetParent = elm;
       
 15673 				while (offsetParent && offsetParent.nodeType) {
       
 15674 					x += offsetParent.offsetLeft || 0;
       
 15675 					y += offsetParent.offsetTop || 0;
       
 15676 					offsetParent = offsetParent.offsetParent;
       
 15677 				}
       
 15678 
       
 15679 				return {x: x, y: y};
       
 15680 			}
       
 15681 
       
 15682 			if (root.nodeName != 'BODY') {
       
 15683 				var scrollContainer = self.getScrollContainer();
       
 15684 				if (scrollContainer) {
       
 15685 					y = getPos(elm).y - getPos(scrollContainer).y;
       
 15686 					viewPortH = scrollContainer.clientHeight;
       
 15687 					viewPortY = scrollContainer.scrollTop;
       
 15688 					if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
       
 15689 						scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
       
 15690 					}
       
 15691 
       
 15692 					return;
       
 15693 				}
       
 15694 			}
       
 15695 
       
 15696 			viewPort = dom.getViewPort(self.editor.getWin());
       
 15697 			y = dom.getPos(elm).y;
       
 15698 			viewPortY = viewPort.y;
       
 15699 			viewPortH = viewPort.h;
       
 15700 			if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
       
 15701 				self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
       
 15702 			}
       
 15703 		},
       
 15704 
       
 15705 		placeCaretAt: function(clientX, clientY) {
       
 15706 			var doc = this.editor.getDoc(), rng, point;
       
 15707 
       
 15708 			if (doc.caretPositionFromPoint) {
       
 15709 				point = doc.caretPositionFromPoint(clientX, clientY);
       
 15710 				rng = doc.createRange();
       
 15711 				rng.setStart(point.offsetNode, point.offset);
       
 15712 				rng.collapse(true);
       
 15713 			} else if (doc.caretRangeFromPoint) {
       
 15714 				rng = doc.caretRangeFromPoint(clientX, clientY);
       
 15715 			} else if (doc.body.createTextRange) {
       
 15716 				rng = doc.body.createTextRange();
       
 15717 
       
 15718 				try {
       
 15719 					rng.moveToPoint(clientX, clientY);
       
 15720 					rng.collapse(true);
       
 15721 				} catch (ex) {
       
 15722 					rng.collapse(clientY < doc.body.clientHeight);
       
 15723 				}
       
 15724 			}
       
 15725 
       
 15726 			this.setRng(rng);
       
 15727 		},
       
 15728 
       
 15729 		_moveEndPoint: function(rng, node, start) {
       
 15730 			var root = node, walker = new TreeWalker(node, root);
       
 15731 			var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
       
 15732 
       
 15733 			do {
       
 15734 				// Text node
       
 15735 				if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
       
 15736 					if (start) {
       
 15737 						rng.setStart(node, 0);
       
 15738 					} else {
       
 15739 						rng.setEnd(node, node.nodeValue.length);
       
 15740 					}
       
 15741 
       
 15742 					return;
       
 15743 				}
       
 15744 
       
 15745 				// BR/IMG/INPUT elements but not table cells
       
 15746 				if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) {
       
 15747 					if (start) {
       
 15748 						rng.setStartBefore(node);
       
 15749 					} else {
       
 15750 						if (node.nodeName == 'BR') {
       
 15751 							rng.setEndBefore(node);
       
 15752 						} else {
       
 15753 							rng.setEndAfter(node);
       
 15754 						}
       
 15755 					}
       
 15756 
       
 15757 					return;
       
 15758 				}
       
 15759 
       
 15760 				// Found empty text block old IE can place the selection inside those
       
 15761 				if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
       
 15762 					if (start) {
       
 15763 						rng.setStart(node, 0);
       
 15764 					} else {
       
 15765 						rng.setEnd(node, 0);
       
 15766 					}
       
 15767 
       
 15768 					return;
       
 15769 				}
       
 15770 			} while ((node = (start ? walker.next() : walker.prev())));
       
 15771 
       
 15772 			// Failed to find any text node or other suitable location then move to the root of body
       
 15773 			if (root.nodeName == 'BODY') {
       
 15774 				if (start) {
       
 15775 					rng.setStart(root, 0);
       
 15776 				} else {
       
 15777 					rng.setEnd(root, root.childNodes.length);
       
 15778 				}
       
 15779 			}
       
 15780 		},
       
 15781 
       
 15782 		destroy: function() {
       
 15783 			this.win = null;
       
 15784 			this.controlSelection.destroy();
       
 15785 		}
       
 15786 	};
       
 15787 
       
 15788 	return Selection;
       
 15789 });
       
 15790 
       
 15791 // Included from: js/tinymce/classes/dom/ElementUtils.js
       
 15792 
       
 15793 /**
       
 15794  * ElementUtils.js
       
 15795  *
       
 15796  * Copyright, Moxiecode Systems AB
       
 15797  * Released under LGPL License.
       
 15798  *
       
 15799  * License: http://www.tinymce.com/license
       
 15800  * Contributing: http://www.tinymce.com/contributing
       
 15801  */
       
 15802 
       
 15803 /**
       
 15804  * Utility class for various element specific functions.
       
 15805  *
       
 15806  * @private
       
 15807  */
       
 15808 define("tinymce/dom/ElementUtils", [
       
 15809 	"tinymce/dom/BookmarkManager",
       
 15810 	"tinymce/util/Tools"
       
 15811 ], function(BookmarkManager, Tools) {
       
 15812 	var each = Tools.each;
       
 15813 
       
 15814 	function ElementUtils(dom) {
       
 15815 		/**
       
 15816 		 * Compares two nodes and checks if it's attributes and styles matches.
       
 15817 		 * This doesn't compare classes as items since their order is significant.
       
 15818 		 *
       
 15819 		 * @method compare
       
 15820 		 * @param {Node} node1 First node to compare with.
       
 15821 		 * @param {Node} node2 Second node to compare with.
       
 15822 		 * @return {boolean} True/false if the nodes are the same or not.
       
 15823 		 */
       
 15824 		this.compare = function(node1, node2) {
       
 15825 			// Not the same name
       
 15826 			if (node1.nodeName != node2.nodeName) {
       
 15827 				return false;
       
 15828 			}
       
 15829 
       
 15830 			/**
       
 15831 			 * Returns all the nodes attributes excluding internal ones, styles and classes.
       
 15832 			 *
       
 15833 			 * @private
       
 15834 			 * @param {Node} node Node to get attributes from.
       
 15835 			 * @return {Object} Name/value object with attributes and attribute values.
       
 15836 			 */
       
 15837 			function getAttribs(node) {
       
 15838 				var attribs = {};
       
 15839 
       
 15840 				each(dom.getAttribs(node), function(attr) {
       
 15841 					var name = attr.nodeName.toLowerCase();
       
 15842 
       
 15843 					// Don't compare internal attributes or style
       
 15844 					if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
       
 15845 						attribs[name] = dom.getAttrib(node, name);
       
 15846 					}
       
 15847 				});
       
 15848 
       
 15849 				return attribs;
       
 15850 			}
       
 15851 
       
 15852 			/**
       
 15853 			 * Compares two objects checks if it's key + value exists in the other one.
       
 15854 			 *
       
 15855 			 * @private
       
 15856 			 * @param {Object} obj1 First object to compare.
       
 15857 			 * @param {Object} obj2 Second object to compare.
       
 15858 			 * @return {boolean} True/false if the objects matches or not.
       
 15859 			 */
       
 15860 			function compareObjects(obj1, obj2) {
       
 15861 				var value, name;
       
 15862 
       
 15863 				for (name in obj1) {
       
 15864 					// Obj1 has item obj2 doesn't have
       
 15865 					if (obj1.hasOwnProperty(name)) {
       
 15866 						value = obj2[name];
       
 15867 
       
 15868 						// Obj2 doesn't have obj1 item
       
 15869 						if (typeof value == "undefined") {
       
 15870 							return false;
       
 15871 						}
       
 15872 
       
 15873 						// Obj2 item has a different value
       
 15874 						if (obj1[name] != value) {
       
 15875 							return false;
       
 15876 						}
       
 15877 
       
 15878 						// Delete similar value
       
 15879 						delete obj2[name];
       
 15880 					}
       
 15881 				}
       
 15882 
       
 15883 				// Check if obj 2 has something obj 1 doesn't have
       
 15884 				for (name in obj2) {
       
 15885 					// Obj2 has item obj1 doesn't have
       
 15886 					if (obj2.hasOwnProperty(name)) {
       
 15887 						return false;
       
 15888 					}
       
 15889 				}
       
 15890 
       
 15891 				return true;
       
 15892 			}
       
 15893 
       
 15894 			// Attribs are not the same
       
 15895 			if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
       
 15896 				return false;
       
 15897 			}
       
 15898 
       
 15899 			// Styles are not the same
       
 15900 			if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
       
 15901 				return false;
       
 15902 			}
       
 15903 
       
 15904 			return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2);
       
 15905 		};
       
 15906 	}
       
 15907 
       
 15908 	return ElementUtils;
       
 15909 });
       
 15910 
       
 15911 // Included from: js/tinymce/classes/fmt/Preview.js
       
 15912 
       
 15913 /**
       
 15914  * Preview.js
       
 15915  *
       
 15916  * Copyright, Moxiecode Systems AB
       
 15917  * Released under LGPL License.
       
 15918  *
       
 15919  * License: http://www.tinymce.com/license
       
 15920  * Contributing: http://www.tinymce.com/contributing
       
 15921  */
       
 15922 
       
 15923 /**
       
 15924  * Internal class for generating previews styles for formats.
       
 15925  *
       
 15926  * Example:
       
 15927  *  Preview.getCssText(editor, 'bold');
       
 15928  *
       
 15929  * @class tinymce.fmt.Preview
       
 15930  * @private
       
 15931  */
       
 15932 define("tinymce/fmt/Preview", [
       
 15933 	"tinymce/util/Tools"
       
 15934 ], function(Tools) {
       
 15935 	var each = Tools.each;
       
 15936 
       
 15937 	function getCssText(editor, format) {
       
 15938 		var name, previewElm, dom = editor.dom;
       
 15939 		var previewCss = '', parentFontSize, previewStyles;
       
 15940 
       
 15941 		previewStyles = editor.settings.preview_styles;
       
 15942 
       
 15943 		// No preview forced
       
 15944 		if (previewStyles === false) {
       
 15945 			return '';
       
 15946 		}
       
 15947 
       
 15948 		// Default preview
       
 15949 		if (!previewStyles) {
       
 15950 			previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
       
 15951 				'text-transform color background-color border border-radius outline text-shadow';
       
 15952 		}
       
 15953 
       
 15954 		// Removes any variables since these can't be previewed
       
 15955 		function removeVars(val) {
       
 15956 			return val.replace(/%(\w+)/g, '');
       
 15957 		}
       
 15958 
       
 15959 		// Create block/inline element to use for preview
       
 15960 		if (typeof format == "string") {
       
 15961 			format = editor.formatter.get(format);
       
 15962 			if (!format) {
       
 15963 				return;
       
 15964 			}
       
 15965 
       
 15966 			format = format[0];
       
 15967 		}
       
 15968 
       
 15969 		name = format.block || format.inline || 'span';
       
 15970 		previewElm = dom.create(name);
       
 15971 
       
 15972 		// Add format styles to preview element
       
 15973 		each(format.styles, function(value, name) {
       
 15974 			value = removeVars(value);
       
 15975 
       
 15976 			if (value) {
       
 15977 				dom.setStyle(previewElm, name, value);
       
 15978 			}
       
 15979 		});
       
 15980 
       
 15981 		// Add attributes to preview element
       
 15982 		each(format.attributes, function(value, name) {
       
 15983 			value = removeVars(value);
       
 15984 
       
 15985 			if (value) {
       
 15986 				dom.setAttrib(previewElm, name, value);
       
 15987 			}
       
 15988 		});
       
 15989 
       
 15990 		// Add classes to preview element
       
 15991 		each(format.classes, function(value) {
       
 15992 			value = removeVars(value);
       
 15993 
       
 15994 			if (!dom.hasClass(previewElm, value)) {
       
 15995 				dom.addClass(previewElm, value);
       
 15996 			}
       
 15997 		});
       
 15998 
       
 15999 		editor.fire('PreviewFormats');
       
 16000 
       
 16001 		// Add the previewElm outside the visual area
       
 16002 		dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
       
 16003 		editor.getBody().appendChild(previewElm);
       
 16004 
       
 16005 		// Get parent container font size so we can compute px values out of em/% for older IE:s
       
 16006 		parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
       
 16007 		parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
       
 16008 
       
 16009 		each(previewStyles.split(' '), function(name) {
       
 16010 			var value = dom.getStyle(previewElm, name, true);
       
 16011 
       
 16012 			// If background is transparent then check if the body has a background color we can use
       
 16013 			if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
       
 16014 				value = dom.getStyle(editor.getBody(), name, true);
       
 16015 
       
 16016 				// Ignore white since it's the default color, not the nicest fix
       
 16017 				// TODO: Fix this by detecting runtime style
       
 16018 				if (dom.toHex(value).toLowerCase() == '#ffffff') {
       
 16019 					return;
       
 16020 				}
       
 16021 			}
       
 16022 
       
 16023 			if (name == 'color') {
       
 16024 				// Ignore black since it's the default color, not the nicest fix
       
 16025 				// TODO: Fix this by detecting runtime style
       
 16026 				if (dom.toHex(value).toLowerCase() == '#000000') {
       
 16027 					return;
       
 16028 				}
       
 16029 			}
       
 16030 
       
 16031 			// Old IE won't calculate the font size so we need to do that manually
       
 16032 			if (name == 'font-size') {
       
 16033 				if (/em|%$/.test(value)) {
       
 16034 					if (parentFontSize === 0) {
       
 16035 						return;
       
 16036 					}
       
 16037 
       
 16038 					// Convert font size from em/% to px
       
 16039 					value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
       
 16040 					value = (value * parentFontSize) + 'px';
       
 16041 				}
       
 16042 			}
       
 16043 
       
 16044 			if (name == "border" && value) {
       
 16045 				previewCss += 'padding:0 2px;';
       
 16046 			}
       
 16047 
       
 16048 			previewCss += name + ':' + value + ';';
       
 16049 		});
       
 16050 
       
 16051 		editor.fire('AfterPreviewFormats');
       
 16052 
       
 16053 		//previewCss += 'line-height:normal';
       
 16054 
       
 16055 		dom.remove(previewElm);
       
 16056 
       
 16057 		return previewCss;
       
 16058 	}
       
 16059 
       
 16060 	return {
       
 16061 		getCssText: getCssText
       
 16062 	};
       
 16063 });
       
 16064 
       
 16065 // Included from: js/tinymce/classes/Formatter.js
       
 16066 
       
 16067 /**
       
 16068  * Formatter.js
       
 16069  *
       
 16070  * Copyright, Moxiecode Systems AB
       
 16071  * Released under LGPL License.
       
 16072  *
       
 16073  * License: http://www.tinymce.com/license
       
 16074  * Contributing: http://www.tinymce.com/contributing
       
 16075  */
       
 16076 
       
 16077 /**
       
 16078  * Text formatter engine class. This class is used to apply formats like bold, italic, font size
       
 16079  * etc to the current selection or specific nodes. This engine was build to replace the browsers
       
 16080  * default formatting logic for execCommand due to it's inconsistent and buggy behavior.
       
 16081  *
       
 16082  * @class tinymce.Formatter
       
 16083  * @example
       
 16084  *  tinymce.activeEditor.formatter.register('mycustomformat', {
       
 16085  *    inline: 'span',
       
 16086  *    styles: {color: '#ff0000'}
       
 16087  *  });
       
 16088  *
       
 16089  *  tinymce.activeEditor.formatter.apply('mycustomformat');
       
 16090  */
       
 16091 define("tinymce/Formatter", [
       
 16092 	"tinymce/dom/TreeWalker",
       
 16093 	"tinymce/dom/RangeUtils",
       
 16094 	"tinymce/dom/BookmarkManager",
       
 16095 	"tinymce/dom/ElementUtils",
       
 16096 	"tinymce/util/Tools",
       
 16097 	"tinymce/fmt/Preview"
       
 16098 ], function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, Tools, Preview) {
       
 16099 	/**
       
 16100 	 * Constructs a new formatter instance.
       
 16101 	 *
       
 16102 	 * @constructor Formatter
       
 16103 	 * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to.
       
 16104 	 */
       
 16105 	return function(ed) {
       
 16106 		var formats = {},
       
 16107 			dom = ed.dom,
       
 16108 			selection = ed.selection,
       
 16109 			rangeUtils = new RangeUtils(dom),
       
 16110 			isValid = ed.schema.isValidChild,
       
 16111 			isBlock = dom.isBlock,
       
 16112 			forcedRootBlock = ed.settings.forced_root_block,
       
 16113 			nodeIndex = dom.nodeIndex,
       
 16114 			INVISIBLE_CHAR = '\uFEFF',
       
 16115 			MCE_ATTR_RE = /^(src|href|style)$/,
       
 16116 			FALSE = false,
       
 16117 			TRUE = true,
       
 16118 			formatChangeData,
       
 16119 			undef,
       
 16120 			getContentEditable = dom.getContentEditable,
       
 16121 			disableCaretContainer,
       
 16122 			markCaretContainersBogus,
       
 16123 			isBookmarkNode = BookmarkManager.isBookmarkNode;
       
 16124 
       
 16125 		var each = Tools.each,
       
 16126 			grep = Tools.grep,
       
 16127 			walk = Tools.walk,
       
 16128 			extend = Tools.extend;
       
 16129 
       
 16130 		function isTextBlock(name) {
       
 16131 			if (name.nodeType) {
       
 16132 				name = name.nodeName;
       
 16133 			}
       
 16134 
       
 16135 			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
       
 16136 		}
       
 16137 
       
 16138 		function isTableCell(node) {
       
 16139 			return /^(TH|TD)$/.test(node.nodeName);
       
 16140 		}
       
 16141 
       
 16142 		function getParents(node, selector) {
       
 16143 			return dom.getParents(node, selector, dom.getRoot());
       
 16144 		}
       
 16145 
       
 16146 		function isCaretNode(node) {
       
 16147 			return node.nodeType === 1 && node.id === '_mce_caret';
       
 16148 		}
       
 16149 
       
 16150 		function defaultFormats() {
       
 16151 			register({
       
 16152 				valigntop: [
       
 16153 					{selector: 'td,th', styles: {'verticalAlign': 'top'}}
       
 16154 				],
       
 16155 
       
 16156 				valignmiddle: [
       
 16157 					{selector: 'td,th', styles: {'verticalAlign': 'middle'}}
       
 16158 				],
       
 16159 
       
 16160 				valignbottom: [
       
 16161 					{selector: 'td,th', styles: {'verticalAlign': 'bottom'}}
       
 16162 				],
       
 16163 
       
 16164 				alignleft: [
       
 16165 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'left'}, defaultBlock: 'div'},
       
 16166 					{selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
       
 16167 				],
       
 16168 
       
 16169 				aligncenter: [
       
 16170 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'center'}, defaultBlock: 'div'},
       
 16171 					{selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
       
 16172 					{selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
       
 16173 				],
       
 16174 
       
 16175 				alignright: [
       
 16176 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'right'}, defaultBlock: 'div'},
       
 16177 					{selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
       
 16178 				],
       
 16179 
       
 16180 				alignjustify: [
       
 16181 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'justify'}, defaultBlock: 'div'}
       
 16182 				],
       
 16183 
       
 16184 				bold: [
       
 16185 					{inline: 'strong', remove: 'all'},
       
 16186 					{inline: 'span', styles: {fontWeight: 'bold'}},
       
 16187 					{inline: 'b', remove: 'all'}
       
 16188 				],
       
 16189 
       
 16190 				italic: [
       
 16191 					{inline: 'em', remove: 'all'},
       
 16192 					{inline: 'span', styles: {fontStyle: 'italic'}},
       
 16193 					{inline: 'i', remove: 'all'}
       
 16194 				],
       
 16195 
       
 16196 				underline: [
       
 16197 					{inline: 'span', styles: {textDecoration: 'underline'}, exact: true},
       
 16198 					{inline: 'u', remove: 'all'}
       
 16199 				],
       
 16200 
       
 16201 				strikethrough: [
       
 16202 					{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
       
 16203 					{inline: 'strike', remove: 'all'}
       
 16204 				],
       
 16205 
       
 16206 				forecolor: {inline: 'span', styles: {color: '%value'}, links: true, remove_similar: true},
       
 16207 				hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, links: true, remove_similar: true},
       
 16208 				fontname: {inline: 'span', styles: {fontFamily: '%value'}},
       
 16209 				fontsize: {inline: 'span', styles: {fontSize: '%value'}},
       
 16210 				fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
       
 16211 				blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
       
 16212 				subscript: {inline: 'sub'},
       
 16213 				superscript: {inline: 'sup'},
       
 16214 				code: {inline: 'code'},
       
 16215 
       
 16216 				link: {inline: 'a', selector: 'a', remove: 'all', split: true, deep: true,
       
 16217 					onmatch: function() {
       
 16218 						return true;
       
 16219 					},
       
 16220 
       
 16221 					onformat: function(elm, fmt, vars) {
       
 16222 						each(vars, function(value, key) {
       
 16223 							dom.setAttrib(elm, key, value);
       
 16224 						});
       
 16225 					}
       
 16226 				},
       
 16227 
       
 16228 				removeformat: [
       
 16229 					{
       
 16230 						selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins',
       
 16231 						remove: 'all',
       
 16232 						split: true,
       
 16233 						expand: false,
       
 16234 						block_expand: true,
       
 16235 						deep: true
       
 16236 					},
       
 16237 					{selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
       
 16238 					{selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
       
 16239 				]
       
 16240 			});
       
 16241 
       
 16242 			// Register default block formats
       
 16243 			each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) {
       
 16244 				register(name, {block: name, remove: 'all'});
       
 16245 			});
       
 16246 
       
 16247 			// Register user defined formats
       
 16248 			register(ed.settings.formats);
       
 16249 		}
       
 16250 
       
 16251 		function addKeyboardShortcuts() {
       
 16252 			// Add some inline shortcuts
       
 16253 			ed.addShortcut('meta+b', 'bold_desc', 'Bold');
       
 16254 			ed.addShortcut('meta+i', 'italic_desc', 'Italic');
       
 16255 			ed.addShortcut('meta+u', 'underline_desc', 'Underline');
       
 16256 
       
 16257 			// BlockFormat shortcuts keys
       
 16258 			for (var i = 1; i <= 6; i++) {
       
 16259 				ed.addShortcut('access+' + i, '', ['FormatBlock', false, 'h' + i]);
       
 16260 			}
       
 16261 
       
 16262 			ed.addShortcut('access+7', '', ['FormatBlock', false, 'p']);
       
 16263 			ed.addShortcut('access+8', '', ['FormatBlock', false, 'div']);
       
 16264 			ed.addShortcut('access+9', '', ['FormatBlock', false, 'address']);
       
 16265 		}
       
 16266 
       
 16267 		// Public functions
       
 16268 
       
 16269 		/**
       
 16270 		 * Returns the format by name or all formats if no name is specified.
       
 16271 		 *
       
 16272 		 * @method get
       
 16273 		 * @param {String} name Optional name to retrive by.
       
 16274 		 * @return {Array/Object} Array/Object with all registred formats or a specific format.
       
 16275 		 */
       
 16276 		function get(name) {
       
 16277 			return name ? formats[name] : formats;
       
 16278 		}
       
 16279 
       
 16280 		/**
       
 16281 		 * Registers a specific format by name.
       
 16282 		 *
       
 16283 		 * @method register
       
 16284 		 * @param {Object/String} name Name of the format for example "bold".
       
 16285 		 * @param {Object/Array} format Optional format object or array of format variants
       
 16286 		 * can only be omitted if the first arg is an object.
       
 16287 		 */
       
 16288 		function register(name, format) {
       
 16289 			if (name) {
       
 16290 				if (typeof name !== 'string') {
       
 16291 					each(name, function(format, name) {
       
 16292 						register(name, format);
       
 16293 					});
       
 16294 				} else {
       
 16295 					// Force format into array and add it to internal collection
       
 16296 					format = format.length ? format : [format];
       
 16297 
       
 16298 					each(format, function(format) {
       
 16299 						// Set deep to false by default on selector formats this to avoid removing
       
 16300 						// alignment on images inside paragraphs when alignment is changed on paragraphs
       
 16301 						if (format.deep === undef) {
       
 16302 							format.deep = !format.selector;
       
 16303 						}
       
 16304 
       
 16305 						// Default to true
       
 16306 						if (format.split === undef) {
       
 16307 							format.split = !format.selector || format.inline;
       
 16308 						}
       
 16309 
       
 16310 						// Default to true
       
 16311 						if (format.remove === undef && format.selector && !format.inline) {
       
 16312 							format.remove = 'none';
       
 16313 						}
       
 16314 
       
 16315 						// Mark format as a mixed format inline + block level
       
 16316 						if (format.selector && format.inline) {
       
 16317 							format.mixed = true;
       
 16318 							format.block_expand = true;
       
 16319 						}
       
 16320 
       
 16321 						// Split classes if needed
       
 16322 						if (typeof format.classes === 'string') {
       
 16323 							format.classes = format.classes.split(/\s+/);
       
 16324 						}
       
 16325 					});
       
 16326 
       
 16327 					formats[name] = format;
       
 16328 				}
       
 16329 			}
       
 16330 		}
       
 16331 
       
 16332 		/**
       
 16333 		 * Unregister a specific format by name.
       
 16334 		 *
       
 16335 		 * @method unregister
       
 16336 		 * @param {String} name Name of the format for example "bold".
       
 16337 		 */
       
 16338 		function unregister(name) {
       
 16339 			if (name && formats[name]) {
       
 16340 				delete formats[name];
       
 16341 			}
       
 16342 
       
 16343 			return formats;
       
 16344 		}
       
 16345 
       
 16346 		function getTextDecoration(node) {
       
 16347 			var decoration;
       
 16348 
       
 16349 			ed.dom.getParent(node, function(n) {
       
 16350 				decoration = ed.dom.getStyle(n, 'text-decoration');
       
 16351 				return decoration && decoration !== 'none';
       
 16352 			});
       
 16353 
       
 16354 			return decoration;
       
 16355 		}
       
 16356 
       
 16357 		function processUnderlineAndColor(node) {
       
 16358 			var textDecoration;
       
 16359 			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
       
 16360 				textDecoration = getTextDecoration(node.parentNode);
       
 16361 				if (ed.dom.getStyle(node, 'color') && textDecoration) {
       
 16362 					ed.dom.setStyle(node, 'text-decoration', textDecoration);
       
 16363 				} else if (ed.dom.getStyle(node, 'text-decoration') === textDecoration) {
       
 16364 					ed.dom.setStyle(node, 'text-decoration', null);
       
 16365 				}
       
 16366 			}
       
 16367 		}
       
 16368 
       
 16369 		/**
       
 16370 		 * Applies the specified format to the current selection or specified node.
       
 16371 		 *
       
 16372 		 * @method apply
       
 16373 		 * @param {String} name Name of format to apply.
       
 16374 		 * @param {Object} vars Optional list of variables to replace within format before applying it.
       
 16375 		 * @param {Node} node Optional node to apply the format to defaults to current selection.
       
 16376 		 */
       
 16377 		function apply(name, vars, node) {
       
 16378 			var formatList = get(name), format = formatList[0], bookmark, rng, isCollapsed = !node && selection.isCollapsed();
       
 16379 
       
 16380 			function setElementFormat(elm, fmt) {
       
 16381 				fmt = fmt || format;
       
 16382 
       
 16383 				if (elm) {
       
 16384 					if (fmt.onformat) {
       
 16385 						fmt.onformat(elm, fmt, vars, node);
       
 16386 					}
       
 16387 
       
 16388 					each(fmt.styles, function(value, name) {
       
 16389 						dom.setStyle(elm, name, replaceVars(value, vars));
       
 16390 					});
       
 16391 
       
 16392 					// Needed for the WebKit span spam bug
       
 16393 					// TODO: Remove this once WebKit/Blink fixes this
       
 16394 					if (fmt.styles) {
       
 16395 						var styleVal = dom.getAttrib(elm, 'style');
       
 16396 
       
 16397 						if (styleVal) {
       
 16398 							elm.setAttribute('data-mce-style', styleVal);
       
 16399 						}
       
 16400 					}
       
 16401 
       
 16402 					each(fmt.attributes, function(value, name) {
       
 16403 						dom.setAttrib(elm, name, replaceVars(value, vars));
       
 16404 					});
       
 16405 
       
 16406 					each(fmt.classes, function(value) {
       
 16407 						value = replaceVars(value, vars);
       
 16408 
       
 16409 						if (!dom.hasClass(elm, value)) {
       
 16410 							dom.addClass(elm, value);
       
 16411 						}
       
 16412 					});
       
 16413 				}
       
 16414 			}
       
 16415 
       
 16416 			function adjustSelectionToVisibleSelection() {
       
 16417 				function findSelectionEnd(start, end) {
       
 16418 					var walker = new TreeWalker(end);
       
 16419 					for (node = walker.current(); node; node = walker.prev()) {
       
 16420 						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
       
 16421 							return node;
       
 16422 						}
       
 16423 					}
       
 16424 				}
       
 16425 
       
 16426 				// Adjust selection so that a end container with a end offset of zero is not included in the selection
       
 16427 				// as this isn't visible to the user.
       
 16428 				var rng = ed.selection.getRng();
       
 16429 				var start = rng.startContainer;
       
 16430 				var end = rng.endContainer;
       
 16431 
       
 16432 				if (start != end && rng.endOffset === 0) {
       
 16433 					var newEnd = findSelectionEnd(start, end);
       
 16434 					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
       
 16435 
       
 16436 					rng.setEnd(newEnd, endOffset);
       
 16437 				}
       
 16438 
       
 16439 				return rng;
       
 16440 			}
       
 16441 
       
 16442 			function applyRngStyle(rng, bookmark, node_specific) {
       
 16443 				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
       
 16444 
       
 16445 				// Setup wrapper element
       
 16446 				wrapName = format.inline || format.block;
       
 16447 				wrapElm = dom.create(wrapName);
       
 16448 				setElementFormat(wrapElm);
       
 16449 
       
 16450 				rangeUtils.walk(rng, function(nodes) {
       
 16451 					var currentWrapElm;
       
 16452 
       
 16453 					/**
       
 16454 					 * Process a list of nodes wrap them.
       
 16455 					 */
       
 16456 					function process(node) {
       
 16457 						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
       
 16458 
       
 16459 						lastContentEditable = contentEditable;
       
 16460 						nodeName = node.nodeName.toLowerCase();
       
 16461 						parentName = node.parentNode.nodeName.toLowerCase();
       
 16462 
       
 16463 						// Node has a contentEditable value
       
 16464 						if (node.nodeType === 1 && getContentEditable(node)) {
       
 16465 							lastContentEditable = contentEditable;
       
 16466 							contentEditable = getContentEditable(node) === "true";
       
 16467 							hasContentEditableState = true; // We don't want to wrap the container only it's children
       
 16468 						}
       
 16469 
       
 16470 						// Stop wrapping on br elements
       
 16471 						if (isEq(nodeName, 'br')) {
       
 16472 							currentWrapElm = 0;
       
 16473 
       
 16474 							// Remove any br elements when we wrap things
       
 16475 							if (format.block) {
       
 16476 								dom.remove(node);
       
 16477 							}
       
 16478 
       
 16479 							return;
       
 16480 						}
       
 16481 
       
 16482 						// If node is wrapper type
       
 16483 						if (format.wrapper && matchNode(node, name, vars)) {
       
 16484 							currentWrapElm = 0;
       
 16485 							return;
       
 16486 						}
       
 16487 
       
 16488 						// Can we rename the block
       
 16489 						// TODO: Break this if up, too complex
       
 16490 						if (contentEditable && !hasContentEditableState && format.block &&
       
 16491 							!format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) {
       
 16492 							node = dom.rename(node, wrapName);
       
 16493 							setElementFormat(node);
       
 16494 							newWrappers.push(node);
       
 16495 							currentWrapElm = 0;
       
 16496 							return;
       
 16497 						}
       
 16498 
       
 16499 						// Handle selector patterns
       
 16500 						if (format.selector) {
       
 16501 							// Look for matching formats
       
 16502 							each(formatList, function(format) {
       
 16503 								// Check collapsed state if it exists
       
 16504 								if ('collapsed' in format && format.collapsed !== isCollapsed) {
       
 16505 									return;
       
 16506 								}
       
 16507 
       
 16508 								if (dom.is(node, format.selector) && !isCaretNode(node)) {
       
 16509 									setElementFormat(node, format);
       
 16510 									found = true;
       
 16511 								}
       
 16512 							});
       
 16513 
       
 16514 							// Continue processing if a selector match wasn't found and a inline element is defined
       
 16515 							if (!format.inline || found) {
       
 16516 								currentWrapElm = 0;
       
 16517 								return;
       
 16518 							}
       
 16519 						}
       
 16520 
       
 16521 						// Is it valid to wrap this item
       
 16522 						// TODO: Break this if up, too complex
       
 16523 						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
       
 16524 								!(!node_specific && node.nodeType === 3 &&
       
 16525 								node.nodeValue.length === 1 &&
       
 16526 								node.nodeValue.charCodeAt(0) === 65279) &&
       
 16527 								!isCaretNode(node) &&
       
 16528 								(!format.inline || !isBlock(node))) {
       
 16529 							// Start wrapping
       
 16530 							if (!currentWrapElm) {
       
 16531 								// Wrap the node
       
 16532 								currentWrapElm = dom.clone(wrapElm, FALSE);
       
 16533 								node.parentNode.insertBefore(currentWrapElm, node);
       
 16534 								newWrappers.push(currentWrapElm);
       
 16535 							}
       
 16536 
       
 16537 							currentWrapElm.appendChild(node);
       
 16538 						} else {
       
 16539 							// Start a new wrapper for possible children
       
 16540 							currentWrapElm = 0;
       
 16541 
       
 16542 							each(grep(node.childNodes), process);
       
 16543 
       
 16544 							if (hasContentEditableState) {
       
 16545 								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
       
 16546 							}
       
 16547 
       
 16548 							// End the last wrapper
       
 16549 							currentWrapElm = 0;
       
 16550 						}
       
 16551 					}
       
 16552 
       
 16553 					// Process siblings from range
       
 16554 					each(nodes, process);
       
 16555 				});
       
 16556 
       
 16557 				// Apply formats to links as well to get the color of the underline to change as well
       
 16558 				if (format.links === true) {
       
 16559 					each(newWrappers, function(node) {
       
 16560 						function process(node) {
       
 16561 							if (node.nodeName === 'A') {
       
 16562 								setElementFormat(node, format);
       
 16563 							}
       
 16564 
       
 16565 							each(grep(node.childNodes), process);
       
 16566 						}
       
 16567 
       
 16568 						process(node);
       
 16569 					});
       
 16570 				}
       
 16571 
       
 16572 				// Cleanup
       
 16573 				each(newWrappers, function(node) {
       
 16574 					var childCount;
       
 16575 
       
 16576 					function getChildCount(node) {
       
 16577 						var count = 0;
       
 16578 
       
 16579 						each(node.childNodes, function(node) {
       
 16580 							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) {
       
 16581 								count++;
       
 16582 							}
       
 16583 						});
       
 16584 
       
 16585 						return count;
       
 16586 					}
       
 16587 
       
 16588 					function mergeStyles(node) {
       
 16589 						var child, clone;
       
 16590 
       
 16591 						each(node.childNodes, function(node) {
       
 16592 							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
       
 16593 								child = node;
       
 16594 								return FALSE; // break loop
       
 16595 							}
       
 16596 						});
       
 16597 
       
 16598 						// If child was found and of the same type as the current node
       
 16599 						if (child && !isBookmarkNode(child) && matchName(child, format)) {
       
 16600 							clone = dom.clone(child, FALSE);
       
 16601 							setElementFormat(clone);
       
 16602 
       
 16603 							dom.replace(clone, node, TRUE);
       
 16604 							dom.remove(child, 1);
       
 16605 						}
       
 16606 
       
 16607 						return clone || node;
       
 16608 					}
       
 16609 
       
 16610 					childCount = getChildCount(node);
       
 16611 
       
 16612 					// Remove empty nodes but only if there is multiple wrappers and they are not block
       
 16613 					// elements so never remove single <h1></h1> since that would remove the
       
 16614 					// currrent empty block element where the caret is at
       
 16615 					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
       
 16616 						dom.remove(node, 1);
       
 16617 						return;
       
 16618 					}
       
 16619 
       
 16620 					if (format.inline || format.wrapper) {
       
 16621 						// Merges the current node with it's children of similar type to reduce the number of elements
       
 16622 						if (!format.exact && childCount === 1) {
       
 16623 							node = mergeStyles(node);
       
 16624 						}
       
 16625 
       
 16626 						// Remove/merge children
       
 16627 						each(formatList, function(format) {
       
 16628 							// Merge all children of similar type will move styles from child to parent
       
 16629 							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
       
 16630 							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
       
 16631 							each(dom.select(format.inline, node), function(child) {
       
 16632 								if (isBookmarkNode(child)) {
       
 16633 									return;
       
 16634 								}
       
 16635 
       
 16636 								removeFormat(format, vars, child, format.exact ? child : null);
       
 16637 							});
       
 16638 						});
       
 16639 
       
 16640 						// Remove child if direct parent is of same type
       
 16641 						if (matchNode(node.parentNode, name, vars)) {
       
 16642 							dom.remove(node, 1);
       
 16643 							node = 0;
       
 16644 							return TRUE;
       
 16645 						}
       
 16646 
       
 16647 						// Look for parent with similar style format
       
 16648 						if (format.merge_with_parents) {
       
 16649 							dom.getParent(node.parentNode, function(parent) {
       
 16650 								if (matchNode(parent, name, vars)) {
       
 16651 									dom.remove(node, 1);
       
 16652 									node = 0;
       
 16653 									return TRUE;
       
 16654 								}
       
 16655 							});
       
 16656 						}
       
 16657 
       
 16658 						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
       
 16659 						if (node && format.merge_siblings !== false) {
       
 16660 							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
       
 16661 							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
       
 16662 						}
       
 16663 					}
       
 16664 				});
       
 16665 			}
       
 16666 
       
 16667 			if (format) {
       
 16668 				if (node) {
       
 16669 					if (node.nodeType) {
       
 16670 						rng = dom.createRng();
       
 16671 						rng.setStartBefore(node);
       
 16672 						rng.setEndAfter(node);
       
 16673 						applyRngStyle(expandRng(rng, formatList), null, true);
       
 16674 					} else {
       
 16675 						applyRngStyle(node, null, true);
       
 16676 					}
       
 16677 				} else {
       
 16678 					if (!isCollapsed || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
       
 16679 						// Obtain selection node before selection is unselected by applyRngStyle()
       
 16680 						var curSelNode = ed.selection.getNode();
       
 16681 
       
 16682 						// If the formats have a default block and we can't find a parent block then
       
 16683 						// start wrapping it with a DIV this is for forced_root_blocks: false
       
 16684 						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
       
 16685 						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
       
 16686 							apply(formatList[0].defaultBlock);
       
 16687 						}
       
 16688 
       
 16689 						// Apply formatting to selection
       
 16690 						ed.selection.setRng(adjustSelectionToVisibleSelection());
       
 16691 						bookmark = selection.getBookmark();
       
 16692 						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
       
 16693 
       
 16694 						// Colored nodes should be underlined so that the color of the underline matches the text color.
       
 16695 						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
       
 16696 							walk(curSelNode, processUnderlineAndColor, 'childNodes');
       
 16697 							processUnderlineAndColor(curSelNode);
       
 16698 						}
       
 16699 
       
 16700 						selection.moveToBookmark(bookmark);
       
 16701 						moveStart(selection.getRng(TRUE));
       
 16702 						ed.nodeChanged();
       
 16703 					} else {
       
 16704 						performCaretAction('apply', name, vars);
       
 16705 					}
       
 16706 				}
       
 16707 			}
       
 16708 		}
       
 16709 
       
 16710 		/**
       
 16711 		 * Removes the specified format from the current selection or specified node.
       
 16712 		 *
       
 16713 		 * @method remove
       
 16714 		 * @param {String} name Name of format to remove.
       
 16715 		 * @param {Object} vars Optional list of variables to replace within format before removing it.
       
 16716 		 * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
       
 16717 		 */
       
 16718 		function remove(name, vars, node, similar) {
       
 16719 			var formatList = get(name), format = formatList[0], bookmark, rng, contentEditable = true;
       
 16720 
       
 16721 			// Merges the styles for each node
       
 16722 			function process(node) {
       
 16723 				var children, i, l, lastContentEditable, hasContentEditableState;
       
 16724 
       
 16725 				// Node has a contentEditable value
       
 16726 				if (node.nodeType === 1 && getContentEditable(node)) {
       
 16727 					lastContentEditable = contentEditable;
       
 16728 					contentEditable = getContentEditable(node) === "true";
       
 16729 					hasContentEditableState = true; // We don't want to wrap the container only it's children
       
 16730 				}
       
 16731 
       
 16732 				// Grab the children first since the nodelist might be changed
       
 16733 				children = grep(node.childNodes);
       
 16734 
       
 16735 				// Process current node
       
 16736 				if (contentEditable && !hasContentEditableState) {
       
 16737 					for (i = 0, l = formatList.length; i < l; i++) {
       
 16738 						if (removeFormat(formatList[i], vars, node, node)) {
       
 16739 							break;
       
 16740 						}
       
 16741 					}
       
 16742 				}
       
 16743 
       
 16744 				// Process the children
       
 16745 				if (format.deep) {
       
 16746 					if (children.length) {
       
 16747 						for (i = 0, l = children.length; i < l; i++) {
       
 16748 							process(children[i]);
       
 16749 						}
       
 16750 
       
 16751 						if (hasContentEditableState) {
       
 16752 							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
       
 16753 						}
       
 16754 					}
       
 16755 				}
       
 16756 			}
       
 16757 
       
 16758 			function findFormatRoot(container) {
       
 16759 				var formatRoot;
       
 16760 
       
 16761 				// Find format root
       
 16762 				each(getParents(container.parentNode).reverse(), function(parent) {
       
 16763 					var format;
       
 16764 
       
 16765 					// Find format root element
       
 16766 					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
       
 16767 						// Is the node matching the format we are looking for
       
 16768 						format = matchNode(parent, name, vars, similar);
       
 16769 						if (format && format.split !== false) {
       
 16770 							formatRoot = parent;
       
 16771 						}
       
 16772 					}
       
 16773 				});
       
 16774 
       
 16775 				return formatRoot;
       
 16776 			}
       
 16777 
       
 16778 			function wrapAndSplit(formatRoot, container, target, split) {
       
 16779 				var parent, clone, lastClone, firstClone, i, formatRootParent;
       
 16780 
       
 16781 				// Format root found then clone formats and split it
       
 16782 				if (formatRoot) {
       
 16783 					formatRootParent = formatRoot.parentNode;
       
 16784 
       
 16785 					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
       
 16786 						clone = dom.clone(parent, FALSE);
       
 16787 
       
 16788 						for (i = 0; i < formatList.length; i++) {
       
 16789 							if (removeFormat(formatList[i], vars, clone, clone)) {
       
 16790 								clone = 0;
       
 16791 								break;
       
 16792 							}
       
 16793 						}
       
 16794 
       
 16795 						// Build wrapper node
       
 16796 						if (clone) {
       
 16797 							if (lastClone) {
       
 16798 								clone.appendChild(lastClone);
       
 16799 							}
       
 16800 
       
 16801 							if (!firstClone) {
       
 16802 								firstClone = clone;
       
 16803 							}
       
 16804 
       
 16805 							lastClone = clone;
       
 16806 						}
       
 16807 					}
       
 16808 
       
 16809 					// Never split block elements if the format is mixed
       
 16810 					if (split && (!format.mixed || !isBlock(formatRoot))) {
       
 16811 						container = dom.split(formatRoot, container);
       
 16812 					}
       
 16813 
       
 16814 					// Wrap container in cloned formats
       
 16815 					if (lastClone) {
       
 16816 						target.parentNode.insertBefore(lastClone, target);
       
 16817 						firstClone.appendChild(target);
       
 16818 					}
       
 16819 				}
       
 16820 
       
 16821 				return container;
       
 16822 			}
       
 16823 
       
 16824 			function splitToFormatRoot(container) {
       
 16825 				return wrapAndSplit(findFormatRoot(container), container, container, true);
       
 16826 			}
       
 16827 
       
 16828 			function unwrap(start) {
       
 16829 				var node = dom.get(start ? '_start' : '_end'),
       
 16830 					out = node[start ? 'firstChild' : 'lastChild'];
       
 16831 
       
 16832 				// If the end is placed within the start the result will be removed
       
 16833 				// So this checks if the out node is a bookmark node if it is it
       
 16834 				// checks for another more suitable node
       
 16835 				if (isBookmarkNode(out)) {
       
 16836 					out = out[start ? 'firstChild' : 'lastChild'];
       
 16837 				}
       
 16838 
       
 16839 				// Since dom.remove removes empty text nodes then we need to try to find a better node
       
 16840 				if (out.nodeType == 3 && out.data.length === 0) {
       
 16841 					out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling;
       
 16842 				}
       
 16843 
       
 16844 				dom.remove(node, true);
       
 16845 
       
 16846 				return out;
       
 16847 			}
       
 16848 
       
 16849 			function removeRngStyle(rng) {
       
 16850 				var startContainer, endContainer;
       
 16851 				var commonAncestorContainer = rng.commonAncestorContainer;
       
 16852 
       
 16853 				rng = expandRng(rng, formatList, TRUE);
       
 16854 
       
 16855 				if (format.split) {
       
 16856 					startContainer = getContainer(rng, TRUE);
       
 16857 					endContainer = getContainer(rng);
       
 16858 
       
 16859 					if (startContainer != endContainer) {
       
 16860 						// WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN
       
 16861 						// so let's see if we can use the first child instead
       
 16862 						// This will happen if you triple click a table cell and use remove formatting
       
 16863 						if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
       
 16864 							if (startContainer.nodeName == "TR") {
       
 16865 								startContainer = startContainer.firstChild.firstChild || startContainer;
       
 16866 							} else {
       
 16867 								startContainer = startContainer.firstChild || startContainer;
       
 16868 							}
       
 16869 						}
       
 16870 
       
 16871 						// Try to adjust endContainer as well if cells on the same row were selected - bug #6410
       
 16872 						if (commonAncestorContainer &&
       
 16873 							/^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) &&
       
 16874 							isTableCell(endContainer) && endContainer.firstChild) {
       
 16875 							endContainer = endContainer.firstChild || endContainer;
       
 16876 						}
       
 16877 
       
 16878 						if (dom.isChildOf(startContainer, endContainer) && !isTableCell(startContainer) && !isTableCell(endContainer)) {
       
 16879 							startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
       
 16880 							splitToFormatRoot(startContainer);
       
 16881 							startContainer = unwrap(TRUE);
       
 16882 							return;
       
 16883 						} else {
       
 16884 							// Wrap start/end nodes in span element since these might be cloned/moved
       
 16885 							startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
       
 16886 							endContainer = wrap(endContainer, 'span', {id: '_end', 'data-mce-type': 'bookmark'});
       
 16887 
       
 16888 							// Split start/end
       
 16889 							splitToFormatRoot(startContainer);
       
 16890 							splitToFormatRoot(endContainer);
       
 16891 
       
 16892 							// Unwrap start/end to get real elements again
       
 16893 							startContainer = unwrap(TRUE);
       
 16894 							endContainer = unwrap();
       
 16895 						}
       
 16896 					} else {
       
 16897 						startContainer = endContainer = splitToFormatRoot(startContainer);
       
 16898 					}
       
 16899 
       
 16900 					// Update range positions since they might have changed after the split operations
       
 16901 					rng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer;
       
 16902 					rng.startOffset = nodeIndex(startContainer);
       
 16903 					rng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer;
       
 16904 					rng.endOffset = nodeIndex(endContainer) + 1;
       
 16905 				}
       
 16906 
       
 16907 				// Remove items between start/end
       
 16908 				rangeUtils.walk(rng, function(nodes) {
       
 16909 					each(nodes, function(node) {
       
 16910 						process(node);
       
 16911 
       
 16912 						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
       
 16913 						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' &&
       
 16914 							node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
       
 16915 							removeFormat({
       
 16916 								'deep': false,
       
 16917 								'exact': true,
       
 16918 								'inline': 'span',
       
 16919 								'styles': {
       
 16920 									'textDecoration': 'underline'
       
 16921 								}
       
 16922 							}, null, node);
       
 16923 						}
       
 16924 					});
       
 16925 				});
       
 16926 			}
       
 16927 
       
 16928 			// Handle node
       
 16929 			if (node) {
       
 16930 				if (node.nodeType) {
       
 16931 					rng = dom.createRng();
       
 16932 					rng.setStartBefore(node);
       
 16933 					rng.setEndAfter(node);
       
 16934 					removeRngStyle(rng);
       
 16935 				} else {
       
 16936 					removeRngStyle(node);
       
 16937 				}
       
 16938 
       
 16939 				return;
       
 16940 			}
       
 16941 
       
 16942 			if (!selection.isCollapsed() || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
       
 16943 				bookmark = selection.getBookmark();
       
 16944 				removeRngStyle(selection.getRng(TRUE));
       
 16945 				selection.moveToBookmark(bookmark);
       
 16946 
       
 16947 				// Check if start element still has formatting then we are at: "<b>text|</b>text"
       
 16948 				// and need to move the start into the next text node
       
 16949 				if (format.inline && match(name, vars, selection.getStart())) {
       
 16950 					moveStart(selection.getRng(true));
       
 16951 				}
       
 16952 
       
 16953 				ed.nodeChanged();
       
 16954 			} else {
       
 16955 				performCaretAction('remove', name, vars, similar);
       
 16956 			}
       
 16957 		}
       
 16958 
       
 16959 		/**
       
 16960 		 * Toggles the specified format on/off.
       
 16961 		 *
       
 16962 		 * @method toggle
       
 16963 		 * @param {String} name Name of format to apply/remove.
       
 16964 		 * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
       
 16965 		 * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
       
 16966 		 */
       
 16967 		function toggle(name, vars, node) {
       
 16968 			var fmt = get(name);
       
 16969 
       
 16970 			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
       
 16971 				remove(name, vars, node);
       
 16972 			} else {
       
 16973 				apply(name, vars, node);
       
 16974 			}
       
 16975 		}
       
 16976 
       
 16977 		/**
       
 16978 		 * Return true/false if the specified node has the specified format.
       
 16979 		 *
       
 16980 		 * @method matchNode
       
 16981 		 * @param {Node} node Node to check the format on.
       
 16982 		 * @param {String} name Format name to check.
       
 16983 		 * @param {Object} vars Optional list of variables to replace before checking it.
       
 16984 		 * @param {Boolean} similar Match format that has similar properties.
       
 16985 		 * @return {Object} Returns the format object it matches or undefined if it doesn't match.
       
 16986 		 */
       
 16987 		function matchNode(node, name, vars, similar) {
       
 16988 			var formatList = get(name), format, i, classes;
       
 16989 
       
 16990 			function matchItems(node, format, item_name) {
       
 16991 				var key, value, items = format[item_name], i;
       
 16992 
       
 16993 				// Custom match
       
 16994 				if (format.onmatch) {
       
 16995 					return format.onmatch(node, format, item_name);
       
 16996 				}
       
 16997 
       
 16998 				// Check all items
       
 16999 				if (items) {
       
 17000 					// Non indexed object
       
 17001 					if (items.length === undef) {
       
 17002 						for (key in items) {
       
 17003 							if (items.hasOwnProperty(key)) {
       
 17004 								if (item_name === 'attributes') {
       
 17005 									value = dom.getAttrib(node, key);
       
 17006 								} else {
       
 17007 									value = getStyle(node, key);
       
 17008 								}
       
 17009 
       
 17010 								if (similar && !value && !format.exact) {
       
 17011 									return;
       
 17012 								}
       
 17013 
       
 17014 								if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) {
       
 17015 									return;
       
 17016 								}
       
 17017 							}
       
 17018 						}
       
 17019 					} else {
       
 17020 						// Only one match needed for indexed arrays
       
 17021 						for (i = 0; i < items.length; i++) {
       
 17022 							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) {
       
 17023 								return format;
       
 17024 							}
       
 17025 						}
       
 17026 					}
       
 17027 				}
       
 17028 
       
 17029 				return format;
       
 17030 			}
       
 17031 
       
 17032 			if (formatList && node) {
       
 17033 				// Check each format in list
       
 17034 				for (i = 0; i < formatList.length; i++) {
       
 17035 					format = formatList[i];
       
 17036 
       
 17037 					// Name name, attributes, styles and classes
       
 17038 					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
       
 17039 						// Match classes
       
 17040 						if ((classes = format.classes)) {
       
 17041 							for (i = 0; i < classes.length; i++) {
       
 17042 								if (!dom.hasClass(node, classes[i])) {
       
 17043 									return;
       
 17044 								}
       
 17045 							}
       
 17046 						}
       
 17047 
       
 17048 						return format;
       
 17049 					}
       
 17050 				}
       
 17051 			}
       
 17052 		}
       
 17053 
       
 17054 		/**
       
 17055 		 * Matches the current selection or specified node against the specified format name.
       
 17056 		 *
       
 17057 		 * @method match
       
 17058 		 * @param {String} name Name of format to match.
       
 17059 		 * @param {Object} vars Optional list of variables to replace before checking it.
       
 17060 		 * @param {Node} node Optional node to check.
       
 17061 		 * @return {boolean} true/false if the specified selection/node matches the format.
       
 17062 		 */
       
 17063 		function match(name, vars, node) {
       
 17064 			var startNode;
       
 17065 
       
 17066 			function matchParents(node) {
       
 17067 				var root = dom.getRoot();
       
 17068 
       
 17069 				if (node === root) {
       
 17070 					return false;
       
 17071 				}
       
 17072 
       
 17073 				// Find first node with similar format settings
       
 17074 				node = dom.getParent(node, function(node) {
       
 17075 					return node.parentNode === root || !!matchNode(node, name, vars, true);
       
 17076 				});
       
 17077 
       
 17078 				// Do an exact check on the similar format element
       
 17079 				return matchNode(node, name, vars);
       
 17080 			}
       
 17081 
       
 17082 			// Check specified node
       
 17083 			if (node) {
       
 17084 				return matchParents(node);
       
 17085 			}
       
 17086 
       
 17087 			// Check selected node
       
 17088 			node = selection.getNode();
       
 17089 			if (matchParents(node)) {
       
 17090 				return TRUE;
       
 17091 			}
       
 17092 
       
 17093 			// Check start node if it's different
       
 17094 			startNode = selection.getStart();
       
 17095 			if (startNode != node) {
       
 17096 				if (matchParents(startNode)) {
       
 17097 					return TRUE;
       
 17098 				}
       
 17099 			}
       
 17100 
       
 17101 			return FALSE;
       
 17102 		}
       
 17103 
       
 17104 		/**
       
 17105 		 * Matches the current selection against the array of formats and returns a new array with matching formats.
       
 17106 		 *
       
 17107 		 * @method matchAll
       
 17108 		 * @param {Array} names Name of format to match.
       
 17109 		 * @param {Object} vars Optional list of variables to replace before checking it.
       
 17110 		 * @return {Array} Array with matched formats.
       
 17111 		 */
       
 17112 		function matchAll(names, vars) {
       
 17113 			var startElement, matchedFormatNames = [], checkedMap = {};
       
 17114 
       
 17115 			// Check start of selection for formats
       
 17116 			startElement = selection.getStart();
       
 17117 			dom.getParent(startElement, function(node) {
       
 17118 				var i, name;
       
 17119 
       
 17120 				for (i = 0; i < names.length; i++) {
       
 17121 					name = names[i];
       
 17122 
       
 17123 					if (!checkedMap[name] && matchNode(node, name, vars)) {
       
 17124 						checkedMap[name] = true;
       
 17125 						matchedFormatNames.push(name);
       
 17126 					}
       
 17127 				}
       
 17128 			}, dom.getRoot());
       
 17129 
       
 17130 			return matchedFormatNames;
       
 17131 		}
       
 17132 
       
 17133 		/**
       
 17134 		 * Returns true/false if the specified format can be applied to the current selection or not. It
       
 17135 		 * will currently only check the state for selector formats, it returns true on all other format types.
       
 17136 		 *
       
 17137 		 * @method canApply
       
 17138 		 * @param {String} name Name of format to check.
       
 17139 		 * @return {boolean} true/false if the specified format can be applied to the current selection/node.
       
 17140 		 */
       
 17141 		function canApply(name) {
       
 17142 			var formatList = get(name), startNode, parents, i, x, selector;
       
 17143 
       
 17144 			if (formatList) {
       
 17145 				startNode = selection.getStart();
       
 17146 				parents = getParents(startNode);
       
 17147 
       
 17148 				for (x = formatList.length - 1; x >= 0; x--) {
       
 17149 					selector = formatList[x].selector;
       
 17150 
       
 17151 					// Format is not selector based then always return TRUE
       
 17152 					// Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
       
 17153 					if (!selector || formatList[x].defaultBlock) {
       
 17154 						return TRUE;
       
 17155 					}
       
 17156 
       
 17157 					for (i = parents.length - 1; i >= 0; i--) {
       
 17158 						if (dom.is(parents[i], selector)) {
       
 17159 							return TRUE;
       
 17160 						}
       
 17161 					}
       
 17162 				}
       
 17163 			}
       
 17164 
       
 17165 			return FALSE;
       
 17166 		}
       
 17167 
       
 17168 		/**
       
 17169 		 * Executes the specified callback when the current selection matches the formats or not.
       
 17170 		 *
       
 17171 		 * @method formatChanged
       
 17172 		 * @param {String} formats Comma separated list of formats to check for.
       
 17173 		 * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
       
 17174 		 * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
       
 17175 		 */
       
 17176 		function formatChanged(formats, callback, similar) {
       
 17177 			var currentFormats;
       
 17178 
       
 17179 			// Setup format node change logic
       
 17180 			if (!formatChangeData) {
       
 17181 				formatChangeData = {};
       
 17182 				currentFormats = {};
       
 17183 
       
 17184 				ed.on('NodeChange', function(e) {
       
 17185 					var parents = getParents(e.element), matchedFormats = {};
       
 17186 
       
 17187 					// Ignore bogus nodes like the <a> tag created by moveStart()
       
 17188 					parents = Tools.grep(parents, function(node) {
       
 17189 						return node.nodeType == 1 && !node.getAttribute('data-mce-bogus');
       
 17190 					});
       
 17191 
       
 17192 					// Check for new formats
       
 17193 					each(formatChangeData, function(callbacks, format) {
       
 17194 						each(parents, function(node) {
       
 17195 							if (matchNode(node, format, {}, callbacks.similar)) {
       
 17196 								if (!currentFormats[format]) {
       
 17197 									// Execute callbacks
       
 17198 									each(callbacks, function(callback) {
       
 17199 										callback(true, {node: node, format: format, parents: parents});
       
 17200 									});
       
 17201 
       
 17202 									currentFormats[format] = callbacks;
       
 17203 								}
       
 17204 
       
 17205 								matchedFormats[format] = callbacks;
       
 17206 								return false;
       
 17207 							}
       
 17208 						});
       
 17209 					});
       
 17210 
       
 17211 					// Check if current formats still match
       
 17212 					each(currentFormats, function(callbacks, format) {
       
 17213 						if (!matchedFormats[format]) {
       
 17214 							delete currentFormats[format];
       
 17215 
       
 17216 							each(callbacks, function(callback) {
       
 17217 								callback(false, {node: e.element, format: format, parents: parents});
       
 17218 							});
       
 17219 						}
       
 17220 					});
       
 17221 				});
       
 17222 			}
       
 17223 
       
 17224 			// Add format listeners
       
 17225 			each(formats.split(','), function(format) {
       
 17226 				if (!formatChangeData[format]) {
       
 17227 					formatChangeData[format] = [];
       
 17228 					formatChangeData[format].similar = similar;
       
 17229 				}
       
 17230 
       
 17231 				formatChangeData[format].push(callback);
       
 17232 			});
       
 17233 
       
 17234 			return this;
       
 17235 		}
       
 17236 
       
 17237 		/**
       
 17238 		 * Returns a preview css text for the specified format.
       
 17239 		 *
       
 17240 		 * @method getCssText
       
 17241 		 * @param {String/Object} format Format to generate preview css text for.
       
 17242 		 * @return {String} Css text for the specified format.
       
 17243 		 * @example
       
 17244 		 * var cssText1 = editor.formatter.getCssText('bold');
       
 17245 		 * var cssText2 = editor.formatter.getCssText({inline: 'b'});
       
 17246 		 */
       
 17247 		function getCssText(format) {
       
 17248 			return Preview.getCssText(ed, format);
       
 17249 		}
       
 17250 
       
 17251 		// Expose to public
       
 17252 		extend(this, {
       
 17253 			get: get,
       
 17254 			register: register,
       
 17255 			unregister: unregister,
       
 17256 			apply: apply,
       
 17257 			remove: remove,
       
 17258 			toggle: toggle,
       
 17259 			match: match,
       
 17260 			matchAll: matchAll,
       
 17261 			matchNode: matchNode,
       
 17262 			canApply: canApply,
       
 17263 			formatChanged: formatChanged,
       
 17264 			getCssText: getCssText
       
 17265 		});
       
 17266 
       
 17267 		// Initialize
       
 17268 		defaultFormats();
       
 17269 		addKeyboardShortcuts();
       
 17270 		ed.on('BeforeGetContent', function(e) {
       
 17271 			if (markCaretContainersBogus && e.format != 'raw') {
       
 17272 				markCaretContainersBogus();
       
 17273 			}
       
 17274 		});
       
 17275 		ed.on('mouseup keydown', function(e) {
       
 17276 			if (disableCaretContainer) {
       
 17277 				disableCaretContainer(e);
       
 17278 			}
       
 17279 		});
       
 17280 
       
 17281 		// Private functions
       
 17282 
       
 17283 		/**
       
 17284 		 * Checks if the specified nodes name matches the format inline/block or selector.
       
 17285 		 *
       
 17286 		 * @private
       
 17287 		 * @param {Node} node Node to match against the specified format.
       
 17288 		 * @param {Object} format Format object o match with.
       
 17289 		 * @return {boolean} true/false if the format matches.
       
 17290 		 */
       
 17291 		function matchName(node, format) {
       
 17292 			// Check for inline match
       
 17293 			if (isEq(node, format.inline)) {
       
 17294 				return TRUE;
       
 17295 			}
       
 17296 
       
 17297 			// Check for block match
       
 17298 			if (isEq(node, format.block)) {
       
 17299 				return TRUE;
       
 17300 			}
       
 17301 
       
 17302 			// Check for selector match
       
 17303 			if (format.selector) {
       
 17304 				return node.nodeType == 1 && dom.is(node, format.selector);
       
 17305 			}
       
 17306 		}
       
 17307 
       
 17308 		/**
       
 17309 		 * Compares two string/nodes regardless of their case.
       
 17310 		 *
       
 17311 		 * @private
       
 17312 		 * @param {String/Node} Node or string to compare.
       
 17313 		 * @param {String/Node} Node or string to compare.
       
 17314 		 * @return {boolean} True/false if they match.
       
 17315 		 */
       
 17316 		function isEq(str1, str2) {
       
 17317 			str1 = str1 || '';
       
 17318 			str2 = str2 || '';
       
 17319 
       
 17320 			str1 = '' + (str1.nodeName || str1);
       
 17321 			str2 = '' + (str2.nodeName || str2);
       
 17322 
       
 17323 			return str1.toLowerCase() == str2.toLowerCase();
       
 17324 		}
       
 17325 
       
 17326 		/**
       
 17327 		 * Returns the style by name on the specified node. This method modifies the style
       
 17328 		 * contents to make it more easy to match. This will resolve a few browser issues.
       
 17329 		 *
       
 17330 		 * @private
       
 17331 		 * @param {Node} node to get style from.
       
 17332 		 * @param {String} name Style name to get.
       
 17333 		 * @return {String} Style item value.
       
 17334 		 */
       
 17335 		function getStyle(node, name) {
       
 17336 			return normalizeStyleValue(dom.getStyle(node, name), name);
       
 17337 		}
       
 17338 
       
 17339 		/**
       
 17340 		 * Normalize style value by name. This method modifies the style contents
       
 17341 		 * to make it more easy to match. This will resolve a few browser issues.
       
 17342 		 *
       
 17343 		 * @private
       
 17344 		 * @param {Node} node to get style from.
       
 17345 		 * @param {String} name Style name to get.
       
 17346 		 * @return {String} Style item value.
       
 17347 		 */
       
 17348 		function normalizeStyleValue(value, name) {
       
 17349 			// Force the format to hex
       
 17350 			if (name == 'color' || name == 'backgroundColor') {
       
 17351 				value = dom.toHex(value);
       
 17352 			}
       
 17353 
       
 17354 			// Opera will return bold as 700
       
 17355 			if (name == 'fontWeight' && value == 700) {
       
 17356 				value = 'bold';
       
 17357 			}
       
 17358 
       
 17359 			// Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font"
       
 17360 			if (name == 'fontFamily') {
       
 17361 				value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
       
 17362 			}
       
 17363 
       
 17364 			return '' + value;
       
 17365 		}
       
 17366 
       
 17367 		/**
       
 17368 		 * Replaces variables in the value. The variable format is %var.
       
 17369 		 *
       
 17370 		 * @private
       
 17371 		 * @param {String} value Value to replace variables in.
       
 17372 		 * @param {Object} vars Name/value array with variables to replace.
       
 17373 		 * @return {String} New value with replaced variables.
       
 17374 		 */
       
 17375 		function replaceVars(value, vars) {
       
 17376 			if (typeof value != "string") {
       
 17377 				value = value(vars);
       
 17378 			} else if (vars) {
       
 17379 				value = value.replace(/%(\w+)/g, function(str, name) {
       
 17380 					return vars[name] || str;
       
 17381 				});
       
 17382 			}
       
 17383 
       
 17384 			return value;
       
 17385 		}
       
 17386 
       
 17387 		function isWhiteSpaceNode(node) {
       
 17388 			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
       
 17389 		}
       
 17390 
       
 17391 		function wrap(node, name, attrs) {
       
 17392 			var wrapper = dom.create(name, attrs);
       
 17393 
       
 17394 			node.parentNode.insertBefore(wrapper, node);
       
 17395 			wrapper.appendChild(node);
       
 17396 
       
 17397 			return wrapper;
       
 17398 		}
       
 17399 
       
 17400 		/**
       
 17401 		 * Expands the specified range like object to depending on format.
       
 17402 		 *
       
 17403 		 * For example on block formats it will move the start/end position
       
 17404 		 * to the beginning of the current block.
       
 17405 		 *
       
 17406 		 * @private
       
 17407 		 * @param {Object} rng Range like object.
       
 17408 		 * @param {Array} formats Array with formats to expand by.
       
 17409 		 * @return {Object} Expanded range like object.
       
 17410 		 */
       
 17411 		function expandRng(rng, format, remove) {
       
 17412 			var lastIdx, leaf, endPoint,
       
 17413 				startContainer = rng.startContainer,
       
 17414 				startOffset = rng.startOffset,
       
 17415 				endContainer = rng.endContainer,
       
 17416 				endOffset = rng.endOffset;
       
 17417 
       
 17418 			// This function walks up the tree if there is no siblings before/after the node
       
 17419 			function findParentContainer(start) {
       
 17420 				var container, parent, sibling, siblingName, root;
       
 17421 
       
 17422 				container = parent = start ? startContainer : endContainer;
       
 17423 				siblingName = start ? 'previousSibling' : 'nextSibling';
       
 17424 				root = dom.getRoot();
       
 17425 
       
 17426 				function isBogusBr(node) {
       
 17427 					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
       
 17428 				}
       
 17429 
       
 17430 				// If it's a text node and the offset is inside the text
       
 17431 				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
       
 17432 					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
       
 17433 						return container;
       
 17434 					}
       
 17435 				}
       
 17436 
       
 17437 				/*eslint no-constant-condition:0 */
       
 17438 				while (true) {
       
 17439 					// Stop expanding on block elements
       
 17440 					if (!format[0].block_expand && isBlock(parent)) {
       
 17441 						return parent;
       
 17442 					}
       
 17443 
       
 17444 					// Walk left/right
       
 17445 					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
       
 17446 						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
       
 17447 							return parent;
       
 17448 						}
       
 17449 					}
       
 17450 
       
 17451 					// Check if we can move up are we at root level or body level
       
 17452 					if (parent.parentNode == root) {
       
 17453 						container = parent;
       
 17454 						break;
       
 17455 					}
       
 17456 
       
 17457 					parent = parent.parentNode;
       
 17458 				}
       
 17459 
       
 17460 				return container;
       
 17461 			}
       
 17462 
       
 17463 			// This function walks down the tree to find the leaf at the selection.
       
 17464 			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
       
 17465 			function findLeaf(node, offset) {
       
 17466 				if (offset === undef) {
       
 17467 					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
       
 17468 				}
       
 17469 
       
 17470 				while (node && node.hasChildNodes()) {
       
 17471 					node = node.childNodes[offset];
       
 17472 					if (node) {
       
 17473 						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
       
 17474 					}
       
 17475 				}
       
 17476 				return {node: node, offset: offset};
       
 17477 			}
       
 17478 
       
 17479 			// If index based start position then resolve it
       
 17480 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
       
 17481 				lastIdx = startContainer.childNodes.length - 1;
       
 17482 				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
       
 17483 
       
 17484 				if (startContainer.nodeType == 3) {
       
 17485 					startOffset = 0;
       
 17486 				}
       
 17487 			}
       
 17488 
       
 17489 			// If index based end position then resolve it
       
 17490 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
       
 17491 				lastIdx = endContainer.childNodes.length - 1;
       
 17492 				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
       
 17493 
       
 17494 				if (endContainer.nodeType == 3) {
       
 17495 					endOffset = endContainer.nodeValue.length;
       
 17496 				}
       
 17497 			}
       
 17498 
       
 17499 			// Expands the node to the closes contentEditable false element if it exists
       
 17500 			function findParentContentEditable(node) {
       
 17501 				var parent = node;
       
 17502 
       
 17503 				while (parent) {
       
 17504 					if (parent.nodeType === 1 && getContentEditable(parent)) {
       
 17505 						return getContentEditable(parent) === "false" ? parent : node;
       
 17506 					}
       
 17507 
       
 17508 					parent = parent.parentNode;
       
 17509 				}
       
 17510 
       
 17511 				return node;
       
 17512 			}
       
 17513 
       
 17514 			function findWordEndPoint(container, offset, start) {
       
 17515 				var walker, node, pos, lastTextNode;
       
 17516 
       
 17517 				function findSpace(node, offset) {
       
 17518 					var pos, pos2, str = node.nodeValue;
       
 17519 
       
 17520 					if (typeof offset == "undefined") {
       
 17521 						offset = start ? str.length : 0;
       
 17522 					}
       
 17523 
       
 17524 					if (start) {
       
 17525 						pos = str.lastIndexOf(' ', offset);
       
 17526 						pos2 = str.lastIndexOf('\u00a0', offset);
       
 17527 						pos = pos > pos2 ? pos : pos2;
       
 17528 
       
 17529 						// Include the space on remove to avoid tag soup
       
 17530 						if (pos !== -1 && !remove) {
       
 17531 							pos++;
       
 17532 						}
       
 17533 					} else {
       
 17534 						pos = str.indexOf(' ', offset);
       
 17535 						pos2 = str.indexOf('\u00a0', offset);
       
 17536 						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
       
 17537 					}
       
 17538 
       
 17539 					return pos;
       
 17540 				}
       
 17541 
       
 17542 				if (container.nodeType === 3) {
       
 17543 					pos = findSpace(container, offset);
       
 17544 
       
 17545 					if (pos !== -1) {
       
 17546 						return {container: container, offset: pos};
       
 17547 					}
       
 17548 
       
 17549 					lastTextNode = container;
       
 17550 				}
       
 17551 
       
 17552 				// Walk the nodes inside the block
       
 17553 				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
       
 17554 				while ((node = walker[start ? 'prev' : 'next']())) {
       
 17555 					if (node.nodeType === 3) {
       
 17556 						lastTextNode = node;
       
 17557 						pos = findSpace(node);
       
 17558 
       
 17559 						if (pos !== -1) {
       
 17560 							return {container: node, offset: pos};
       
 17561 						}
       
 17562 					} else if (isBlock(node)) {
       
 17563 						break;
       
 17564 					}
       
 17565 				}
       
 17566 
       
 17567 				if (lastTextNode) {
       
 17568 					if (start) {
       
 17569 						offset = 0;
       
 17570 					} else {
       
 17571 						offset = lastTextNode.length;
       
 17572 					}
       
 17573 
       
 17574 					return {container: lastTextNode, offset: offset};
       
 17575 				}
       
 17576 			}
       
 17577 
       
 17578 			function findSelectorEndPoint(container, sibling_name) {
       
 17579 				var parents, i, y, curFormat;
       
 17580 
       
 17581 				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name]) {
       
 17582 					container = container[sibling_name];
       
 17583 				}
       
 17584 
       
 17585 				parents = getParents(container);
       
 17586 				for (i = 0; i < parents.length; i++) {
       
 17587 					for (y = 0; y < format.length; y++) {
       
 17588 						curFormat = format[y];
       
 17589 
       
 17590 						// If collapsed state is set then skip formats that doesn't match that
       
 17591 						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) {
       
 17592 							continue;
       
 17593 						}
       
 17594 
       
 17595 						if (dom.is(parents[i], curFormat.selector)) {
       
 17596 							return parents[i];
       
 17597 						}
       
 17598 					}
       
 17599 				}
       
 17600 
       
 17601 				return container;
       
 17602 			}
       
 17603 
       
 17604 			function findBlockEndPoint(container, sibling_name) {
       
 17605 				var node, root = dom.getRoot();
       
 17606 
       
 17607 				// Expand to block of similar type
       
 17608 				if (!format[0].wrapper) {
       
 17609 					node = dom.getParent(container, format[0].block, root);
       
 17610 				}
       
 17611 
       
 17612 				// Expand to first wrappable block element or any block element
       
 17613 				if (!node) {
       
 17614 					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
       
 17615 						// Fixes #6183 where it would expand to editable parent element in inline mode
       
 17616 						return node != root && isTextBlock(node);
       
 17617 					});
       
 17618 				}
       
 17619 
       
 17620 				// Exclude inner lists from wrapping
       
 17621 				if (node && format[0].wrapper) {
       
 17622 					node = getParents(node, 'ul,ol').reverse()[0] || node;
       
 17623 				}
       
 17624 
       
 17625 				// Didn't find a block element look for first/last wrappable element
       
 17626 				if (!node) {
       
 17627 					node = container;
       
 17628 
       
 17629 					while (node[sibling_name] && !isBlock(node[sibling_name])) {
       
 17630 						node = node[sibling_name];
       
 17631 
       
 17632 						// Break on BR but include it will be removed later on
       
 17633 						// we can't remove it now since we need to check if it can be wrapped
       
 17634 						if (isEq(node, 'br')) {
       
 17635 							break;
       
 17636 						}
       
 17637 					}
       
 17638 				}
       
 17639 
       
 17640 				return node || container;
       
 17641 			}
       
 17642 
       
 17643 			// Expand to closest contentEditable element
       
 17644 			startContainer = findParentContentEditable(startContainer);
       
 17645 			endContainer = findParentContentEditable(endContainer);
       
 17646 
       
 17647 			// Exclude bookmark nodes if possible
       
 17648 			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
       
 17649 				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
       
 17650 				startContainer = startContainer.nextSibling || startContainer;
       
 17651 
       
 17652 				if (startContainer.nodeType == 3) {
       
 17653 					startOffset = 0;
       
 17654 				}
       
 17655 			}
       
 17656 
       
 17657 			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
       
 17658 				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
       
 17659 				endContainer = endContainer.previousSibling || endContainer;
       
 17660 
       
 17661 				if (endContainer.nodeType == 3) {
       
 17662 					endOffset = endContainer.length;
       
 17663 				}
       
 17664 			}
       
 17665 
       
 17666 			if (format[0].inline) {
       
 17667 				if (rng.collapsed) {
       
 17668 					// Expand left to closest word boundary
       
 17669 					endPoint = findWordEndPoint(startContainer, startOffset, true);
       
 17670 					if (endPoint) {
       
 17671 						startContainer = endPoint.container;
       
 17672 						startOffset = endPoint.offset;
       
 17673 					}
       
 17674 
       
 17675 					// Expand right to closest word boundary
       
 17676 					endPoint = findWordEndPoint(endContainer, endOffset);
       
 17677 					if (endPoint) {
       
 17678 						endContainer = endPoint.container;
       
 17679 						endOffset = endPoint.offset;
       
 17680 					}
       
 17681 				}
       
 17682 
       
 17683 				// Avoid applying formatting to a trailing space.
       
 17684 				leaf = findLeaf(endContainer, endOffset);
       
 17685 				if (leaf.node) {
       
 17686 					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) {
       
 17687 						leaf = findLeaf(leaf.node.previousSibling);
       
 17688 					}
       
 17689 
       
 17690 					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
       
 17691 							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
       
 17692 
       
 17693 						if (leaf.offset > 1) {
       
 17694 							endContainer = leaf.node;
       
 17695 							endContainer.splitText(leaf.offset - 1);
       
 17696 						}
       
 17697 					}
       
 17698 				}
       
 17699 			}
       
 17700 
       
 17701 			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
       
 17702 			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
       
 17703 			// This will reduce the number of wrapper elements that needs to be created
       
 17704 			// Move start point up the tree
       
 17705 			if (format[0].inline || format[0].block_expand) {
       
 17706 				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
       
 17707 					startContainer = findParentContainer(true);
       
 17708 				}
       
 17709 
       
 17710 				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
       
 17711 					endContainer = findParentContainer();
       
 17712 				}
       
 17713 			}
       
 17714 
       
 17715 			// Expand start/end container to matching selector
       
 17716 			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
       
 17717 				// Find new startContainer/endContainer if there is better one
       
 17718 				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
       
 17719 				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
       
 17720 			}
       
 17721 
       
 17722 			// Expand start/end container to matching block element or text node
       
 17723 			if (format[0].block || format[0].selector) {
       
 17724 				// Find new startContainer/endContainer if there is better one
       
 17725 				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
       
 17726 				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
       
 17727 
       
 17728 				// Non block element then try to expand up the leaf
       
 17729 				if (format[0].block) {
       
 17730 					if (!isBlock(startContainer)) {
       
 17731 						startContainer = findParentContainer(true);
       
 17732 					}
       
 17733 
       
 17734 					if (!isBlock(endContainer)) {
       
 17735 						endContainer = findParentContainer();
       
 17736 					}
       
 17737 				}
       
 17738 			}
       
 17739 
       
 17740 			// Setup index for startContainer
       
 17741 			if (startContainer.nodeType == 1) {
       
 17742 				startOffset = nodeIndex(startContainer);
       
 17743 				startContainer = startContainer.parentNode;
       
 17744 			}
       
 17745 
       
 17746 			// Setup index for endContainer
       
 17747 			if (endContainer.nodeType == 1) {
       
 17748 				endOffset = nodeIndex(endContainer) + 1;
       
 17749 				endContainer = endContainer.parentNode;
       
 17750 			}
       
 17751 
       
 17752 			// Return new range like object
       
 17753 			return {
       
 17754 				startContainer: startContainer,
       
 17755 				startOffset: startOffset,
       
 17756 				endContainer: endContainer,
       
 17757 				endOffset: endOffset
       
 17758 			};
       
 17759 		}
       
 17760 
       
 17761 		function isColorFormatAndAnchor(node, format) {
       
 17762 			return format.links && node.tagName == 'A';
       
 17763 		}
       
 17764 
       
 17765 		/**
       
 17766 		 * Removes the specified format for the specified node. It will also remove the node if it doesn't have
       
 17767 		 * any attributes if the format specifies it to do so.
       
 17768 		 *
       
 17769 		 * @private
       
 17770 		 * @param {Object} format Format object with items to remove from node.
       
 17771 		 * @param {Object} vars Name/value object with variables to apply to format.
       
 17772 		 * @param {Node} node Node to remove the format styles on.
       
 17773 		 * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
       
 17774 		 * @return {Boolean} True/false if the node was removed or not.
       
 17775 		 */
       
 17776 		function removeFormat(format, vars, node, compare_node) {
       
 17777 			var i, attrs, stylesModified;
       
 17778 
       
 17779 			// Check if node matches format
       
 17780 			if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) {
       
 17781 				return FALSE;
       
 17782 			}
       
 17783 
       
 17784 			// Should we compare with format attribs and styles
       
 17785 			if (format.remove != 'all') {
       
 17786 				// Remove styles
       
 17787 				each(format.styles, function(value, name) {
       
 17788 					value = normalizeStyleValue(replaceVars(value, vars), name);
       
 17789 
       
 17790 					// Indexed array
       
 17791 					if (typeof name === 'number') {
       
 17792 						name = value;
       
 17793 						compare_node = 0;
       
 17794 					}
       
 17795 
       
 17796 					if (format.remove_similar || (!compare_node || isEq(getStyle(compare_node, name), value))) {
       
 17797 						dom.setStyle(node, name, '');
       
 17798 					}
       
 17799 
       
 17800 					stylesModified = 1;
       
 17801 				});
       
 17802 
       
 17803 				// Remove style attribute if it's empty
       
 17804 				if (stylesModified && dom.getAttrib(node, 'style') === '') {
       
 17805 					node.removeAttribute('style');
       
 17806 					node.removeAttribute('data-mce-style');
       
 17807 				}
       
 17808 
       
 17809 				// Remove attributes
       
 17810 				each(format.attributes, function(value, name) {
       
 17811 					var valueOut;
       
 17812 
       
 17813 					value = replaceVars(value, vars);
       
 17814 
       
 17815 					// Indexed array
       
 17816 					if (typeof name === 'number') {
       
 17817 						name = value;
       
 17818 						compare_node = 0;
       
 17819 					}
       
 17820 
       
 17821 					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
       
 17822 						// Keep internal classes
       
 17823 						if (name == 'class') {
       
 17824 							value = dom.getAttrib(node, name);
       
 17825 							if (value) {
       
 17826 								// Build new class value where everything is removed except the internal prefixed classes
       
 17827 								valueOut = '';
       
 17828 								each(value.split(/\s+/), function(cls) {
       
 17829 									if (/mce\-\w+/.test(cls)) {
       
 17830 										valueOut += (valueOut ? ' ' : '') + cls;
       
 17831 									}
       
 17832 								});
       
 17833 
       
 17834 								// We got some internal classes left
       
 17835 								if (valueOut) {
       
 17836 									dom.setAttrib(node, name, valueOut);
       
 17837 									return;
       
 17838 								}
       
 17839 							}
       
 17840 						}
       
 17841 
       
 17842 						// IE6 has a bug where the attribute doesn't get removed correctly
       
 17843 						if (name == "class") {
       
 17844 							node.removeAttribute('className');
       
 17845 						}
       
 17846 
       
 17847 						// Remove mce prefixed attributes
       
 17848 						if (MCE_ATTR_RE.test(name)) {
       
 17849 							node.removeAttribute('data-mce-' + name);
       
 17850 						}
       
 17851 
       
 17852 						node.removeAttribute(name);
       
 17853 					}
       
 17854 				});
       
 17855 
       
 17856 				// Remove classes
       
 17857 				each(format.classes, function(value) {
       
 17858 					value = replaceVars(value, vars);
       
 17859 
       
 17860 					if (!compare_node || dom.hasClass(compare_node, value)) {
       
 17861 						dom.removeClass(node, value);
       
 17862 					}
       
 17863 				});
       
 17864 
       
 17865 				// Check for non internal attributes
       
 17866 				attrs = dom.getAttribs(node);
       
 17867 				for (i = 0; i < attrs.length; i++) {
       
 17868 					if (attrs[i].nodeName.indexOf('_') !== 0) {
       
 17869 						return FALSE;
       
 17870 					}
       
 17871 				}
       
 17872 			}
       
 17873 
       
 17874 			// Remove the inline child if it's empty for example <b> or <span>
       
 17875 			if (format.remove != 'none') {
       
 17876 				removeNode(node, format);
       
 17877 				return TRUE;
       
 17878 			}
       
 17879 		}
       
 17880 
       
 17881 		/**
       
 17882 		 * Removes the node and wrap it's children in paragraphs before doing so or
       
 17883 		 * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled.
       
 17884 		 *
       
 17885 		 * If the div in the node below gets removed:
       
 17886 		 *  text<div>text</div>text
       
 17887 		 *
       
 17888 		 * Output becomes:
       
 17889 		 *  text<div><br />text<br /></div>text
       
 17890 		 *
       
 17891 		 * So when the div is removed the result is:
       
 17892 		 *  text<br />text<br />text
       
 17893 		 *
       
 17894 		 * @private
       
 17895 		 * @param {Node} node Node to remove + apply BR/P elements to.
       
 17896 		 * @param {Object} format Format rule.
       
 17897 		 * @return {Node} Input node.
       
 17898 		 */
       
 17899 		function removeNode(node, format) {
       
 17900 			var parentNode = node.parentNode, rootBlockElm;
       
 17901 
       
 17902 			function find(node, next, inc) {
       
 17903 				node = getNonWhiteSpaceSibling(node, next, inc);
       
 17904 
       
 17905 				return !node || (node.nodeName == 'BR' || isBlock(node));
       
 17906 			}
       
 17907 
       
 17908 			if (format.block) {
       
 17909 				if (!forcedRootBlock) {
       
 17910 					// Append BR elements if needed before we remove the block
       
 17911 					if (isBlock(node) && !isBlock(parentNode)) {
       
 17912 						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) {
       
 17913 							node.insertBefore(dom.create('br'), node.firstChild);
       
 17914 						}
       
 17915 
       
 17916 						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) {
       
 17917 							node.appendChild(dom.create('br'));
       
 17918 						}
       
 17919 					}
       
 17920 				} else {
       
 17921 					// Wrap the block in a forcedRootBlock if we are at the root of document
       
 17922 					if (parentNode == dom.getRoot()) {
       
 17923 						if (!format.list_block || !isEq(node, format.list_block)) {
       
 17924 							each(grep(node.childNodes), function(node) {
       
 17925 								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
       
 17926 									if (!rootBlockElm) {
       
 17927 										rootBlockElm = wrap(node, forcedRootBlock);
       
 17928 										dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs);
       
 17929 									} else {
       
 17930 										rootBlockElm.appendChild(node);
       
 17931 									}
       
 17932 								} else {
       
 17933 									rootBlockElm = 0;
       
 17934 								}
       
 17935 							});
       
 17936 						}
       
 17937 					}
       
 17938 				}
       
 17939 			}
       
 17940 
       
 17941 			// Never remove nodes that isn't the specified inline element if a selector is specified too
       
 17942 			if (format.selector && format.inline && !isEq(format.inline, node)) {
       
 17943 				return;
       
 17944 			}
       
 17945 
       
 17946 			dom.remove(node, 1);
       
 17947 		}
       
 17948 
       
 17949 		/**
       
 17950 		 * Returns the next/previous non whitespace node.
       
 17951 		 *
       
 17952 		 * @private
       
 17953 		 * @param {Node} node Node to start at.
       
 17954 		 * @param {boolean} next (Optional) Include next or previous node defaults to previous.
       
 17955 		 * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false.
       
 17956 		 * @return {Node} Next or previous node or undefined if it wasn't found.
       
 17957 		 */
       
 17958 		function getNonWhiteSpaceSibling(node, next, inc) {
       
 17959 			if (node) {
       
 17960 				next = next ? 'nextSibling' : 'previousSibling';
       
 17961 
       
 17962 				for (node = inc ? node : node[next]; node; node = node[next]) {
       
 17963 					if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
       
 17964 						return node;
       
 17965 					}
       
 17966 				}
       
 17967 			}
       
 17968 		}
       
 17969 
       
 17970 		/**
       
 17971 		 * Merges the next/previous sibling element if they match.
       
 17972 		 *
       
 17973 		 * @private
       
 17974 		 * @param {Node} prev Previous node to compare/merge.
       
 17975 		 * @param {Node} next Next node to compare/merge.
       
 17976 		 * @return {Node} Next node if we didn't merge and prev node if we did.
       
 17977 		 */
       
 17978 		function mergeSiblings(prev, next) {
       
 17979 			var sibling, tmpSibling, elementUtils = new ElementUtils(dom);
       
 17980 
       
 17981 			function findElementSibling(node, sibling_name) {
       
 17982 				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
       
 17983 					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) {
       
 17984 						return node;
       
 17985 					}
       
 17986 
       
 17987 					if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) {
       
 17988 						return sibling;
       
 17989 					}
       
 17990 				}
       
 17991 
       
 17992 				return node;
       
 17993 			}
       
 17994 
       
 17995 			// Check if next/prev exists and that they are elements
       
 17996 			if (prev && next) {
       
 17997 				// If previous sibling is empty then jump over it
       
 17998 				prev = findElementSibling(prev, 'previousSibling');
       
 17999 				next = findElementSibling(next, 'nextSibling');
       
 18000 
       
 18001 				// Compare next and previous nodes
       
 18002 				if (elementUtils.compare(prev, next)) {
       
 18003 					// Append nodes between
       
 18004 					for (sibling = prev.nextSibling; sibling && sibling != next;) {
       
 18005 						tmpSibling = sibling;
       
 18006 						sibling = sibling.nextSibling;
       
 18007 						prev.appendChild(tmpSibling);
       
 18008 					}
       
 18009 
       
 18010 					// Remove next node
       
 18011 					dom.remove(next);
       
 18012 
       
 18013 					// Move children into prev node
       
 18014 					each(grep(next.childNodes), function(node) {
       
 18015 						prev.appendChild(node);
       
 18016 					});
       
 18017 
       
 18018 					return prev;
       
 18019 				}
       
 18020 			}
       
 18021 
       
 18022 			return next;
       
 18023 		}
       
 18024 
       
 18025 		function getContainer(rng, start) {
       
 18026 			var container, offset, lastIdx;
       
 18027 
       
 18028 			container = rng[start ? 'startContainer' : 'endContainer'];
       
 18029 			offset = rng[start ? 'startOffset' : 'endOffset'];
       
 18030 
       
 18031 			if (container.nodeType == 1) {
       
 18032 				lastIdx = container.childNodes.length - 1;
       
 18033 
       
 18034 				if (!start && offset) {
       
 18035 					offset--;
       
 18036 				}
       
 18037 
       
 18038 				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
       
 18039 			}
       
 18040 
       
 18041 			// If start text node is excluded then walk to the next node
       
 18042 			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
       
 18043 				container = new TreeWalker(container, ed.getBody()).next() || container;
       
 18044 			}
       
 18045 
       
 18046 			// If end text node is excluded then walk to the previous node
       
 18047 			if (container.nodeType === 3 && !start && offset === 0) {
       
 18048 				container = new TreeWalker(container, ed.getBody()).prev() || container;
       
 18049 			}
       
 18050 
       
 18051 			return container;
       
 18052 		}
       
 18053 
       
 18054 		function performCaretAction(type, name, vars, similar) {
       
 18055 			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
       
 18056 
       
 18057 			// Creates a caret container bogus element
       
 18058 			function createCaretContainer(fill) {
       
 18059 				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
       
 18060 
       
 18061 				if (fill) {
       
 18062 					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
       
 18063 				}
       
 18064 
       
 18065 				return caretContainer;
       
 18066 			}
       
 18067 
       
 18068 			function isCaretContainerEmpty(node, nodes) {
       
 18069 				while (node) {
       
 18070 					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
       
 18071 						return false;
       
 18072 					}
       
 18073 
       
 18074 					// Collect nodes
       
 18075 					if (nodes && node.nodeType === 1) {
       
 18076 						nodes.push(node);
       
 18077 					}
       
 18078 
       
 18079 					node = node.firstChild;
       
 18080 				}
       
 18081 
       
 18082 				return true;
       
 18083 			}
       
 18084 
       
 18085 			// Returns any parent caret container element
       
 18086 			function getParentCaretContainer(node) {
       
 18087 				while (node) {
       
 18088 					if (node.id === caretContainerId) {
       
 18089 						return node;
       
 18090 					}
       
 18091 
       
 18092 					node = node.parentNode;
       
 18093 				}
       
 18094 			}
       
 18095 
       
 18096 			// Finds the first text node in the specified node
       
 18097 			function findFirstTextNode(node) {
       
 18098 				var walker;
       
 18099 
       
 18100 				if (node) {
       
 18101 					walker = new TreeWalker(node, node);
       
 18102 
       
 18103 					for (node = walker.current(); node; node = walker.next()) {
       
 18104 						if (node.nodeType === 3) {
       
 18105 							return node;
       
 18106 						}
       
 18107 					}
       
 18108 				}
       
 18109 			}
       
 18110 
       
 18111 			// Removes the caret container for the specified node or all on the current document
       
 18112 			function removeCaretContainer(node, move_caret) {
       
 18113 				var child, rng;
       
 18114 
       
 18115 				if (!node) {
       
 18116 					node = getParentCaretContainer(selection.getStart());
       
 18117 
       
 18118 					if (!node) {
       
 18119 						while ((node = dom.get(caretContainerId))) {
       
 18120 							removeCaretContainer(node, false);
       
 18121 						}
       
 18122 					}
       
 18123 				} else {
       
 18124 					rng = selection.getRng(true);
       
 18125 
       
 18126 					if (isCaretContainerEmpty(node)) {
       
 18127 						if (move_caret !== false) {
       
 18128 							rng.setStartBefore(node);
       
 18129 							rng.setEndBefore(node);
       
 18130 						}
       
 18131 
       
 18132 						dom.remove(node);
       
 18133 					} else {
       
 18134 						child = findFirstTextNode(node);
       
 18135 
       
 18136 						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
       
 18137 							child.deleteData(0, 1);
       
 18138 
       
 18139 							// Fix for bug #6976
       
 18140 							if (rng.startContainer == child && rng.startOffset > 0) {
       
 18141 								rng.setStart(child, rng.startOffset - 1);
       
 18142 							}
       
 18143 
       
 18144 							if (rng.endContainer == child && rng.endOffset > 0) {
       
 18145 								rng.setEnd(child, rng.endOffset - 1);
       
 18146 							}
       
 18147 						}
       
 18148 
       
 18149 						dom.remove(node, 1);
       
 18150 					}
       
 18151 
       
 18152 					selection.setRng(rng);
       
 18153 				}
       
 18154 			}
       
 18155 
       
 18156 			// Applies formatting to the caret postion
       
 18157 			function applyCaretFormat() {
       
 18158 				var rng, caretContainer, textNode, offset, bookmark, container, text;
       
 18159 
       
 18160 				rng = selection.getRng(true);
       
 18161 				offset = rng.startOffset;
       
 18162 				container = rng.startContainer;
       
 18163 				text = container.nodeValue;
       
 18164 
       
 18165 				caretContainer = getParentCaretContainer(selection.getStart());
       
 18166 				if (caretContainer) {
       
 18167 					textNode = findFirstTextNode(caretContainer);
       
 18168 				}
       
 18169 
       
 18170 				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
       
 18171 				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
       
 18172 					// Get bookmark of caret position
       
 18173 					bookmark = selection.getBookmark();
       
 18174 
       
 18175 					// Collapse bookmark range (WebKit)
       
 18176 					rng.collapse(true);
       
 18177 
       
 18178 					// Expand the range to the closest word and split it at those points
       
 18179 					rng = expandRng(rng, get(name));
       
 18180 					rng = rangeUtils.split(rng);
       
 18181 
       
 18182 					// Apply the format to the range
       
 18183 					apply(name, vars, rng);
       
 18184 
       
 18185 					// Move selection back to caret position
       
 18186 					selection.moveToBookmark(bookmark);
       
 18187 				} else {
       
 18188 					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
       
 18189 						caretContainer = createCaretContainer(true);
       
 18190 						textNode = caretContainer.firstChild;
       
 18191 
       
 18192 						rng.insertNode(caretContainer);
       
 18193 						offset = 1;
       
 18194 
       
 18195 						apply(name, vars, caretContainer);
       
 18196 					} else {
       
 18197 						apply(name, vars, caretContainer);
       
 18198 					}
       
 18199 
       
 18200 					// Move selection to text node
       
 18201 					selection.setCursorLocation(textNode, offset);
       
 18202 				}
       
 18203 			}
       
 18204 
       
 18205 			function removeCaretFormat() {
       
 18206 				var rng = selection.getRng(true), container, offset, bookmark,
       
 18207 					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
       
 18208 
       
 18209 				container = rng.startContainer;
       
 18210 				offset = rng.startOffset;
       
 18211 				node = container;
       
 18212 
       
 18213 				if (container.nodeType == 3) {
       
 18214 					if (offset != container.nodeValue.length) {
       
 18215 						hasContentAfter = true;
       
 18216 					}
       
 18217 
       
 18218 					node = node.parentNode;
       
 18219 				}
       
 18220 
       
 18221 				while (node) {
       
 18222 					if (matchNode(node, name, vars, similar)) {
       
 18223 						formatNode = node;
       
 18224 						break;
       
 18225 					}
       
 18226 
       
 18227 					if (node.nextSibling) {
       
 18228 						hasContentAfter = true;
       
 18229 					}
       
 18230 
       
 18231 					parents.push(node);
       
 18232 					node = node.parentNode;
       
 18233 				}
       
 18234 
       
 18235 				// Node doesn't have the specified format
       
 18236 				if (!formatNode) {
       
 18237 					return;
       
 18238 				}
       
 18239 
       
 18240 				// Is there contents after the caret then remove the format on the element
       
 18241 				if (hasContentAfter) {
       
 18242 					// Get bookmark of caret position
       
 18243 					bookmark = selection.getBookmark();
       
 18244 
       
 18245 					// Collapse bookmark range (WebKit)
       
 18246 					rng.collapse(true);
       
 18247 
       
 18248 					// Expand the range to the closest word and split it at those points
       
 18249 					rng = expandRng(rng, get(name), true);
       
 18250 					rng = rangeUtils.split(rng);
       
 18251 
       
 18252 					// Remove the format from the range
       
 18253 					remove(name, vars, rng);
       
 18254 
       
 18255 					// Move selection back to caret position
       
 18256 					selection.moveToBookmark(bookmark);
       
 18257 				} else {
       
 18258 					caretContainer = createCaretContainer();
       
 18259 
       
 18260 					node = caretContainer;
       
 18261 					for (i = parents.length - 1; i >= 0; i--) {
       
 18262 						node.appendChild(dom.clone(parents[i], false));
       
 18263 						node = node.firstChild;
       
 18264 					}
       
 18265 
       
 18266 					// Insert invisible character into inner most format element
       
 18267 					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
       
 18268 					node = node.firstChild;
       
 18269 
       
 18270 					var block = dom.getParent(formatNode, isTextBlock);
       
 18271 
       
 18272 					if (block && dom.isEmpty(block)) {
       
 18273 						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
       
 18274 						formatNode.parentNode.replaceChild(caretContainer, formatNode);
       
 18275 					} else {
       
 18276 						// Insert caret container after the formated node
       
 18277 						dom.insertAfter(caretContainer, formatNode);
       
 18278 					}
       
 18279 
       
 18280 					// Move selection to text node
       
 18281 					selection.setCursorLocation(node, 1);
       
 18282 
       
 18283 					// If the formatNode is empty, we can remove it safely.
       
 18284 					if (dom.isEmpty(formatNode)) {
       
 18285 						dom.remove(formatNode);
       
 18286 					}
       
 18287 				}
       
 18288 			}
       
 18289 
       
 18290 			// Checks if the parent caret container node isn't empty if that is the case it
       
 18291 			// will remove the bogus state on all children that isn't empty
       
 18292 			function unmarkBogusCaretParents() {
       
 18293 				var caretContainer;
       
 18294 
       
 18295 				caretContainer = getParentCaretContainer(selection.getStart());
       
 18296 				if (caretContainer && !dom.isEmpty(caretContainer)) {
       
 18297 					walk(caretContainer, function(node) {
       
 18298 						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
       
 18299 							dom.setAttrib(node, 'data-mce-bogus', null);
       
 18300 						}
       
 18301 					}, 'childNodes');
       
 18302 				}
       
 18303 			}
       
 18304 
       
 18305 			// Only bind the caret events once
       
 18306 			if (!ed._hasCaretEvents) {
       
 18307 				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
       
 18308 				markCaretContainersBogus = function() {
       
 18309 					var nodes = [], i;
       
 18310 
       
 18311 					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
       
 18312 						// Mark children
       
 18313 						i = nodes.length;
       
 18314 						while (i--) {
       
 18315 							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
       
 18316 						}
       
 18317 					}
       
 18318 				};
       
 18319 
       
 18320 				disableCaretContainer = function(e) {
       
 18321 					var keyCode = e.keyCode;
       
 18322 
       
 18323 					removeCaretContainer();
       
 18324 
       
 18325 					// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
       
 18326 					// Backspace key needs to check if the range is collapsed due to bug #6780
       
 18327 					if ((keyCode == 8 && selection.isCollapsed()) || keyCode == 37 || keyCode == 39) {
       
 18328 						removeCaretContainer(getParentCaretContainer(selection.getStart()));
       
 18329 					}
       
 18330 
       
 18331 					unmarkBogusCaretParents();
       
 18332 				};
       
 18333 
       
 18334 				// Remove bogus state if they got filled by contents using editor.selection.setContent
       
 18335 				ed.on('SetContent', function(e) {
       
 18336 					if (e.selection) {
       
 18337 						unmarkBogusCaretParents();
       
 18338 					}
       
 18339 				});
       
 18340 				ed._hasCaretEvents = true;
       
 18341 			}
       
 18342 
       
 18343 			// Do apply or remove caret format
       
 18344 			if (type == "apply") {
       
 18345 				applyCaretFormat();
       
 18346 			} else {
       
 18347 				removeCaretFormat();
       
 18348 			}
       
 18349 		}
       
 18350 
       
 18351 		/**
       
 18352 		 * Moves the start to the first suitable text node.
       
 18353 		 */
       
 18354 		function moveStart(rng) {
       
 18355 			var container = rng.startContainer,
       
 18356 					offset = rng.startOffset, isAtEndOfText,
       
 18357 					walker, node, nodes, tmpNode;
       
 18358 
       
 18359 			// Convert text node into index if possible
       
 18360 			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
       
 18361 				// Get the parent container location and walk from there
       
 18362 				offset = nodeIndex(container);
       
 18363 				container = container.parentNode;
       
 18364 				isAtEndOfText = true;
       
 18365 			}
       
 18366 
       
 18367 			// Move startContainer/startOffset in to a suitable node
       
 18368 			if (container.nodeType == 1) {
       
 18369 				nodes = container.childNodes;
       
 18370 				container = nodes[Math.min(offset, nodes.length - 1)];
       
 18371 				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
       
 18372 
       
 18373 				// If offset is at end of the parent node walk to the next one
       
 18374 				if (offset > nodes.length - 1 || isAtEndOfText) {
       
 18375 					walker.next();
       
 18376 				}
       
 18377 
       
 18378 				for (node = walker.current(); node; node = walker.next()) {
       
 18379 					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
       
 18380 						// IE has a "neat" feature where it moves the start node into the closest element
       
 18381 						// we can avoid this by inserting an element before it and then remove it after we set the selection
       
 18382 						tmpNode = dom.create('a', {'data-mce-bogus': 'all'}, INVISIBLE_CHAR);
       
 18383 						node.parentNode.insertBefore(tmpNode, node);
       
 18384 
       
 18385 						// Set selection and remove tmpNode
       
 18386 						rng.setStart(node, 0);
       
 18387 						selection.setRng(rng);
       
 18388 						dom.remove(tmpNode);
       
 18389 
       
 18390 						return;
       
 18391 					}
       
 18392 				}
       
 18393 			}
       
 18394 		}
       
 18395 	};
       
 18396 });
       
 18397 
       
 18398 // Included from: js/tinymce/classes/UndoManager.js
       
 18399 
       
 18400 /**
       
 18401  * UndoManager.js
       
 18402  *
       
 18403  * Copyright, Moxiecode Systems AB
       
 18404  * Released under LGPL License.
       
 18405  *
       
 18406  * License: http://www.tinymce.com/license
       
 18407  * Contributing: http://www.tinymce.com/contributing
       
 18408  */
       
 18409 
       
 18410 /**
       
 18411  * This class handles the undo/redo history levels for the editor. Since the build in undo/redo has major drawbacks a custom one was needed.
       
 18412  *
       
 18413  * @class tinymce.UndoManager
       
 18414  */
       
 18415 define("tinymce/UndoManager", [
       
 18416 	"tinymce/util/VK",
       
 18417 	"tinymce/Env",
       
 18418 	"tinymce/util/Tools",
       
 18419 	"tinymce/html/SaxParser"
       
 18420 ], function(VK, Env, Tools, SaxParser) {
       
 18421 	var trim = Tools.trim, trimContentRegExp;
       
 18422 
       
 18423 	trimContentRegExp = new RegExp([
       
 18424 		'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
       
 18425 		'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
       
 18426 	].join('|'), 'gi');
       
 18427 
       
 18428 	return function(editor) {
       
 18429 		var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
       
 18430 
       
 18431 		/**
       
 18432 		 * Returns a trimmed version of the editor contents to be used for the undo level. This
       
 18433 		 * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also
       
 18434 		 * remove the data-mce-selected attributes used for selection of objects and caret containers.
       
 18435 		 * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will
       
 18436 		 * be removed by the serialization logic when you save.
       
 18437 		 *
       
 18438 		 * @private
       
 18439 		 * @return {String} HTML contents of the editor excluding some internal bogus elements.
       
 18440 		 */
       
 18441 		function getContent() {
       
 18442 			var content = editor.getContent({format: 'raw', no_events: 1});
       
 18443 			var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g;
       
 18444 			var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema;
       
 18445 
       
 18446 			content = content.replace(trimContentRegExp, '');
       
 18447 			shortEndedElements = schema.getShortEndedElements();
       
 18448 
       
 18449 			// Remove all bogus elements marked with "all"
       
 18450 			while ((matches = bogusAllRegExp.exec(content))) {
       
 18451 				index = bogusAllRegExp.lastIndex;
       
 18452 				matchLength = matches[0].length;
       
 18453 
       
 18454 				if (shortEndedElements[matches[1]]) {
       
 18455 					endTagIndex = index;
       
 18456 				} else {
       
 18457 					endTagIndex = SaxParser.findEndTag(schema, content, index);
       
 18458 				}
       
 18459 
       
 18460 				content = content.substring(0, index - matchLength) + content.substring(endTagIndex);
       
 18461 				bogusAllRegExp.lastIndex = index - matchLength;
       
 18462 			}
       
 18463 
       
 18464 			return trim(content);
       
 18465 		}
       
 18466 
       
 18467 		function setDirty(state) {
       
 18468 			editor.isNotDirty = !state;
       
 18469 		}
       
 18470 
       
 18471 		function addNonTypingUndoLevel(e) {
       
 18472 			self.typing = false;
       
 18473 			self.add({}, e);
       
 18474 		}
       
 18475 
       
 18476 		// Add initial undo level when the editor is initialized
       
 18477 		editor.on('init', function() {
       
 18478 			self.add();
       
 18479 		});
       
 18480 
       
 18481 		// Get position before an execCommand is processed
       
 18482 		editor.on('BeforeExecCommand', function(e) {
       
 18483 			var cmd = e.command;
       
 18484 
       
 18485 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
       
 18486 				self.beforeChange();
       
 18487 			}
       
 18488 		});
       
 18489 
       
 18490 		// Add undo level after an execCommand call was made
       
 18491 		editor.on('ExecCommand', function(e) {
       
 18492 			var cmd = e.command;
       
 18493 
       
 18494 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
       
 18495 				addNonTypingUndoLevel(e);
       
 18496 			}
       
 18497 		});
       
 18498 
       
 18499 		editor.on('ObjectResizeStart', function() {
       
 18500 			self.beforeChange();
       
 18501 		});
       
 18502 
       
 18503 		editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel);
       
 18504 		editor.on('DragEnd', addNonTypingUndoLevel);
       
 18505 
       
 18506 		editor.on('KeyUp', function(e) {
       
 18507 			var keyCode = e.keyCode;
       
 18508 
       
 18509 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
       
 18510 				addNonTypingUndoLevel();
       
 18511 				editor.nodeChanged();
       
 18512 			}
       
 18513 
       
 18514 			if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
       
 18515 				editor.nodeChanged();
       
 18516 			}
       
 18517 
       
 18518 			// Fire a TypingUndo event on the first character entered
       
 18519 			if (isFirstTypedCharacter && self.typing) {
       
 18520 				// Make it dirty if the content was changed after typing the first character
       
 18521 				if (!editor.isDirty()) {
       
 18522 					setDirty(data[0] && getContent() != data[0].content);
       
 18523 
       
 18524 					// Fire initial change event
       
 18525 					if (!editor.isNotDirty) {
       
 18526 						editor.fire('change', {level: data[0], lastLevel: null});
       
 18527 					}
       
 18528 				}
       
 18529 
       
 18530 				editor.fire('TypingUndo');
       
 18531 				isFirstTypedCharacter = false;
       
 18532 				editor.nodeChanged();
       
 18533 			}
       
 18534 		});
       
 18535 
       
 18536 		editor.on('KeyDown', function(e) {
       
 18537 			var keyCode = e.keyCode;
       
 18538 
       
 18539 			// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
       
 18540 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
       
 18541 				if (self.typing) {
       
 18542 					addNonTypingUndoLevel(e);
       
 18543 				}
       
 18544 
       
 18545 				return;
       
 18546 			}
       
 18547 
       
 18548 			// If key isn't Ctrl+Alt/AltGr
       
 18549 			var modKey = (e.ctrlKey && !e.altKey) || e.metaKey;
       
 18550 			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing && !modKey) {
       
 18551 				self.beforeChange();
       
 18552 				self.typing = true;
       
 18553 				self.add({}, e);
       
 18554 				isFirstTypedCharacter = true;
       
 18555 			}
       
 18556 		});
       
 18557 
       
 18558 		editor.on('MouseDown', function(e) {
       
 18559 			if (self.typing) {
       
 18560 				addNonTypingUndoLevel(e);
       
 18561 			}
       
 18562 		});
       
 18563 
       
 18564 		// Add keyboard shortcuts for undo/redo keys
       
 18565 		editor.addShortcut('meta+z', '', 'Undo');
       
 18566 		editor.addShortcut('meta+y,meta+shift+z', '', 'Redo');
       
 18567 
       
 18568 		editor.on('AddUndo Undo Redo ClearUndos', function(e) {
       
 18569 			if (!e.isDefaultPrevented()) {
       
 18570 				editor.nodeChanged();
       
 18571 			}
       
 18572 		});
       
 18573 
       
 18574 		/*eslint consistent-this:0 */
       
 18575 		self = {
       
 18576 			// Explose for debugging reasons
       
 18577 			data: data,
       
 18578 
       
 18579 			/**
       
 18580 			 * State if the user is currently typing or not. This will add a typing operation into one undo
       
 18581 			 * level instead of one new level for each keystroke.
       
 18582 			 *
       
 18583 			 * @field {Boolean} typing
       
 18584 			 */
       
 18585 			typing: false,
       
 18586 
       
 18587 			/**
       
 18588 			 * Stores away a bookmark to be used when performing an undo action so that the selection is before
       
 18589 			 * the change has been made.
       
 18590 			 *
       
 18591 			 * @method beforeChange
       
 18592 			 */
       
 18593 			beforeChange: function() {
       
 18594 				if (!locks) {
       
 18595 					beforeBookmark = editor.selection.getBookmark(2, true);
       
 18596 				}
       
 18597 			},
       
 18598 
       
 18599 			/**
       
 18600 			 * Adds a new undo level/snapshot to the undo list.
       
 18601 			 *
       
 18602 			 * @method add
       
 18603 			 * @param {Object} level Optional undo level object to add.
       
 18604 			 * @param {DOMEvent} Event Optional event responsible for the creation of the undo level.
       
 18605 			 * @return {Object} Undo level that got added or null it a level wasn't needed.
       
 18606 			 */
       
 18607 			add: function(level, event) {
       
 18608 				var i, settings = editor.settings, lastLevel;
       
 18609 
       
 18610 				level = level || {};
       
 18611 				level.content = getContent();
       
 18612 
       
 18613 				if (locks || editor.removed) {
       
 18614 					return null;
       
 18615 				}
       
 18616 
       
 18617 				lastLevel = data[index];
       
 18618 				if (editor.fire('BeforeAddUndo', {level: level, lastLevel: lastLevel, originalEvent: event}).isDefaultPrevented()) {
       
 18619 					return null;
       
 18620 				}
       
 18621 
       
 18622 				// Add undo level if needed
       
 18623 				if (lastLevel && lastLevel.content == level.content) {
       
 18624 					return null;
       
 18625 				}
       
 18626 
       
 18627 				// Set before bookmark on previous level
       
 18628 				if (data[index]) {
       
 18629 					data[index].beforeBookmark = beforeBookmark;
       
 18630 				}
       
 18631 
       
 18632 				// Time to compress
       
 18633 				if (settings.custom_undo_redo_levels) {
       
 18634 					if (data.length > settings.custom_undo_redo_levels) {
       
 18635 						for (i = 0; i < data.length - 1; i++) {
       
 18636 							data[i] = data[i + 1];
       
 18637 						}
       
 18638 
       
 18639 						data.length--;
       
 18640 						index = data.length;
       
 18641 					}
       
 18642 				}
       
 18643 
       
 18644 				// Get a non intrusive normalized bookmark
       
 18645 				level.bookmark = editor.selection.getBookmark(2, true);
       
 18646 
       
 18647 				// Crop array if needed
       
 18648 				if (index < data.length - 1) {
       
 18649 					data.length = index + 1;
       
 18650 				}
       
 18651 
       
 18652 				data.push(level);
       
 18653 				index = data.length - 1;
       
 18654 
       
 18655 				var args = {level: level, lastLevel: lastLevel, originalEvent: event};
       
 18656 
       
 18657 				editor.fire('AddUndo', args);
       
 18658 
       
 18659 				if (index > 0) {
       
 18660 					setDirty(true);
       
 18661 					editor.fire('change', args);
       
 18662 				}
       
 18663 
       
 18664 				return level;
       
 18665 			},
       
 18666 
       
 18667 			/**
       
 18668 			 * Undoes the last action.
       
 18669 			 *
       
 18670 			 * @method undo
       
 18671 			 * @return {Object} Undo level or null if no undo was performed.
       
 18672 			 */
       
 18673 			undo: function() {
       
 18674 				var level;
       
 18675 
       
 18676 				if (self.typing) {
       
 18677 					self.add();
       
 18678 					self.typing = false;
       
 18679 				}
       
 18680 
       
 18681 				if (index > 0) {
       
 18682 					level = data[--index];
       
 18683 
       
 18684 					// Undo to first index then set dirty state to false
       
 18685 					if (index === 0) {
       
 18686 						setDirty(false);
       
 18687 					}
       
 18688 
       
 18689 					editor.setContent(level.content, {format: 'raw'});
       
 18690 					editor.selection.moveToBookmark(level.beforeBookmark);
       
 18691 
       
 18692 					editor.fire('undo', {level: level});
       
 18693 				}
       
 18694 
       
 18695 				return level;
       
 18696 			},
       
 18697 
       
 18698 			/**
       
 18699 			 * Redoes the last action.
       
 18700 			 *
       
 18701 			 * @method redo
       
 18702 			 * @return {Object} Redo level or null if no redo was performed.
       
 18703 			 */
       
 18704 			redo: function() {
       
 18705 				var level;
       
 18706 
       
 18707 				if (index < data.length - 1) {
       
 18708 					level = data[++index];
       
 18709 
       
 18710 					editor.setContent(level.content, {format: 'raw'});
       
 18711 					editor.selection.moveToBookmark(level.bookmark);
       
 18712 					setDirty(true);
       
 18713 
       
 18714 					editor.fire('redo', {level: level});
       
 18715 				}
       
 18716 
       
 18717 				return level;
       
 18718 			},
       
 18719 
       
 18720 			/**
       
 18721 			 * Removes all undo levels.
       
 18722 			 *
       
 18723 			 * @method clear
       
 18724 			 */
       
 18725 			clear: function() {
       
 18726 				data = [];
       
 18727 				index = 0;
       
 18728 				self.typing = false;
       
 18729 				editor.fire('ClearUndos');
       
 18730 			},
       
 18731 
       
 18732 			/**
       
 18733 			 * Returns true/false if the undo manager has any undo levels.
       
 18734 			 *
       
 18735 			 * @method hasUndo
       
 18736 			 * @return {Boolean} true/false if the undo manager has any undo levels.
       
 18737 			 */
       
 18738 			hasUndo: function() {
       
 18739 				// Has undo levels or typing and content isn't the same as the initial level
       
 18740 				return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
       
 18741 			},
       
 18742 
       
 18743 			/**
       
 18744 			 * Returns true/false if the undo manager has any redo levels.
       
 18745 			 *
       
 18746 			 * @method hasRedo
       
 18747 			 * @return {Boolean} true/false if the undo manager has any redo levels.
       
 18748 			 */
       
 18749 			hasRedo: function() {
       
 18750 				return index < data.length - 1 && !this.typing;
       
 18751 			},
       
 18752 
       
 18753 			/**
       
 18754 			 * Executes the specified function in an undo transation. The selection
       
 18755 			 * before the modification will be stored to the undo stack and if the DOM changes
       
 18756 			 * it will add a new undo level. Any methods within the transation that adds undo levels will
       
 18757 			 * be ignored. So a transation can include calls to execCommand or editor.insertContent.
       
 18758 			 *
       
 18759 			 * @method transact
       
 18760 			 * @param {function} callback Function to execute dom manipulation logic in.
       
 18761 			 */
       
 18762 			transact: function(callback) {
       
 18763 				self.beforeChange();
       
 18764 
       
 18765 				try {
       
 18766 					locks++;
       
 18767 					callback();
       
 18768 				} finally {
       
 18769 					locks--;
       
 18770 				}
       
 18771 
       
 18772 				self.add();
       
 18773 			}
       
 18774 		};
       
 18775 
       
 18776 		return self;
       
 18777 	};
       
 18778 });
       
 18779 
       
 18780 // Included from: js/tinymce/classes/EnterKey.js
       
 18781 
       
 18782 /**
       
 18783  * EnterKey.js
       
 18784  *
       
 18785  * Copyright, Moxiecode Systems AB
       
 18786  * Released under LGPL License.
       
 18787  *
       
 18788  * License: http://www.tinymce.com/license
       
 18789  * Contributing: http://www.tinymce.com/contributing
       
 18790  */
       
 18791 
       
 18792 /**
       
 18793  * Contains logic for handling the enter key to split/generate block elements.
       
 18794  */
       
 18795 define("tinymce/EnterKey", [
       
 18796 	"tinymce/dom/TreeWalker",
       
 18797 	"tinymce/dom/RangeUtils",
       
 18798 	"tinymce/Env"
       
 18799 ], function(TreeWalker, RangeUtils, Env) {
       
 18800 	var isIE = Env.ie && Env.ie < 11;
       
 18801 
       
 18802 	return function(editor) {
       
 18803 		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
       
 18804 		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements(),
       
 18805 			moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements();
       
 18806 
       
 18807 		function handleEnterKey(evt) {
       
 18808 			var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
       
 18809 				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
       
 18810 
       
 18811 			// Returns true if the block can be split into two blocks or not
       
 18812 			function canSplitBlock(node) {
       
 18813 				return node &&
       
 18814 					dom.isBlock(node) &&
       
 18815 					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
       
 18816 					!/^(fixed|absolute)/i.test(node.style.position) &&
       
 18817 					dom.getContentEditable(node) !== "true";
       
 18818 			}
       
 18819 
       
 18820 			// Renders empty block on IE
       
 18821 			function renderBlockOnIE(block) {
       
 18822 				var oldRng;
       
 18823 
       
 18824 				if (dom.isBlock(block)) {
       
 18825 					oldRng = selection.getRng();
       
 18826 					block.appendChild(dom.create('span', null, '\u00a0'));
       
 18827 					selection.select(block);
       
 18828 					block.lastChild.outerHTML = '';
       
 18829 					selection.setRng(oldRng);
       
 18830 				}
       
 18831 			}
       
 18832 
       
 18833 			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
       
 18834 			function trimInlineElementsOnLeftSideOfBlock(block) {
       
 18835 				var node = block, firstChilds = [], i;
       
 18836 
       
 18837 				if (!node) {
       
 18838 					return;
       
 18839 				}
       
 18840 
       
 18841 				// Find inner most first child ex: <p><i><b>*</b></i></p>
       
 18842 				while ((node = node.firstChild)) {
       
 18843 					if (dom.isBlock(node)) {
       
 18844 						return;
       
 18845 					}
       
 18846 
       
 18847 					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
 18848 						firstChilds.push(node);
       
 18849 					}
       
 18850 				}
       
 18851 
       
 18852 				i = firstChilds.length;
       
 18853 				while (i--) {
       
 18854 					node = firstChilds[i];
       
 18855 					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
       
 18856 						dom.remove(node);
       
 18857 					} else {
       
 18858 						// Remove <a> </a> see #5381
       
 18859 						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
       
 18860 							dom.remove(node);
       
 18861 						}
       
 18862 					}
       
 18863 				}
       
 18864 			}
       
 18865 
       
 18866 			// Moves the caret to a suitable position within the root for example in the first non
       
 18867 			// pure whitespace text node or before an image
       
 18868 			function moveToCaretPosition(root) {
       
 18869 				var walker, node, rng, lastNode = root, tempElm;
       
 18870 				function firstNonWhiteSpaceNodeSibling(node) {
       
 18871 					while (node) {
       
 18872 						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
       
 18873 							return node;
       
 18874 						}
       
 18875 
       
 18876 						node = node.nextSibling;
       
 18877 					}
       
 18878 				}
       
 18879 
       
 18880 				if (!root) {
       
 18881 					return;
       
 18882 				}
       
 18883 
       
 18884 				// Old IE versions doesn't properly render blocks with br elements in them
       
 18885 				// For example <p><br></p> wont be rendered correctly in a contentEditable area
       
 18886 				// until you remove the br producing <p></p>
       
 18887 				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
       
 18888 					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
       
 18889 						dom.remove(parentBlock.firstChild);
       
 18890 					}
       
 18891 				}
       
 18892 
       
 18893 				if (/^(LI|DT|DD)$/.test(root.nodeName)) {
       
 18894 					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
       
 18895 
       
 18896 					if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
       
 18897 						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
       
 18898 					}
       
 18899 				}
       
 18900 
       
 18901 				rng = dom.createRng();
       
 18902 
       
 18903 				// Normalize whitespace to remove empty text nodes. Fix for: #6904
       
 18904 				// Gecko will be able to place the caret in empty text nodes but it won't render propery
       
 18905 				// Older IE versions will sometimes crash so for now ignore all IE versions
       
 18906 				if (!Env.ie) {
       
 18907 					root.normalize();
       
 18908 				}
       
 18909 
       
 18910 				if (root.hasChildNodes()) {
       
 18911 					walker = new TreeWalker(root, root);
       
 18912 
       
 18913 					while ((node = walker.current())) {
       
 18914 						if (node.nodeType == 3) {
       
 18915 							rng.setStart(node, 0);
       
 18916 							rng.setEnd(node, 0);
       
 18917 							break;
       
 18918 						}
       
 18919 
       
 18920 						if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) {
       
 18921 							rng.setStartBefore(node);
       
 18922 							rng.setEndBefore(node);
       
 18923 							break;
       
 18924 						}
       
 18925 
       
 18926 						lastNode = node;
       
 18927 						node = walker.next();
       
 18928 					}
       
 18929 
       
 18930 					if (!node) {
       
 18931 						rng.setStart(lastNode, 0);
       
 18932 						rng.setEnd(lastNode, 0);
       
 18933 					}
       
 18934 				} else {
       
 18935 					if (root.nodeName == 'BR') {
       
 18936 						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
       
 18937 							// Trick on older IE versions to render the caret before the BR between two lists
       
 18938 							if (!documentMode || documentMode < 9) {
       
 18939 								tempElm = dom.create('br');
       
 18940 								root.parentNode.insertBefore(tempElm, root);
       
 18941 							}
       
 18942 
       
 18943 							rng.setStartBefore(root);
       
 18944 							rng.setEndBefore(root);
       
 18945 						} else {
       
 18946 							rng.setStartAfter(root);
       
 18947 							rng.setEndAfter(root);
       
 18948 						}
       
 18949 					} else {
       
 18950 						rng.setStart(root, 0);
       
 18951 						rng.setEnd(root, 0);
       
 18952 					}
       
 18953 				}
       
 18954 
       
 18955 				selection.setRng(rng);
       
 18956 
       
 18957 				// Remove tempElm created for old IE:s
       
 18958 				dom.remove(tempElm);
       
 18959 				selection.scrollIntoView(root);
       
 18960 			}
       
 18961 
       
 18962 			function setForcedBlockAttrs(node) {
       
 18963 				var forcedRootBlockName = settings.forced_root_block;
       
 18964 
       
 18965 				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
       
 18966 					dom.setAttribs(node, settings.forced_root_block_attrs);
       
 18967 				}
       
 18968 			}
       
 18969 
       
 18970 			// Creates a new block element by cloning the current one or creating a new one if the name is specified
       
 18971 			// This function will also copy any text formatting from the parent block and add it to the new one
       
 18972 			function createNewBlock(name) {
       
 18973 				var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements();
       
 18974 
       
 18975 				if (name || parentBlockName == "TABLE") {
       
 18976 					block = dom.create(name || newBlockName);
       
 18977 					setForcedBlockAttrs(block);
       
 18978 				} else {
       
 18979 					block = parentBlock.cloneNode(false);
       
 18980 				}
       
 18981 
       
 18982 				caretNode = block;
       
 18983 
       
 18984 				// Clone any parent styles
       
 18985 				if (settings.keep_styles !== false) {
       
 18986 					do {
       
 18987 						if (textInlineElements[node.nodeName]) {
       
 18988 							// Never clone a caret containers
       
 18989 							if (node.id == '_mce_caret') {
       
 18990 								continue;
       
 18991 							}
       
 18992 
       
 18993 							clonedNode = node.cloneNode(false);
       
 18994 							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
       
 18995 
       
 18996 							if (block.hasChildNodes()) {
       
 18997 								clonedNode.appendChild(block.firstChild);
       
 18998 								block.appendChild(clonedNode);
       
 18999 							} else {
       
 19000 								caretNode = clonedNode;
       
 19001 								block.appendChild(clonedNode);
       
 19002 							}
       
 19003 						}
       
 19004 					} while ((node = node.parentNode));
       
 19005 				}
       
 19006 
       
 19007 				// BR is needed in empty blocks on non IE browsers
       
 19008 				if (!isIE) {
       
 19009 					caretNode.innerHTML = '<br data-mce-bogus="1">';
       
 19010 				}
       
 19011 
       
 19012 				return block;
       
 19013 			}
       
 19014 
       
 19015 			// Returns true/false if the caret is at the start/end of the parent block element
       
 19016 			function isCaretAtStartOrEndOfBlock(start) {
       
 19017 				var walker, node, name;
       
 19018 
       
 19019 				// Caret is in the middle of a text node like "a|b"
       
 19020 				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
       
 19021 					return false;
       
 19022 				}
       
 19023 
       
 19024 				// If after the last element in block node edge case for #5091
       
 19025 				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
       
 19026 					return true;
       
 19027 				}
       
 19028 
       
 19029 				// If the caret if before the first element in parentBlock
       
 19030 				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
       
 19031 					return true;
       
 19032 				}
       
 19033 
       
 19034 				// Caret can be before/after a table
       
 19035 				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
       
 19036 					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
       
 19037 				}
       
 19038 
       
 19039 				// Walk the DOM and look for text nodes or non empty elements
       
 19040 				walker = new TreeWalker(container, parentBlock);
       
 19041 
       
 19042 				// If caret is in beginning or end of a text block then jump to the next/previous node
       
 19043 				if (container.nodeType == 3) {
       
 19044 					if (start && offset === 0) {
       
 19045 						walker.prev();
       
 19046 					} else if (!start && offset == container.nodeValue.length) {
       
 19047 						walker.next();
       
 19048 					}
       
 19049 				}
       
 19050 
       
 19051 				while ((node = walker.current())) {
       
 19052 					if (node.nodeType === 1) {
       
 19053 						// Ignore bogus elements
       
 19054 						if (!node.getAttribute('data-mce-bogus')) {
       
 19055 							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
       
 19056 							name = node.nodeName.toLowerCase();
       
 19057 							if (nonEmptyElementsMap[name] && name !== 'br') {
       
 19058 								return false;
       
 19059 							}
       
 19060 						}
       
 19061 					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
       
 19062 						return false;
       
 19063 					}
       
 19064 
       
 19065 					if (start) {
       
 19066 						walker.prev();
       
 19067 					} else {
       
 19068 						walker.next();
       
 19069 					}
       
 19070 				}
       
 19071 
       
 19072 				return true;
       
 19073 			}
       
 19074 
       
 19075 			// Wraps any text nodes or inline elements in the specified forced root block name
       
 19076 			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
       
 19077 				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
       
 19078 
       
 19079 				// Not in a block element or in a table cell or caption
       
 19080 				parentBlock = dom.getParent(container, dom.isBlock);
       
 19081 				rootBlockName = editor.getBody().nodeName.toLowerCase();
       
 19082 				if (!parentBlock || !canSplitBlock(parentBlock)) {
       
 19083 					parentBlock = parentBlock || editableRoot;
       
 19084 
       
 19085 					if (!parentBlock.hasChildNodes()) {
       
 19086 						newBlock = dom.create(blockName);
       
 19087 						setForcedBlockAttrs(newBlock);
       
 19088 						parentBlock.appendChild(newBlock);
       
 19089 						rng.setStart(newBlock, 0);
       
 19090 						rng.setEnd(newBlock, 0);
       
 19091 						return newBlock;
       
 19092 					}
       
 19093 
       
 19094 					// Find parent that is the first child of parentBlock
       
 19095 					node = container;
       
 19096 					while (node.parentNode != parentBlock) {
       
 19097 						node = node.parentNode;
       
 19098 					}
       
 19099 
       
 19100 					// Loop left to find start node start wrapping at
       
 19101 					while (node && !dom.isBlock(node)) {
       
 19102 						startNode = node;
       
 19103 						node = node.previousSibling;
       
 19104 					}
       
 19105 
       
 19106 					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
       
 19107 						newBlock = dom.create(blockName);
       
 19108 						setForcedBlockAttrs(newBlock);
       
 19109 						startNode.parentNode.insertBefore(newBlock, startNode);
       
 19110 
       
 19111 						// Start wrapping until we hit a block
       
 19112 						node = startNode;
       
 19113 						while (node && !dom.isBlock(node)) {
       
 19114 							next = node.nextSibling;
       
 19115 							newBlock.appendChild(node);
       
 19116 							node = next;
       
 19117 						}
       
 19118 
       
 19119 						// Restore range to it's past location
       
 19120 						rng.setStart(container, offset);
       
 19121 						rng.setEnd(container, offset);
       
 19122 					}
       
 19123 				}
       
 19124 
       
 19125 				return container;
       
 19126 			}
       
 19127 
       
 19128 			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
       
 19129 			function handleEmptyListItem() {
       
 19130 				function isFirstOrLastLi(first) {
       
 19131 					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
       
 19132 
       
 19133 					// Find first/last element since there might be whitespace there
       
 19134 					while (node) {
       
 19135 						if (node.nodeType == 1) {
       
 19136 							break;
       
 19137 						}
       
 19138 
       
 19139 						node = node[first ? 'nextSibling' : 'previousSibling'];
       
 19140 					}
       
 19141 
       
 19142 					return node === parentBlock;
       
 19143 				}
       
 19144 
       
 19145 				function getContainerBlock() {
       
 19146 					var containerBlockParent = containerBlock.parentNode;
       
 19147 
       
 19148 					if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) {
       
 19149 						return containerBlockParent;
       
 19150 					}
       
 19151 
       
 19152 					return containerBlock;
       
 19153 				}
       
 19154 
       
 19155 				// Check if we are in an nested list
       
 19156 				var containerBlockParentName = containerBlock.parentNode.nodeName;
       
 19157 				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
       
 19158 					newBlockName = 'LI';
       
 19159 				}
       
 19160 
       
 19161 				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
       
 19162 
       
 19163 				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
       
 19164 					if (containerBlockParentName == 'LI') {
       
 19165 						// Nested list is inside a LI
       
 19166 						dom.insertAfter(newBlock, getContainerBlock());
       
 19167 					} else {
       
 19168 						// Is first and last list item then replace the OL/UL with a text block
       
 19169 						dom.replace(newBlock, containerBlock);
       
 19170 					}
       
 19171 				} else if (isFirstOrLastLi(true)) {
       
 19172 					if (containerBlockParentName == 'LI') {
       
 19173 						// List nested in an LI then move the list to a new sibling LI
       
 19174 						dom.insertAfter(newBlock, getContainerBlock());
       
 19175 						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
       
 19176 						newBlock.appendChild(containerBlock);
       
 19177 					} else {
       
 19178 						// First LI in list then remove LI and add text block before list
       
 19179 						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
       
 19180 					}
       
 19181 				} else if (isFirstOrLastLi()) {
       
 19182 					// Last LI in list then remove LI and add text block after list
       
 19183 					dom.insertAfter(newBlock, getContainerBlock());
       
 19184 					renderBlockOnIE(newBlock);
       
 19185 				} else {
       
 19186 					// Middle LI in list the split the list and insert a text block in the middle
       
 19187 					// Extract after fragment and insert it after the current block
       
 19188 					containerBlock = getContainerBlock();
       
 19189 					tmpRng = rng.cloneRange();
       
 19190 					tmpRng.setStartAfter(parentBlock);
       
 19191 					tmpRng.setEndAfter(containerBlock);
       
 19192 					fragment = tmpRng.extractContents();
       
 19193 
       
 19194 					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
       
 19195 						newBlock = fragment.firstChild;
       
 19196 						dom.insertAfter(fragment, containerBlock);
       
 19197 					} else {
       
 19198 						dom.insertAfter(fragment, containerBlock);
       
 19199 						dom.insertAfter(newBlock, containerBlock);
       
 19200 					}
       
 19201 				}
       
 19202 
       
 19203 				dom.remove(parentBlock);
       
 19204 				moveToCaretPosition(newBlock);
       
 19205 				undoManager.add();
       
 19206 			}
       
 19207 
       
 19208 			// Inserts a BR element if the forced_root_block option is set to false or empty string
       
 19209 			function insertBr() {
       
 19210 				editor.execCommand("InsertLineBreak", false, evt);
       
 19211 			}
       
 19212 
       
 19213 			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
       
 19214 			function trimLeadingLineBreaks(node) {
       
 19215 				do {
       
 19216 					if (node.nodeType === 3) {
       
 19217 						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
       
 19218 					}
       
 19219 
       
 19220 					node = node.firstChild;
       
 19221 				} while (node);
       
 19222 			}
       
 19223 
       
 19224 			function getEditableRoot(node) {
       
 19225 				var root = dom.getRoot(), parent, editableRoot;
       
 19226 
       
 19227 				// Get all parents until we hit a non editable parent or the root
       
 19228 				parent = node;
       
 19229 				while (parent !== root && dom.getContentEditable(parent) !== "false") {
       
 19230 					if (dom.getContentEditable(parent) === "true") {
       
 19231 						editableRoot = parent;
       
 19232 					}
       
 19233 
       
 19234 					parent = parent.parentNode;
       
 19235 				}
       
 19236 
       
 19237 				return parent !== root ? editableRoot : root;
       
 19238 			}
       
 19239 
       
 19240 			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
       
 19241 			// these might be floated and then they won't expand the block
       
 19242 			function addBrToBlockIfNeeded(block) {
       
 19243 				var lastChild;
       
 19244 
       
 19245 				// IE will render the blocks correctly other browsers needs a BR
       
 19246 				if (!isIE) {
       
 19247 					block.normalize(); // Remove empty text nodes that got left behind by the extract
       
 19248 
       
 19249 					// Check if the block is empty or contains a floated last child
       
 19250 					lastChild = block.lastChild;
       
 19251 					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
       
 19252 						dom.add(block, 'br');
       
 19253 					}
       
 19254 				}
       
 19255 			}
       
 19256 
       
 19257 			rng = selection.getRng(true);
       
 19258 
       
 19259 			// Event is blocked by some other handler for example the lists plugin
       
 19260 			if (evt.isDefaultPrevented()) {
       
 19261 				return;
       
 19262 			}
       
 19263 
       
 19264 			// Delete any selected contents
       
 19265 			if (!rng.collapsed) {
       
 19266 				editor.execCommand('Delete');
       
 19267 				return;
       
 19268 			}
       
 19269 
       
 19270 			// Setup range items and newBlockName
       
 19271 			new RangeUtils(dom).normalize(rng);
       
 19272 			container = rng.startContainer;
       
 19273 			offset = rng.startOffset;
       
 19274 			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
       
 19275 			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
       
 19276 			documentMode = dom.doc.documentMode;
       
 19277 			shiftKey = evt.shiftKey;
       
 19278 
       
 19279 			// Resolve node index
       
 19280 			if (container.nodeType == 1 && container.hasChildNodes()) {
       
 19281 				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
       
 19282 
       
 19283 				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
       
 19284 				if (isAfterLastNodeInContainer && container.nodeType == 3) {
       
 19285 					offset = container.nodeValue.length;
       
 19286 				} else {
       
 19287 					offset = 0;
       
 19288 				}
       
 19289 			}
       
 19290 
       
 19291 			// Get editable root node normaly the body element but sometimes a div or span
       
 19292 			editableRoot = getEditableRoot(container);
       
 19293 
       
 19294 			// If there is no editable root then enter is done inside a contentEditable false element
       
 19295 			if (!editableRoot) {
       
 19296 				return;
       
 19297 			}
       
 19298 
       
 19299 			undoManager.beforeChange();
       
 19300 
       
 19301 			// If editable root isn't block nor the root of the editor
       
 19302 			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
       
 19303 				if (!newBlockName || shiftKey) {
       
 19304 					insertBr();
       
 19305 				}
       
 19306 
       
 19307 				return;
       
 19308 			}
       
 19309 
       
 19310 			// Wrap the current node and it's sibling in a default block if it's needed.
       
 19311 			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
       
 19312 			// This won't happen if root blocks are disabled or the shiftKey is pressed
       
 19313 			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
       
 19314 				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
       
 19315 			}
       
 19316 
       
 19317 			// Find parent block and setup empty block paddings
       
 19318 			parentBlock = dom.getParent(container, dom.isBlock);
       
 19319 			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
       
 19320 
       
 19321 			// Setup block names
       
 19322 			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 19323 			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 19324 
       
 19325 			// Enter inside block contained within a LI then split or insert before/after LI
       
 19326 			if (containerBlockName == 'LI' && !evt.ctrlKey) {
       
 19327 				parentBlock = containerBlock;
       
 19328 				parentBlockName = containerBlockName;
       
 19329 			}
       
 19330 
       
 19331 			// Handle enter in list item
       
 19332 			if (/^(LI|DT|DD)$/.test(parentBlockName)) {
       
 19333 				if (!newBlockName && shiftKey) {
       
 19334 					insertBr();
       
 19335 					return;
       
 19336 				}
       
 19337 
       
 19338 				// Handle enter inside an empty list item
       
 19339 				if (dom.isEmpty(parentBlock)) {
       
 19340 					handleEmptyListItem();
       
 19341 					return;
       
 19342 				}
       
 19343 			}
       
 19344 
       
 19345 			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
       
 19346 			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
       
 19347 				if (!shiftKey) {
       
 19348 					insertBr();
       
 19349 					return;
       
 19350 				}
       
 19351 			} else {
       
 19352 				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
       
 19353 				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
       
 19354 					insertBr();
       
 19355 					return;
       
 19356 				}
       
 19357 			}
       
 19358 
       
 19359 			// If parent block is root then never insert new blocks
       
 19360 			if (newBlockName && parentBlock === editor.getBody()) {
       
 19361 				return;
       
 19362 			}
       
 19363 
       
 19364 			// Default block name if it's not configured
       
 19365 			newBlockName = newBlockName || 'P';
       
 19366 
       
 19367 			// Insert new block before/after the parent block depending on caret location
       
 19368 			if (isCaretAtStartOrEndOfBlock()) {
       
 19369 				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
       
 19370 				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
       
 19371 					newBlock = createNewBlock(newBlockName);
       
 19372 				} else {
       
 19373 					newBlock = createNewBlock();
       
 19374 				}
       
 19375 
       
 19376 				// Split the current container block element if enter is pressed inside an empty inner block element
       
 19377 				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
       
 19378 					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
       
 19379 					newBlock = dom.split(containerBlock, parentBlock);
       
 19380 				} else {
       
 19381 					dom.insertAfter(newBlock, parentBlock);
       
 19382 				}
       
 19383 
       
 19384 				moveToCaretPosition(newBlock);
       
 19385 			} else if (isCaretAtStartOrEndOfBlock(true)) {
       
 19386 				// Insert new block before
       
 19387 				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
       
 19388 				renderBlockOnIE(newBlock);
       
 19389 				moveToCaretPosition(parentBlock);
       
 19390 			} else {
       
 19391 				// Extract after fragment and insert it after the current block
       
 19392 				tmpRng = rng.cloneRange();
       
 19393 				tmpRng.setEndAfter(parentBlock);
       
 19394 				fragment = tmpRng.extractContents();
       
 19395 				trimLeadingLineBreaks(fragment);
       
 19396 				newBlock = fragment.firstChild;
       
 19397 				dom.insertAfter(fragment, parentBlock);
       
 19398 				trimInlineElementsOnLeftSideOfBlock(newBlock);
       
 19399 				addBrToBlockIfNeeded(parentBlock);
       
 19400 				moveToCaretPosition(newBlock);
       
 19401 			}
       
 19402 
       
 19403 			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
       
 19404 
       
 19405 			// Allow custom handling of new blocks
       
 19406 			editor.fire('NewBlock', {newBlock: newBlock});
       
 19407 
       
 19408 			undoManager.add();
       
 19409 		}
       
 19410 
       
 19411 		editor.on('keydown', function(evt) {
       
 19412 			if (evt.keyCode == 13) {
       
 19413 				if (handleEnterKey(evt) !== false) {
       
 19414 					evt.preventDefault();
       
 19415 				}
       
 19416 			}
       
 19417 		});
       
 19418 	};
       
 19419 });
       
 19420 
       
 19421 // Included from: js/tinymce/classes/ForceBlocks.js
       
 19422 
       
 19423 /**
       
 19424  * ForceBlocks.js
       
 19425  *
       
 19426  * Copyright, Moxiecode Systems AB
       
 19427  * Released under LGPL License.
       
 19428  *
       
 19429  * License: http://www.tinymce.com/license
       
 19430  * Contributing: http://www.tinymce.com/contributing
       
 19431  */
       
 19432 
       
 19433 define("tinymce/ForceBlocks", [], function() {
       
 19434 	return function(editor) {
       
 19435 		var settings = editor.settings, dom = editor.dom, selection = editor.selection;
       
 19436 		var schema = editor.schema, blockElements = schema.getBlockElements();
       
 19437 
       
 19438 		function addRootBlocks() {
       
 19439 			var node = selection.getStart(), rootNode = editor.getBody(), rng;
       
 19440 			var startContainer, startOffset, endContainer, endOffset, rootBlockNode;
       
 19441 			var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection;
       
 19442 			var tmpRng, rootNodeName, forcedRootBlock;
       
 19443 
       
 19444 			forcedRootBlock = settings.forced_root_block;
       
 19445 
       
 19446 			if (!node || node.nodeType !== 1 || !forcedRootBlock) {
       
 19447 				return;
       
 19448 			}
       
 19449 
       
 19450 			// Check if node is wrapped in block
       
 19451 			while (node && node != rootNode) {
       
 19452 				if (blockElements[node.nodeName]) {
       
 19453 					return;
       
 19454 				}
       
 19455 
       
 19456 				node = node.parentNode;
       
 19457 			}
       
 19458 
       
 19459 			// Get current selection
       
 19460 			rng = selection.getRng();
       
 19461 			if (rng.setStart) {
       
 19462 				startContainer = rng.startContainer;
       
 19463 				startOffset = rng.startOffset;
       
 19464 				endContainer = rng.endContainer;
       
 19465 				endOffset = rng.endOffset;
       
 19466 
       
 19467 				try {
       
 19468 					restoreSelection = editor.getDoc().activeElement === rootNode;
       
 19469 				} catch (ex) {
       
 19470 					// IE throws unspecified error here sometimes
       
 19471 				}
       
 19472 			} else {
       
 19473 				// Force control range into text range
       
 19474 				if (rng.item) {
       
 19475 					node = rng.item(0);
       
 19476 					rng = editor.getDoc().body.createTextRange();
       
 19477 					rng.moveToElementText(node);
       
 19478 				}
       
 19479 
       
 19480 				restoreSelection = rng.parentElement().ownerDocument === editor.getDoc();
       
 19481 				tmpRng = rng.duplicate();
       
 19482 				tmpRng.collapse(true);
       
 19483 				startOffset = tmpRng.move('character', offset) * -1;
       
 19484 
       
 19485 				if (!tmpRng.collapsed) {
       
 19486 					tmpRng = rng.duplicate();
       
 19487 					tmpRng.collapse(false);
       
 19488 					endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
       
 19489 				}
       
 19490 			}
       
 19491 
       
 19492 			// Wrap non block elements and text nodes
       
 19493 			node = rootNode.firstChild;
       
 19494 			rootNodeName = rootNode.nodeName.toLowerCase();
       
 19495 			while (node) {
       
 19496 				// TODO: Break this up, too complex
       
 19497 				if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) &&
       
 19498 					schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) {
       
 19499 					// Remove empty text nodes
       
 19500 					if (node.nodeType === 3 && node.nodeValue.length === 0) {
       
 19501 						tempNode = node;
       
 19502 						node = node.nextSibling;
       
 19503 						dom.remove(tempNode);
       
 19504 						continue;
       
 19505 					}
       
 19506 
       
 19507 					if (!rootBlockNode) {
       
 19508 						rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs);
       
 19509 						node.parentNode.insertBefore(rootBlockNode, node);
       
 19510 						wrapped = true;
       
 19511 					}
       
 19512 
       
 19513 					tempNode = node;
       
 19514 					node = node.nextSibling;
       
 19515 					rootBlockNode.appendChild(tempNode);
       
 19516 				} else {
       
 19517 					rootBlockNode = null;
       
 19518 					node = node.nextSibling;
       
 19519 				}
       
 19520 			}
       
 19521 
       
 19522 			if (wrapped && restoreSelection) {
       
 19523 				if (rng.setStart) {
       
 19524 					rng.setStart(startContainer, startOffset);
       
 19525 					rng.setEnd(endContainer, endOffset);
       
 19526 					selection.setRng(rng);
       
 19527 				} else {
       
 19528 					// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
       
 19529 					try {
       
 19530 						rng = editor.getDoc().body.createTextRange();
       
 19531 						rng.moveToElementText(rootNode);
       
 19532 						rng.collapse(true);
       
 19533 						rng.moveStart('character', startOffset);
       
 19534 
       
 19535 						if (endOffset > 0) {
       
 19536 							rng.moveEnd('character', endOffset);
       
 19537 						}
       
 19538 
       
 19539 						rng.select();
       
 19540 					} catch (ex) {
       
 19541 						// Ignore
       
 19542 					}
       
 19543 				}
       
 19544 
       
 19545 				editor.nodeChanged();
       
 19546 			}
       
 19547 		}
       
 19548 
       
 19549 		// Force root blocks
       
 19550 		if (settings.forced_root_block) {
       
 19551 			editor.on('NodeChange', addRootBlocks);
       
 19552 		}
       
 19553 	};
       
 19554 });
       
 19555 
       
 19556 // Included from: js/tinymce/classes/EditorCommands.js
       
 19557 
       
 19558 /**
       
 19559  * EditorCommands.js
       
 19560  *
       
 19561  * Copyright, Moxiecode Systems AB
       
 19562  * Released under LGPL License.
       
 19563  *
       
 19564  * License: http://www.tinymce.com/license
       
 19565  * Contributing: http://www.tinymce.com/contributing
       
 19566  */
       
 19567 
       
 19568 /**
       
 19569  * This class enables you to add custom editor commands and it contains
       
 19570  * overrides for native browser commands to address various bugs and issues.
       
 19571  *
       
 19572  * @class tinymce.EditorCommands
       
 19573  */
       
 19574 define("tinymce/EditorCommands", [
       
 19575 	"tinymce/html/Serializer",
       
 19576 	"tinymce/Env",
       
 19577 	"tinymce/util/Tools",
       
 19578 	"tinymce/dom/ElementUtils",
       
 19579 	"tinymce/dom/RangeUtils",
       
 19580 	"tinymce/dom/TreeWalker"
       
 19581 ], function(Serializer, Env, Tools, ElementUtils, RangeUtils, TreeWalker) {
       
 19582 	// Added for compression purposes
       
 19583 	var each = Tools.each, extend = Tools.extend;
       
 19584 	var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
       
 19585 	var isGecko = Env.gecko, isIE = Env.ie, isOldIE = Env.ie && Env.ie < 11;
       
 19586 	var TRUE = true, FALSE = false;
       
 19587 
       
 19588 	return function(editor) {
       
 19589 		var dom, selection, formatter,
       
 19590 			commands = {state: {}, exec: {}, value: {}},
       
 19591 			settings = editor.settings,
       
 19592 			bookmark;
       
 19593 
       
 19594 		editor.on('PreInit', function() {
       
 19595 			dom = editor.dom;
       
 19596 			selection = editor.selection;
       
 19597 			settings = editor.settings;
       
 19598 			formatter = editor.formatter;
       
 19599 		});
       
 19600 
       
 19601 		/**
       
 19602 		 * Executes the specified command.
       
 19603 		 *
       
 19604 		 * @method execCommand
       
 19605 		 * @param {String} command Command to execute.
       
 19606 		 * @param {Boolean} ui Optional user interface state.
       
 19607 		 * @param {Object} value Optional value for command.
       
 19608 		 * @return {Boolean} true/false if the command was found or not.
       
 19609 		 */
       
 19610 		function execCommand(command, ui, value, args) {
       
 19611 			var func, customCommand, state = 0;
       
 19612 
       
 19613 			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(command) && (!args || !args.skip_focus)) {
       
 19614 				editor.focus();
       
 19615 			}
       
 19616 
       
 19617 			args = extend({}, args);
       
 19618 			args = editor.fire('BeforeExecCommand', {command: command, ui: ui, value: value});
       
 19619 			if (args.isDefaultPrevented()) {
       
 19620 				return false;
       
 19621 			}
       
 19622 
       
 19623 			customCommand = command.toLowerCase();
       
 19624 			if ((func = commands.exec[customCommand])) {
       
 19625 				func(customCommand, ui, value);
       
 19626 				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 19627 				return true;
       
 19628 			}
       
 19629 
       
 19630 			// Plugin commands
       
 19631 			each(editor.plugins, function(p) {
       
 19632 				if (p.execCommand && p.execCommand(command, ui, value)) {
       
 19633 					editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 19634 					state = true;
       
 19635 					return false;
       
 19636 				}
       
 19637 			});
       
 19638 
       
 19639 			if (state) {
       
 19640 				return state;
       
 19641 			}
       
 19642 
       
 19643 			// Theme commands
       
 19644 			if (editor.theme && editor.theme.execCommand && editor.theme.execCommand(command, ui, value)) {
       
 19645 				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 19646 				return true;
       
 19647 			}
       
 19648 
       
 19649 			// Browser commands
       
 19650 			try {
       
 19651 				state = editor.getDoc().execCommand(command, ui, value);
       
 19652 			} catch (ex) {
       
 19653 				// Ignore old IE errors
       
 19654 			}
       
 19655 
       
 19656 			if (state) {
       
 19657 				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 19658 				return true;
       
 19659 			}
       
 19660 
       
 19661 			return false;
       
 19662 		}
       
 19663 
       
 19664 		/**
       
 19665 		 * Queries the current state for a command for example if the current selection is "bold".
       
 19666 		 *
       
 19667 		 * @method queryCommandState
       
 19668 		 * @param {String} command Command to check the state of.
       
 19669 		 * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
       
 19670 		 */
       
 19671 		function queryCommandState(command) {
       
 19672 			var func;
       
 19673 
       
 19674 			// Is hidden then return undefined
       
 19675 			if (editor._isHidden()) {
       
 19676 				return;
       
 19677 			}
       
 19678 
       
 19679 			command = command.toLowerCase();
       
 19680 			if ((func = commands.state[command])) {
       
 19681 				return func(command);
       
 19682 			}
       
 19683 
       
 19684 			// Browser commands
       
 19685 			try {
       
 19686 				return editor.getDoc().queryCommandState(command);
       
 19687 			} catch (ex) {
       
 19688 				// Fails sometimes see bug: 1896577
       
 19689 			}
       
 19690 
       
 19691 			return false;
       
 19692 		}
       
 19693 
       
 19694 		/**
       
 19695 		 * Queries the command value for example the current fontsize.
       
 19696 		 *
       
 19697 		 * @method queryCommandValue
       
 19698 		 * @param {String} command Command to check the value of.
       
 19699 		 * @return {Object} Command value of false if it's not found.
       
 19700 		 */
       
 19701 		function queryCommandValue(command) {
       
 19702 			var func;
       
 19703 
       
 19704 			// Is hidden then return undefined
       
 19705 			if (editor._isHidden()) {
       
 19706 				return;
       
 19707 			}
       
 19708 
       
 19709 			command = command.toLowerCase();
       
 19710 			if ((func = commands.value[command])) {
       
 19711 				return func(command);
       
 19712 			}
       
 19713 
       
 19714 			// Browser commands
       
 19715 			try {
       
 19716 				return editor.getDoc().queryCommandValue(command);
       
 19717 			} catch (ex) {
       
 19718 				// Fails sometimes see bug: 1896577
       
 19719 			}
       
 19720 		}
       
 19721 
       
 19722 		/**
       
 19723 		 * Adds commands to the command collection.
       
 19724 		 *
       
 19725 		 * @method addCommands
       
 19726 		 * @param {Object} command_list Name/value collection with commands to add, the names can also be comma separated.
       
 19727 		 * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
       
 19728 		 */
       
 19729 		function addCommands(command_list, type) {
       
 19730 			type = type || 'exec';
       
 19731 
       
 19732 			each(command_list, function(callback, command) {
       
 19733 				each(command.toLowerCase().split(','), function(command) {
       
 19734 					commands[type][command] = callback;
       
 19735 				});
       
 19736 			});
       
 19737 		}
       
 19738 
       
 19739 		function addCommand(command, callback, scope) {
       
 19740 			command = command.toLowerCase();
       
 19741 			commands.exec[command] = function(command, ui, value, args) {
       
 19742 				return callback.call(scope || editor, ui, value, args);
       
 19743 			};
       
 19744 		}
       
 19745 
       
 19746 		/**
       
 19747 		 * Returns true/false if the command is supported or not.
       
 19748 		 *
       
 19749 		 * @method queryCommandSupported
       
 19750 		 * @param {String} cmd Command that we check support for.
       
 19751 		 * @return {Boolean} true/false if the command is supported or not.
       
 19752 		 */
       
 19753 		function queryCommandSupported(command) {
       
 19754 			command = command.toLowerCase();
       
 19755 
       
 19756 			if (commands.exec[command]) {
       
 19757 				return true;
       
 19758 			}
       
 19759 
       
 19760 			// Browser commands
       
 19761 			try {
       
 19762 				return editor.getDoc().queryCommandSupported(command);
       
 19763 			} catch (ex) {
       
 19764 				// Fails sometimes see bug: 1896577
       
 19765 			}
       
 19766 
       
 19767 			return false;
       
 19768 		}
       
 19769 
       
 19770 		function addQueryStateHandler(command, callback, scope) {
       
 19771 			command = command.toLowerCase();
       
 19772 			commands.state[command] = function() {
       
 19773 				return callback.call(scope || editor);
       
 19774 			};
       
 19775 		}
       
 19776 
       
 19777 		function addQueryValueHandler(command, callback, scope) {
       
 19778 			command = command.toLowerCase();
       
 19779 			commands.value[command] = function() {
       
 19780 				return callback.call(scope || editor);
       
 19781 			};
       
 19782 		}
       
 19783 
       
 19784 		function hasCustomCommand(command) {
       
 19785 			command = command.toLowerCase();
       
 19786 			return !!commands.exec[command];
       
 19787 		}
       
 19788 
       
 19789 		// Expose public methods
       
 19790 		extend(this, {
       
 19791 			execCommand: execCommand,
       
 19792 			queryCommandState: queryCommandState,
       
 19793 			queryCommandValue: queryCommandValue,
       
 19794 			queryCommandSupported: queryCommandSupported,
       
 19795 			addCommands: addCommands,
       
 19796 			addCommand: addCommand,
       
 19797 			addQueryStateHandler: addQueryStateHandler,
       
 19798 			addQueryValueHandler: addQueryValueHandler,
       
 19799 			hasCustomCommand: hasCustomCommand
       
 19800 		});
       
 19801 
       
 19802 		// Private methods
       
 19803 
       
 19804 		function execNativeCommand(command, ui, value) {
       
 19805 			if (ui === undefined) {
       
 19806 				ui = FALSE;
       
 19807 			}
       
 19808 
       
 19809 			if (value === undefined) {
       
 19810 				value = null;
       
 19811 			}
       
 19812 
       
 19813 			return editor.getDoc().execCommand(command, ui, value);
       
 19814 		}
       
 19815 
       
 19816 		function isFormatMatch(name) {
       
 19817 			return formatter.match(name);
       
 19818 		}
       
 19819 
       
 19820 		function toggleFormat(name, value) {
       
 19821 			formatter.toggle(name, value ? {value: value} : undefined);
       
 19822 			editor.nodeChanged();
       
 19823 		}
       
 19824 
       
 19825 		function storeSelection(type) {
       
 19826 			bookmark = selection.getBookmark(type);
       
 19827 		}
       
 19828 
       
 19829 		function restoreSelection() {
       
 19830 			selection.moveToBookmark(bookmark);
       
 19831 		}
       
 19832 
       
 19833 		// Add execCommand overrides
       
 19834 		addCommands({
       
 19835 			// Ignore these, added for compatibility
       
 19836 			'mceResetDesignMode,mceBeginUndoLevel': function() {},
       
 19837 
       
 19838 			// Add undo manager logic
       
 19839 			'mceEndUndoLevel,mceAddUndoLevel': function() {
       
 19840 				editor.undoManager.add();
       
 19841 			},
       
 19842 
       
 19843 			'Cut,Copy,Paste': function(command) {
       
 19844 				var doc = editor.getDoc(), failed;
       
 19845 
       
 19846 				// Try executing the native command
       
 19847 				try {
       
 19848 					execNativeCommand(command);
       
 19849 				} catch (ex) {
       
 19850 					// Command failed
       
 19851 					failed = TRUE;
       
 19852 				}
       
 19853 
       
 19854 				// Present alert message about clipboard access not being available
       
 19855 				if (failed || !doc.queryCommandSupported(command)) {
       
 19856 					var msg = editor.translate(
       
 19857 						"Your browser doesn't support direct access to the clipboard. " +
       
 19858 						"Please use the Ctrl+X/C/V keyboard shortcuts instead."
       
 19859 					);
       
 19860 
       
 19861 					if (Env.mac) {
       
 19862 						msg = msg.replace(/Ctrl\+/g, '\u2318+');
       
 19863 					}
       
 19864 
       
 19865 					editor.windowManager.alert(msg);
       
 19866 				}
       
 19867 			},
       
 19868 
       
 19869 			// Override unlink command
       
 19870 			unlink: function() {
       
 19871 				if (selection.isCollapsed()) {
       
 19872 					var elm = selection.getNode();
       
 19873 					if (elm.tagName == 'A') {
       
 19874 						editor.dom.remove(elm, true);
       
 19875 					}
       
 19876 
       
 19877 					return;
       
 19878 				}
       
 19879 
       
 19880 				formatter.remove("link");
       
 19881 			},
       
 19882 
       
 19883 			// Override justify commands to use the text formatter engine
       
 19884 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
       
 19885 				var align = command.substring(7);
       
 19886 
       
 19887 				if (align == 'full') {
       
 19888 					align = 'justify';
       
 19889 				}
       
 19890 
       
 19891 				// Remove all other alignments first
       
 19892 				each('left,center,right,justify'.split(','), function(name) {
       
 19893 					if (align != name) {
       
 19894 						formatter.remove('align' + name);
       
 19895 					}
       
 19896 				});
       
 19897 
       
 19898 				toggleFormat('align' + align);
       
 19899 				execCommand('mceRepaint');
       
 19900 			},
       
 19901 
       
 19902 			// Override list commands to fix WebKit bug
       
 19903 			'InsertUnorderedList,InsertOrderedList': function(command) {
       
 19904 				var listElm, listParent;
       
 19905 
       
 19906 				execNativeCommand(command);
       
 19907 
       
 19908 				// WebKit produces lists within block elements so we need to split them
       
 19909 				// we will replace the native list creation logic to custom logic later on
       
 19910 				// TODO: Remove this when the list creation logic is removed
       
 19911 				listElm = dom.getParent(selection.getNode(), 'ol,ul');
       
 19912 				if (listElm) {
       
 19913 					listParent = listElm.parentNode;
       
 19914 
       
 19915 					// If list is within a text block then split that block
       
 19916 					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
       
 19917 						storeSelection();
       
 19918 						dom.split(listParent, listElm);
       
 19919 						restoreSelection();
       
 19920 					}
       
 19921 				}
       
 19922 			},
       
 19923 
       
 19924 			// Override commands to use the text formatter engine
       
 19925 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
       
 19926 				toggleFormat(command);
       
 19927 			},
       
 19928 
       
 19929 			// Override commands to use the text formatter engine
       
 19930 			'ForeColor,HiliteColor,FontName': function(command, ui, value) {
       
 19931 				toggleFormat(command, value);
       
 19932 			},
       
 19933 
       
 19934 			FontSize: function(command, ui, value) {
       
 19935 				var fontClasses, fontSizes;
       
 19936 
       
 19937 				// Convert font size 1-7 to styles
       
 19938 				if (value >= 1 && value <= 7) {
       
 19939 					fontSizes = explode(settings.font_size_style_values);
       
 19940 					fontClasses = explode(settings.font_size_classes);
       
 19941 
       
 19942 					if (fontClasses) {
       
 19943 						value = fontClasses[value - 1] || value;
       
 19944 					} else {
       
 19945 						value = fontSizes[value - 1] || value;
       
 19946 					}
       
 19947 				}
       
 19948 
       
 19949 				toggleFormat(command, value);
       
 19950 			},
       
 19951 
       
 19952 			RemoveFormat: function(command) {
       
 19953 				formatter.remove(command);
       
 19954 			},
       
 19955 
       
 19956 			mceBlockQuote: function() {
       
 19957 				toggleFormat('blockquote');
       
 19958 			},
       
 19959 
       
 19960 			FormatBlock: function(command, ui, value) {
       
 19961 				return toggleFormat(value || 'p');
       
 19962 			},
       
 19963 
       
 19964 			mceCleanup: function() {
       
 19965 				var bookmark = selection.getBookmark();
       
 19966 
       
 19967 				editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
       
 19968 
       
 19969 				selection.moveToBookmark(bookmark);
       
 19970 			},
       
 19971 
       
 19972 			mceRemoveNode: function(command, ui, value) {
       
 19973 				var node = value || selection.getNode();
       
 19974 
       
 19975 				// Make sure that the body node isn't removed
       
 19976 				if (node != editor.getBody()) {
       
 19977 					storeSelection();
       
 19978 					editor.dom.remove(node, TRUE);
       
 19979 					restoreSelection();
       
 19980 				}
       
 19981 			},
       
 19982 
       
 19983 			mceSelectNodeDepth: function(command, ui, value) {
       
 19984 				var counter = 0;
       
 19985 
       
 19986 				dom.getParent(selection.getNode(), function(node) {
       
 19987 					if (node.nodeType == 1 && counter++ == value) {
       
 19988 						selection.select(node);
       
 19989 						return FALSE;
       
 19990 					}
       
 19991 				}, editor.getBody());
       
 19992 			},
       
 19993 
       
 19994 			mceSelectNode: function(command, ui, value) {
       
 19995 				selection.select(value);
       
 19996 			},
       
 19997 
       
 19998 			mceInsertContent: function(command, ui, value) {
       
 19999 				var parser, serializer, parentNode, rootNode, fragment, args;
       
 20000 				var marker, rng, node, node2, bookmarkHtml, merge;
       
 20001 				var textInlineElements = editor.schema.getTextInlineElements();
       
 20002 
       
 20003 				function trimOrPaddLeftRight(html) {
       
 20004 					var rng, container, offset;
       
 20005 
       
 20006 					rng = selection.getRng(true);
       
 20007 					container = rng.startContainer;
       
 20008 					offset = rng.startOffset;
       
 20009 
       
 20010 					function hasSiblingText(siblingName) {
       
 20011 						return container[siblingName] && container[siblingName].nodeType == 3;
       
 20012 					}
       
 20013 
       
 20014 					if (container.nodeType == 3) {
       
 20015 						if (offset > 0) {
       
 20016 							html = html.replace(/^&nbsp;/, ' ');
       
 20017 						} else if (!hasSiblingText('previousSibling')) {
       
 20018 							html = html.replace(/^ /, '&nbsp;');
       
 20019 						}
       
 20020 
       
 20021 						if (offset < container.length) {
       
 20022 							html = html.replace(/&nbsp;(<br>|)$/, ' ');
       
 20023 						} else if (!hasSiblingText('nextSibling')) {
       
 20024 							html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
       
 20025 						}
       
 20026 					}
       
 20027 
       
 20028 					return html;
       
 20029 				}
       
 20030 
       
 20031 				// Removes &nbsp; from a [b] c -> a &nbsp;c -> a c
       
 20032 				function trimNbspAfterDeleteAndPaddValue() {
       
 20033 					var rng, container, offset;
       
 20034 
       
 20035 					rng = selection.getRng(true);
       
 20036 					container = rng.startContainer;
       
 20037 					offset = rng.startOffset;
       
 20038 
       
 20039 					if (container.nodeType == 3 && rng.collapsed) {
       
 20040 						if (container.data[offset] === '\u00a0') {
       
 20041 							container.deleteData(offset, 1);
       
 20042 
       
 20043 							if (!/[\u00a0| ]$/.test(value)) {
       
 20044 								value += ' ';
       
 20045 							}
       
 20046 						} else if (container.data[offset - 1] === '\u00a0') {
       
 20047 							container.deleteData(offset - 1, 1);
       
 20048 
       
 20049 							if (!/[\u00a0| ]$/.test(value)) {
       
 20050 								value = ' ' + value;
       
 20051 							}
       
 20052 						}
       
 20053 					}
       
 20054 				}
       
 20055 
       
 20056 				function markInlineFormatElements(fragment) {
       
 20057 					if (merge) {
       
 20058 						for (node = fragment.firstChild; node; node = node.walk(true)) {
       
 20059 							if (textInlineElements[node.name]) {
       
 20060 								node.attr('data-mce-new', "true");
       
 20061 							}
       
 20062 						}
       
 20063 					}
       
 20064 				}
       
 20065 
       
 20066 				function reduceInlineTextElements() {
       
 20067 					if (merge) {
       
 20068 						var root = editor.getBody(), elementUtils = new ElementUtils(dom);
       
 20069 
       
 20070 						each(dom.select('*[data-mce-new]'), function(node) {
       
 20071 							node.removeAttribute('data-mce-new');
       
 20072 
       
 20073 							for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
       
 20074 								if (elementUtils.compare(testNode, node)) {
       
 20075 									dom.remove(node, true);
       
 20076 								}
       
 20077 							}
       
 20078 						});
       
 20079 					}
       
 20080 				}
       
 20081 
       
 20082 				if (typeof value != 'string') {
       
 20083 					merge = value.merge;
       
 20084 					value = value.content;
       
 20085 				}
       
 20086 
       
 20087 				// Check for whitespace before/after value
       
 20088 				if (/^ | $/.test(value)) {
       
 20089 					value = trimOrPaddLeftRight(value);
       
 20090 				}
       
 20091 
       
 20092 				// Setup parser and serializer
       
 20093 				parser = editor.parser;
       
 20094 				serializer = new Serializer({}, editor.schema);
       
 20095 				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>';
       
 20096 
       
 20097 				// Run beforeSetContent handlers on the HTML to be inserted
       
 20098 				args = {content: value, format: 'html', selection: true};
       
 20099 				editor.fire('BeforeSetContent', args);
       
 20100 				value = args.content;
       
 20101 
       
 20102 				// Add caret at end of contents if it's missing
       
 20103 				if (value.indexOf('{$caret}') == -1) {
       
 20104 					value += '{$caret}';
       
 20105 				}
       
 20106 
       
 20107 				// Replace the caret marker with a span bookmark element
       
 20108 				value = value.replace(/\{\$caret\}/, bookmarkHtml);
       
 20109 
       
 20110 				// If selection is at <body>|<p></p> then move it into <body><p>|</p>
       
 20111 				rng = selection.getRng();
       
 20112 				var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
       
 20113 				var body = editor.getBody();
       
 20114 				if (caretElement === body && selection.isCollapsed()) {
       
 20115 					if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
       
 20116 						rng = dom.createRng();
       
 20117 						rng.setStart(body.firstChild, 0);
       
 20118 						rng.setEnd(body.firstChild, 0);
       
 20119 						selection.setRng(rng);
       
 20120 					}
       
 20121 				}
       
 20122 
       
 20123 				// Insert node maker where we will insert the new HTML and get it's parent
       
 20124 				if (!selection.isCollapsed()) {
       
 20125 					editor.getDoc().execCommand('Delete', false, null);
       
 20126 					trimNbspAfterDeleteAndPaddValue();
       
 20127 				}
       
 20128 
       
 20129 				parentNode = selection.getNode();
       
 20130 
       
 20131 				// Parse the fragment within the context of the parent node
       
 20132 				var parserArgs = {context: parentNode.nodeName.toLowerCase()};
       
 20133 				fragment = parser.parse(value, parserArgs);
       
 20134 
       
 20135 				markInlineFormatElements(fragment);
       
 20136 
       
 20137 				// Move the caret to a more suitable location
       
 20138 				node = fragment.lastChild;
       
 20139 				if (node.attr('id') == 'mce_marker') {
       
 20140 					marker = node;
       
 20141 
       
 20142 					for (node = node.prev; node; node = node.walk(true)) {
       
 20143 						if (node.type == 3 || !dom.isBlock(node.name)) {
       
 20144 							if (editor.schema.isValidChild(node.parent.name, 'span')) {
       
 20145 								node.parent.insert(marker, node, node.name === 'br');
       
 20146 							}
       
 20147 							break;
       
 20148 						}
       
 20149 					}
       
 20150 				}
       
 20151 
       
 20152 				// If parser says valid we can insert the contents into that parent
       
 20153 				if (!parserArgs.invalid) {
       
 20154 					value = serializer.serialize(fragment);
       
 20155 
       
 20156 					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
       
 20157 					node = parentNode.firstChild;
       
 20158 					node2 = parentNode.lastChild;
       
 20159 					if (!node || (node === node2 && node.nodeName === 'BR')) {
       
 20160 						dom.setHTML(parentNode, value);
       
 20161 					} else {
       
 20162 						selection.setContent(value);
       
 20163 					}
       
 20164 				} else {
       
 20165 					// If the fragment was invalid within that context then we need
       
 20166 					// to parse and process the parent it's inserted into
       
 20167 
       
 20168 					// Insert bookmark node and get the parent
       
 20169 					selection.setContent(bookmarkHtml);
       
 20170 					parentNode = selection.getNode();
       
 20171 					rootNode = editor.getBody();
       
 20172 
       
 20173 					// Opera will return the document node when selection is in root
       
 20174 					if (parentNode.nodeType == 9) {
       
 20175 						parentNode = node = rootNode;
       
 20176 					} else {
       
 20177 						node = parentNode;
       
 20178 					}
       
 20179 
       
 20180 					// Find the ancestor just before the root element
       
 20181 					while (node !== rootNode) {
       
 20182 						parentNode = node;
       
 20183 						node = node.parentNode;
       
 20184 					}
       
 20185 
       
 20186 					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
       
 20187 					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
       
 20188 					value = serializer.serialize(
       
 20189 						parser.parse(
       
 20190 							// Need to replace by using a function since $ in the contents would otherwise be a problem
       
 20191 							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
       
 20192 								return serializer.serialize(fragment);
       
 20193 							})
       
 20194 						)
       
 20195 					);
       
 20196 
       
 20197 					// Set the inner/outer HTML depending on if we are in the root or not
       
 20198 					if (parentNode == rootNode) {
       
 20199 						dom.setHTML(rootNode, value);
       
 20200 					} else {
       
 20201 						dom.setOuterHTML(parentNode, value);
       
 20202 					}
       
 20203 				}
       
 20204 
       
 20205 				reduceInlineTextElements();
       
 20206 
       
 20207 				marker = dom.get('mce_marker');
       
 20208 				selection.scrollIntoView(marker);
       
 20209 
       
 20210 				// Move selection before marker and remove it
       
 20211 				rng = dom.createRng();
       
 20212 
       
 20213 				// If previous sibling is a text node set the selection to the end of that node
       
 20214 				node = marker.previousSibling;
       
 20215 				if (node && node.nodeType == 3) {
       
 20216 					rng.setStart(node, node.nodeValue.length);
       
 20217 
       
 20218 					// TODO: Why can't we normalize on IE
       
 20219 					if (!isIE) {
       
 20220 						node2 = marker.nextSibling;
       
 20221 						if (node2 && node2.nodeType == 3) {
       
 20222 							node.appendData(node2.data);
       
 20223 							node2.parentNode.removeChild(node2);
       
 20224 						}
       
 20225 					}
       
 20226 				} else {
       
 20227 					// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
       
 20228 					rng.setStartBefore(marker);
       
 20229 					rng.setEndBefore(marker);
       
 20230 				}
       
 20231 
       
 20232 				// Remove the marker node and set the new range
       
 20233 				dom.remove(marker);
       
 20234 				selection.setRng(rng);
       
 20235 
       
 20236 				// Dispatch after event and add any visual elements needed
       
 20237 				editor.fire('SetContent', args);
       
 20238 				editor.addVisual();
       
 20239 			},
       
 20240 
       
 20241 			mceInsertRawHTML: function(command, ui, value) {
       
 20242 				selection.setContent('tiny_mce_marker');
       
 20243 				editor.setContent(
       
 20244 					editor.getContent().replace(/tiny_mce_marker/g, function() {
       
 20245 						return value;
       
 20246 					})
       
 20247 				);
       
 20248 			},
       
 20249 
       
 20250 			mceToggleFormat: function(command, ui, value) {
       
 20251 				toggleFormat(value);
       
 20252 			},
       
 20253 
       
 20254 			mceSetContent: function(command, ui, value) {
       
 20255 				editor.setContent(value);
       
 20256 			},
       
 20257 
       
 20258 			'Indent,Outdent': function(command) {
       
 20259 				var intentValue, indentUnit, value;
       
 20260 
       
 20261 				// Setup indent level
       
 20262 				intentValue = settings.indentation;
       
 20263 				indentUnit = /[a-z%]+$/i.exec(intentValue);
       
 20264 				intentValue = parseInt(intentValue, 10);
       
 20265 
       
 20266 				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
       
 20267 					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
       
 20268 					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
       
 20269 						formatter.apply('div');
       
 20270 					}
       
 20271 
       
 20272 					each(selection.getSelectedBlocks(), function(element) {
       
 20273 						if (element.nodeName != "LI") {
       
 20274 							var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding';
       
 20275 
       
 20276 							indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left';
       
 20277 
       
 20278 							if (command == 'outdent') {
       
 20279 								value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
       
 20280 								dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
       
 20281 							} else {
       
 20282 								value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
       
 20283 								dom.setStyle(element, indentStyleName, value);
       
 20284 							}
       
 20285 						}
       
 20286 					});
       
 20287 				} else {
       
 20288 					execNativeCommand(command);
       
 20289 				}
       
 20290 			},
       
 20291 
       
 20292 			mceRepaint: function() {
       
 20293 				if (isGecko) {
       
 20294 					try {
       
 20295 						storeSelection(TRUE);
       
 20296 
       
 20297 						if (selection.getSel()) {
       
 20298 							selection.getSel().selectAllChildren(editor.getBody());
       
 20299 						}
       
 20300 
       
 20301 						selection.collapse(TRUE);
       
 20302 						restoreSelection();
       
 20303 					} catch (ex) {
       
 20304 						// Ignore
       
 20305 					}
       
 20306 				}
       
 20307 			},
       
 20308 
       
 20309 			InsertHorizontalRule: function() {
       
 20310 				editor.execCommand('mceInsertContent', false, '<hr />');
       
 20311 			},
       
 20312 
       
 20313 			mceToggleVisualAid: function() {
       
 20314 				editor.hasVisual = !editor.hasVisual;
       
 20315 				editor.addVisual();
       
 20316 			},
       
 20317 
       
 20318 			mceReplaceContent: function(command, ui, value) {
       
 20319 				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
       
 20320 			},
       
 20321 
       
 20322 			mceInsertLink: function(command, ui, value) {
       
 20323 				var anchor;
       
 20324 
       
 20325 				if (typeof value == 'string') {
       
 20326 					value = {href: value};
       
 20327 				}
       
 20328 
       
 20329 				anchor = dom.getParent(selection.getNode(), 'a');
       
 20330 
       
 20331 				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
       
 20332 				value.href = value.href.replace(' ', '%20');
       
 20333 
       
 20334 				// Remove existing links if there could be child links or that the href isn't specified
       
 20335 				if (!anchor || !value.href) {
       
 20336 					formatter.remove('link');
       
 20337 				}
       
 20338 
       
 20339 				// Apply new link to selection
       
 20340 				if (value.href) {
       
 20341 					formatter.apply('link', value, anchor);
       
 20342 				}
       
 20343 			},
       
 20344 
       
 20345 			selectAll: function() {
       
 20346 				var root = dom.getRoot(), rng;
       
 20347 
       
 20348 				if (selection.getRng().setStart) {
       
 20349 					rng = dom.createRng();
       
 20350 					rng.setStart(root, 0);
       
 20351 					rng.setEnd(root, root.childNodes.length);
       
 20352 					selection.setRng(rng);
       
 20353 				} else {
       
 20354 					// IE will render it's own root level block elements and sometimes
       
 20355 					// even put font elements in them when the user starts typing. So we need to
       
 20356 					// move the selection to a more suitable element from this:
       
 20357 					// <body>|<p></p></body> to this: <body><p>|</p></body>
       
 20358 					rng = selection.getRng();
       
 20359 					if (!rng.item) {
       
 20360 						rng.moveToElementText(root);
       
 20361 						rng.select();
       
 20362 					}
       
 20363 				}
       
 20364 			},
       
 20365 
       
 20366 			"delete": function() {
       
 20367 				execNativeCommand("Delete");
       
 20368 
       
 20369 				// Check if body is empty after the delete call if so then set the contents
       
 20370 				// to an empty string and move the caret to any block produced by that operation
       
 20371 				// this fixes the issue with root blocks not being properly produced after a delete call on IE
       
 20372 				var body = editor.getBody();
       
 20373 
       
 20374 				if (dom.isEmpty(body)) {
       
 20375 					editor.setContent('');
       
 20376 
       
 20377 					if (body.firstChild && dom.isBlock(body.firstChild)) {
       
 20378 						editor.selection.setCursorLocation(body.firstChild, 0);
       
 20379 					} else {
       
 20380 						editor.selection.setCursorLocation(body, 0);
       
 20381 					}
       
 20382 				}
       
 20383 			},
       
 20384 
       
 20385 			mceNewDocument: function() {
       
 20386 				editor.setContent('');
       
 20387 			},
       
 20388 
       
 20389 			InsertLineBreak: function(command, ui, value) {
       
 20390 				// We load the current event in from EnterKey.js when appropriate to heed
       
 20391 				// certain event-specific variations such as ctrl-enter in a list
       
 20392 				var evt = value;
       
 20393 				var brElm, extraBr, marker;
       
 20394 				var rng = selection.getRng(true);
       
 20395 				new RangeUtils(dom).normalize(rng);
       
 20396 
       
 20397 				var offset = rng.startOffset;
       
 20398 				var container = rng.startContainer;
       
 20399 
       
 20400 				// Resolve node index
       
 20401 				if (container.nodeType == 1 && container.hasChildNodes()) {
       
 20402 					var isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
       
 20403 
       
 20404 					container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
       
 20405 					if (isAfterLastNodeInContainer && container.nodeType == 3) {
       
 20406 						offset = container.nodeValue.length;
       
 20407 					} else {
       
 20408 						offset = 0;
       
 20409 					}
       
 20410 				}
       
 20411 
       
 20412 				var parentBlock = dom.getParent(container, dom.isBlock);
       
 20413 				var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 20414 				var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
       
 20415 				var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 20416 
       
 20417 				// Enter inside block contained within a LI then split or insert before/after LI
       
 20418 				var isControlKey = evt && evt.ctrlKey;
       
 20419 				if (containerBlockName == 'LI' && !isControlKey) {
       
 20420 					parentBlock = containerBlock;
       
 20421 					parentBlockName = containerBlockName;
       
 20422 				}
       
 20423 
       
 20424 				// Walks the parent block to the right and look for BR elements
       
 20425 				function hasRightSideContent() {
       
 20426 					var walker = new TreeWalker(container, parentBlock), node;
       
 20427 					var nonEmptyElementsMap = editor.schema.getNonEmptyElements();
       
 20428 
       
 20429 					while ((node = walker.next())) {
       
 20430 						if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
       
 20431 							return true;
       
 20432 						}
       
 20433 					}
       
 20434 				}
       
 20435 
       
 20436 				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
       
 20437 					// Insert extra BR element at the end block elements
       
 20438 					if (!isOldIE && !hasRightSideContent()) {
       
 20439 						brElm = dom.create('br');
       
 20440 						rng.insertNode(brElm);
       
 20441 						rng.setStartAfter(brElm);
       
 20442 						rng.setEndAfter(brElm);
       
 20443 						extraBr = true;
       
 20444 					}
       
 20445 				}
       
 20446 
       
 20447 				brElm = dom.create('br');
       
 20448 				rng.insertNode(brElm);
       
 20449 
       
 20450 				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
       
 20451 				var documentMode = dom.doc.documentMode;
       
 20452 				if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
       
 20453 					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
       
 20454 				}
       
 20455 
       
 20456 				// Insert temp marker and scroll to that
       
 20457 				marker = dom.create('span', {}, '&nbsp;');
       
 20458 				brElm.parentNode.insertBefore(marker, brElm);
       
 20459 				selection.scrollIntoView(marker);
       
 20460 				dom.remove(marker);
       
 20461 
       
 20462 				if (!extraBr) {
       
 20463 					rng.setStartAfter(brElm);
       
 20464 					rng.setEndAfter(brElm);
       
 20465 				} else {
       
 20466 					rng.setStartBefore(brElm);
       
 20467 					rng.setEndBefore(brElm);
       
 20468 				}
       
 20469 
       
 20470 				selection.setRng(rng);
       
 20471 				editor.undoManager.add();
       
 20472 
       
 20473 				return TRUE;
       
 20474 			}
       
 20475 		});
       
 20476 
       
 20477 		// Add queryCommandState overrides
       
 20478 		addCommands({
       
 20479 			// Override justify commands
       
 20480 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
       
 20481 				var name = 'align' + command.substring(7);
       
 20482 				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
       
 20483 				var matches = map(nodes, function(node) {
       
 20484 					return !!formatter.matchNode(node, name);
       
 20485 				});
       
 20486 				return inArray(matches, TRUE) !== -1;
       
 20487 			},
       
 20488 
       
 20489 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
       
 20490 				return isFormatMatch(command);
       
 20491 			},
       
 20492 
       
 20493 			mceBlockQuote: function() {
       
 20494 				return isFormatMatch('blockquote');
       
 20495 			},
       
 20496 
       
 20497 			Outdent: function() {
       
 20498 				var node;
       
 20499 
       
 20500 				if (settings.inline_styles) {
       
 20501 					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
       
 20502 						return TRUE;
       
 20503 					}
       
 20504 
       
 20505 					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
       
 20506 						return TRUE;
       
 20507 					}
       
 20508 				}
       
 20509 
       
 20510 				return (
       
 20511 					queryCommandState('InsertUnorderedList') ||
       
 20512 					queryCommandState('InsertOrderedList') ||
       
 20513 					(!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
       
 20514 				);
       
 20515 			},
       
 20516 
       
 20517 			'InsertUnorderedList,InsertOrderedList': function(command) {
       
 20518 				var list = dom.getParent(selection.getNode(), 'ul,ol');
       
 20519 
       
 20520 				return list &&
       
 20521 					(
       
 20522 						command === 'insertunorderedlist' && list.tagName === 'UL' ||
       
 20523 						command === 'insertorderedlist' && list.tagName === 'OL'
       
 20524 					);
       
 20525 			}
       
 20526 		}, 'state');
       
 20527 
       
 20528 		// Add queryCommandValue overrides
       
 20529 		addCommands({
       
 20530 			'FontSize,FontName': function(command) {
       
 20531 				var value = 0, parent;
       
 20532 
       
 20533 				if ((parent = dom.getParent(selection.getNode(), 'span'))) {
       
 20534 					if (command == 'fontsize') {
       
 20535 						value = parent.style.fontSize;
       
 20536 					} else {
       
 20537 						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
       
 20538 					}
       
 20539 				}
       
 20540 
       
 20541 				return value;
       
 20542 			}
       
 20543 		}, 'value');
       
 20544 
       
 20545 		// Add undo manager logic
       
 20546 		addCommands({
       
 20547 			Undo: function() {
       
 20548 				editor.undoManager.undo();
       
 20549 			},
       
 20550 
       
 20551 			Redo: function() {
       
 20552 				editor.undoManager.redo();
       
 20553 			}
       
 20554 		});
       
 20555 	};
       
 20556 });
       
 20557 
       
 20558 // Included from: js/tinymce/classes/util/URI.js
       
 20559 
       
 20560 /**
       
 20561  * URI.js
       
 20562  *
       
 20563  * Copyright, Moxiecode Systems AB
       
 20564  * Released under LGPL License.
       
 20565  *
       
 20566  * License: http://www.tinymce.com/license
       
 20567  * Contributing: http://www.tinymce.com/contributing
       
 20568  */
       
 20569 
       
 20570 /**
       
 20571  * This class handles parsing, modification and serialization of URI/URL strings.
       
 20572  * @class tinymce.util.URI
       
 20573  */
       
 20574 define("tinymce/util/URI", [
       
 20575 	"tinymce/util/Tools"
       
 20576 ], function(Tools) {
       
 20577 	var each = Tools.each, trim = Tools.trim;
       
 20578 	var queryParts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(' ');
       
 20579 	var DEFAULT_PORTS = {
       
 20580 		'ftp': 21,
       
 20581 		'http': 80,
       
 20582 		'https': 443,
       
 20583 		'mailto': 25
       
 20584 	};
       
 20585 
       
 20586 	/**
       
 20587 	 * Constructs a new URI instance.
       
 20588 	 *
       
 20589 	 * @constructor
       
 20590 	 * @method URI
       
 20591 	 * @param {String} url URI string to parse.
       
 20592 	 * @param {Object} settings Optional settings object.
       
 20593 	 */
       
 20594 	function URI(url, settings) {
       
 20595 		var self = this, baseUri, base_url;
       
 20596 
       
 20597 		url = trim(url);
       
 20598 		settings = self.settings = settings || {};
       
 20599 		baseUri = settings.base_uri;
       
 20600 
       
 20601 		// Strange app protocol that isn't http/https or local anchor
       
 20602 		// For example: mailto,skype,tel etc.
       
 20603 		if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
       
 20604 			self.source = url;
       
 20605 			return;
       
 20606 		}
       
 20607 
       
 20608 		var isProtocolRelative = url.indexOf('//') === 0;
       
 20609 
       
 20610 		// Absolute path with no host, fake host and protocol
       
 20611 		if (url.indexOf('/') === 0 && !isProtocolRelative) {
       
 20612 			url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url;
       
 20613 		}
       
 20614 
       
 20615 		// Relative path http:// or protocol relative //path
       
 20616 		if (!/^[\w\-]*:?\/\//.test(url)) {
       
 20617 			base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
       
 20618 			if (settings.base_uri.protocol === "") {
       
 20619 				url = '//mce_host' + self.toAbsPath(base_url, url);
       
 20620 			} else {
       
 20621 				url = /([^#?]*)([#?]?.*)/.exec(url);
       
 20622 				url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url[1]) + url[2];
       
 20623 			}
       
 20624 		}
       
 20625 
       
 20626 		// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
       
 20627 		url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
       
 20628 
       
 20629 		/*jshint maxlen: 255 */
       
 20630 		/*eslint max-len: 0 */
       
 20631 		url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
       
 20632 
       
 20633 		each(queryParts, function(v, i) {
       
 20634 			var part = url[i];
       
 20635 
       
 20636 			// Zope 3 workaround, they use @@something
       
 20637 			if (part) {
       
 20638 				part = part.replace(/\(mce_at\)/g, '@@');
       
 20639 			}
       
 20640 
       
 20641 			self[v] = part;
       
 20642 		});
       
 20643 
       
 20644 		if (baseUri) {
       
 20645 			if (!self.protocol) {
       
 20646 				self.protocol = baseUri.protocol;
       
 20647 			}
       
 20648 
       
 20649 			if (!self.userInfo) {
       
 20650 				self.userInfo = baseUri.userInfo;
       
 20651 			}
       
 20652 
       
 20653 			if (!self.port && self.host === 'mce_host') {
       
 20654 				self.port = baseUri.port;
       
 20655 			}
       
 20656 
       
 20657 			if (!self.host || self.host === 'mce_host') {
       
 20658 				self.host = baseUri.host;
       
 20659 			}
       
 20660 
       
 20661 			self.source = '';
       
 20662 		}
       
 20663 
       
 20664 		if (isProtocolRelative) {
       
 20665 			self.protocol = '';
       
 20666 		}
       
 20667 
       
 20668 		//t.path = t.path || '/';
       
 20669 	}
       
 20670 
       
 20671 	URI.prototype = {
       
 20672 		/**
       
 20673 		 * Sets the internal path part of the URI.
       
 20674 		 *
       
 20675 		 * @method setPath
       
 20676 		 * @param {string} path Path string to set.
       
 20677 		 */
       
 20678 		setPath: function(path) {
       
 20679 			var self = this;
       
 20680 
       
 20681 			path = /^(.*?)\/?(\w+)?$/.exec(path);
       
 20682 
       
 20683 			// Update path parts
       
 20684 			self.path = path[0];
       
 20685 			self.directory = path[1];
       
 20686 			self.file = path[2];
       
 20687 
       
 20688 			// Rebuild source
       
 20689 			self.source = '';
       
 20690 			self.getURI();
       
 20691 		},
       
 20692 
       
 20693 		/**
       
 20694 		 * Converts the specified URI into a relative URI based on the current URI instance location.
       
 20695 		 *
       
 20696 		 * @method toRelative
       
 20697 		 * @param {String} uri URI to convert into a relative path/URI.
       
 20698 		 * @return {String} Relative URI from the point specified in the current URI instance.
       
 20699 		 * @example
       
 20700 		 * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
       
 20701 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
       
 20702 		 */
       
 20703 		toRelative: function(uri) {
       
 20704 			var self = this, output;
       
 20705 
       
 20706 			if (uri === "./") {
       
 20707 				return uri;
       
 20708 			}
       
 20709 
       
 20710 			uri = new URI(uri, {base_uri: self});
       
 20711 
       
 20712 			// Not on same domain/port or protocol
       
 20713 			if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
       
 20714 				(self.protocol != uri.protocol && uri.protocol !== "")) {
       
 20715 				return uri.getURI();
       
 20716 			}
       
 20717 
       
 20718 			var tu = self.getURI(), uu = uri.getURI();
       
 20719 
       
 20720 			// Allow usage of the base_uri when relative_urls = true
       
 20721 			if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
       
 20722 				return tu;
       
 20723 			}
       
 20724 
       
 20725 			output = self.toRelPath(self.path, uri.path);
       
 20726 
       
 20727 			// Add query
       
 20728 			if (uri.query) {
       
 20729 				output += '?' + uri.query;
       
 20730 			}
       
 20731 
       
 20732 			// Add anchor
       
 20733 			if (uri.anchor) {
       
 20734 				output += '#' + uri.anchor;
       
 20735 			}
       
 20736 
       
 20737 			return output;
       
 20738 		},
       
 20739 
       
 20740 		/**
       
 20741 		 * Converts the specified URI into a absolute URI based on the current URI instance location.
       
 20742 		 *
       
 20743 		 * @method toAbsolute
       
 20744 		 * @param {String} uri URI to convert into a relative path/URI.
       
 20745 		 * @param {Boolean} noHost No host and protocol prefix.
       
 20746 		 * @return {String} Absolute URI from the point specified in the current URI instance.
       
 20747 		 * @example
       
 20748 		 * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
       
 20749 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
       
 20750 		 */
       
 20751 		toAbsolute: function(uri, noHost) {
       
 20752 			uri = new URI(uri, {base_uri: this});
       
 20753 
       
 20754 			return uri.getURI(noHost && this.isSameOrigin(uri));
       
 20755 		},
       
 20756 
       
 20757 		/**
       
 20758 		 * Determine whether the given URI has the same origin as this URI.  Based on RFC-6454.
       
 20759 		 * Supports default ports for protocols listed in DEFAULT_PORTS.  Unsupported protocols will fail safe: they
       
 20760 		 * won't match, if the port specifications differ.
       
 20761 		 *
       
 20762 		 * @method isSameOrigin
       
 20763 		 * @param {tinymce.util.URI} uri Uri instance to compare.
       
 20764 		 * @returns {Boolean} True if the origins are the same.
       
 20765 		 */
       
 20766 		isSameOrigin: function(uri) {
       
 20767 			if (this.host == uri.host && this.protocol == uri.protocol) {
       
 20768 				if (this.port == uri.port) {
       
 20769 					return true;
       
 20770 				}
       
 20771 
       
 20772 				var defaultPort = DEFAULT_PORTS[this.protocol];
       
 20773 				if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) {
       
 20774 					return true;
       
 20775 				}
       
 20776 			}
       
 20777 
       
 20778 			return false;
       
 20779 		},
       
 20780 
       
 20781 		/**
       
 20782 		 * Converts a absolute path into a relative path.
       
 20783 		 *
       
 20784 		 * @method toRelPath
       
 20785 		 * @param {String} base Base point to convert the path from.
       
 20786 		 * @param {String} path Absolute path to convert into a relative path.
       
 20787 		 */
       
 20788 		toRelPath: function(base, path) {
       
 20789 			var items, breakPoint = 0, out = '', i, l;
       
 20790 
       
 20791 			// Split the paths
       
 20792 			base = base.substring(0, base.lastIndexOf('/'));
       
 20793 			base = base.split('/');
       
 20794 			items = path.split('/');
       
 20795 
       
 20796 			if (base.length >= items.length) {
       
 20797 				for (i = 0, l = base.length; i < l; i++) {
       
 20798 					if (i >= items.length || base[i] != items[i]) {
       
 20799 						breakPoint = i + 1;
       
 20800 						break;
       
 20801 					}
       
 20802 				}
       
 20803 			}
       
 20804 
       
 20805 			if (base.length < items.length) {
       
 20806 				for (i = 0, l = items.length; i < l; i++) {
       
 20807 					if (i >= base.length || base[i] != items[i]) {
       
 20808 						breakPoint = i + 1;
       
 20809 						break;
       
 20810 					}
       
 20811 				}
       
 20812 			}
       
 20813 
       
 20814 			if (breakPoint === 1) {
       
 20815 				return path;
       
 20816 			}
       
 20817 
       
 20818 			for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) {
       
 20819 				out += "../";
       
 20820 			}
       
 20821 
       
 20822 			for (i = breakPoint - 1, l = items.length; i < l; i++) {
       
 20823 				if (i != breakPoint - 1) {
       
 20824 					out += "/" + items[i];
       
 20825 				} else {
       
 20826 					out += items[i];
       
 20827 				}
       
 20828 			}
       
 20829 
       
 20830 			return out;
       
 20831 		},
       
 20832 
       
 20833 		/**
       
 20834 		 * Converts a relative path into a absolute path.
       
 20835 		 *
       
 20836 		 * @method toAbsPath
       
 20837 		 * @param {String} base Base point to convert the path from.
       
 20838 		 * @param {String} path Relative path to convert into an absolute path.
       
 20839 		 */
       
 20840 		toAbsPath: function(base, path) {
       
 20841 			var i, nb = 0, o = [], tr, outPath;
       
 20842 
       
 20843 			// Split paths
       
 20844 			tr = /\/$/.test(path) ? '/' : '';
       
 20845 			base = base.split('/');
       
 20846 			path = path.split('/');
       
 20847 
       
 20848 			// Remove empty chunks
       
 20849 			each(base, function(k) {
       
 20850 				if (k) {
       
 20851 					o.push(k);
       
 20852 				}
       
 20853 			});
       
 20854 
       
 20855 			base = o;
       
 20856 
       
 20857 			// Merge relURLParts chunks
       
 20858 			for (i = path.length - 1, o = []; i >= 0; i--) {
       
 20859 				// Ignore empty or .
       
 20860 				if (path[i].length === 0 || path[i] === ".") {
       
 20861 					continue;
       
 20862 				}
       
 20863 
       
 20864 				// Is parent
       
 20865 				if (path[i] === '..') {
       
 20866 					nb++;
       
 20867 					continue;
       
 20868 				}
       
 20869 
       
 20870 				// Move up
       
 20871 				if (nb > 0) {
       
 20872 					nb--;
       
 20873 					continue;
       
 20874 				}
       
 20875 
       
 20876 				o.push(path[i]);
       
 20877 			}
       
 20878 
       
 20879 			i = base.length - nb;
       
 20880 
       
 20881 			// If /a/b/c or /
       
 20882 			if (i <= 0) {
       
 20883 				outPath = o.reverse().join('/');
       
 20884 			} else {
       
 20885 				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
       
 20886 			}
       
 20887 
       
 20888 			// Add front / if it's needed
       
 20889 			if (outPath.indexOf('/') !== 0) {
       
 20890 				outPath = '/' + outPath;
       
 20891 			}
       
 20892 
       
 20893 			// Add traling / if it's needed
       
 20894 			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {
       
 20895 				outPath += tr;
       
 20896 			}
       
 20897 
       
 20898 			return outPath;
       
 20899 		},
       
 20900 
       
 20901 		/**
       
 20902 		 * Returns the full URI of the internal structure.
       
 20903 		 *
       
 20904 		 * @method getURI
       
 20905 		 * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
       
 20906 		 */
       
 20907 		getURI: function(noProtoHost) {
       
 20908 			var s, self = this;
       
 20909 
       
 20910 			// Rebuild source
       
 20911 			if (!self.source || noProtoHost) {
       
 20912 				s = '';
       
 20913 
       
 20914 				if (!noProtoHost) {
       
 20915 					if (self.protocol) {
       
 20916 						s += self.protocol + '://';
       
 20917 					} else {
       
 20918 						s += '//';
       
 20919 					}
       
 20920 
       
 20921 					if (self.userInfo) {
       
 20922 						s += self.userInfo + '@';
       
 20923 					}
       
 20924 
       
 20925 					if (self.host) {
       
 20926 						s += self.host;
       
 20927 					}
       
 20928 
       
 20929 					if (self.port) {
       
 20930 						s += ':' + self.port;
       
 20931 					}
       
 20932 				}
       
 20933 
       
 20934 				if (self.path) {
       
 20935 					s += self.path;
       
 20936 				}
       
 20937 
       
 20938 				if (self.query) {
       
 20939 					s += '?' + self.query;
       
 20940 				}
       
 20941 
       
 20942 				if (self.anchor) {
       
 20943 					s += '#' + self.anchor;
       
 20944 				}
       
 20945 
       
 20946 				self.source = s;
       
 20947 			}
       
 20948 
       
 20949 			return self.source;
       
 20950 		}
       
 20951 	};
       
 20952 
       
 20953 	return URI;
       
 20954 });
       
 20955 
       
 20956 // Included from: js/tinymce/classes/util/Class.js
       
 20957 
       
 20958 /**
       
 20959  * Class.js
       
 20960  *
       
 20961  * Copyright 2003-2012, Moxiecode Systems AB, All rights reserved.
       
 20962  */
       
 20963 
       
 20964 /**
       
 20965  * This utilitiy class is used for easier inheritage.
       
 20966  *
       
 20967  * Features:
       
 20968  * * Exposed super functions: this._super();
       
 20969  * * Mixins
       
 20970  * * Dummy functions
       
 20971  * * Property functions: var value = object.value(); and object.value(newValue);
       
 20972  * * Static functions
       
 20973  * * Defaults settings
       
 20974  */
       
 20975 define("tinymce/util/Class", [
       
 20976 	"tinymce/util/Tools"
       
 20977 ], function(Tools) {
       
 20978 	var each = Tools.each, extend = Tools.extend;
       
 20979 
       
 20980 	var extendClass, initializing;
       
 20981 
       
 20982 	function Class() {
       
 20983 	}
       
 20984 
       
 20985 	// Provides classical inheritance, based on code made by John Resig
       
 20986 	Class.extend = extendClass = function(prop) {
       
 20987 		var self = this, _super = self.prototype, prototype, name, member;
       
 20988 
       
 20989 		// The dummy class constructor
       
 20990 		function Class() {
       
 20991 			var i, mixins, mixin, self = this;
       
 20992 
       
 20993 			// All construction is actually done in the init method
       
 20994 			if (!initializing) {
       
 20995 				// Run class constuctor
       
 20996 				if (self.init) {
       
 20997 					self.init.apply(self, arguments);
       
 20998 				}
       
 20999 
       
 21000 				// Run mixin constructors
       
 21001 				mixins = self.Mixins;
       
 21002 				if (mixins) {
       
 21003 					i = mixins.length;
       
 21004 					while (i--) {
       
 21005 						mixin = mixins[i];
       
 21006 						if (mixin.init) {
       
 21007 							mixin.init.apply(self, arguments);
       
 21008 						}
       
 21009 					}
       
 21010 				}
       
 21011 			}
       
 21012 		}
       
 21013 
       
 21014 		// Dummy function, needs to be extended in order to provide functionality
       
 21015 		function dummy() {
       
 21016 			return this;
       
 21017 		}
       
 21018 
       
 21019 		// Creates a overloaded method for the class
       
 21020 		// this enables you to use this._super(); to call the super function
       
 21021 		function createMethod(name, fn) {
       
 21022 			return function() {
       
 21023 				var self = this, tmp = self._super, ret;
       
 21024 
       
 21025 				self._super = _super[name];
       
 21026 				ret = fn.apply(self, arguments);
       
 21027 				self._super = tmp;
       
 21028 
       
 21029 				return ret;
       
 21030 			};
       
 21031 		}
       
 21032 
       
 21033 		// Instantiate a base class (but only create the instance,
       
 21034 		// don't run the init constructor)
       
 21035 		initializing = true;
       
 21036 
       
 21037 		/*eslint new-cap:0 */
       
 21038 		prototype = new self();
       
 21039 		initializing = false;
       
 21040 
       
 21041 		// Add mixins
       
 21042 		if (prop.Mixins) {
       
 21043 			each(prop.Mixins, function(mixin) {
       
 21044 				mixin = mixin;
       
 21045 
       
 21046 				for (var name in mixin) {
       
 21047 					if (name !== "init") {
       
 21048 						prop[name] = mixin[name];
       
 21049 					}
       
 21050 				}
       
 21051 			});
       
 21052 
       
 21053 			if (_super.Mixins) {
       
 21054 				prop.Mixins = _super.Mixins.concat(prop.Mixins);
       
 21055 			}
       
 21056 		}
       
 21057 
       
 21058 		// Generate dummy methods
       
 21059 		if (prop.Methods) {
       
 21060 			each(prop.Methods.split(','), function(name) {
       
 21061 				prop[name] = dummy;
       
 21062 			});
       
 21063 		}
       
 21064 
       
 21065 		// Generate property methods
       
 21066 		if (prop.Properties) {
       
 21067 			each(prop.Properties.split(','), function(name) {
       
 21068 				var fieldName = '_' + name;
       
 21069 
       
 21070 				prop[name] = function(value) {
       
 21071 					var self = this, undef;
       
 21072 
       
 21073 					// Set value
       
 21074 					if (value !== undef) {
       
 21075 						self[fieldName] = value;
       
 21076 
       
 21077 						return self;
       
 21078 					}
       
 21079 
       
 21080 					// Get value
       
 21081 					return self[fieldName];
       
 21082 				};
       
 21083 			});
       
 21084 		}
       
 21085 
       
 21086 		// Static functions
       
 21087 		if (prop.Statics) {
       
 21088 			each(prop.Statics, function(func, name) {
       
 21089 				Class[name] = func;
       
 21090 			});
       
 21091 		}
       
 21092 
       
 21093 		// Default settings
       
 21094 		if (prop.Defaults && _super.Defaults) {
       
 21095 			prop.Defaults = extend({}, _super.Defaults, prop.Defaults);
       
 21096 		}
       
 21097 
       
 21098 		// Copy the properties over onto the new prototype
       
 21099 		for (name in prop) {
       
 21100 			member = prop[name];
       
 21101 
       
 21102 			if (typeof member == "function" && _super[name]) {
       
 21103 				prototype[name] = createMethod(name, member);
       
 21104 			} else {
       
 21105 				prototype[name] = member;
       
 21106 			}
       
 21107 		}
       
 21108 
       
 21109 		// Populate our constructed prototype object
       
 21110 		Class.prototype = prototype;
       
 21111 
       
 21112 		// Enforce the constructor to be what we expect
       
 21113 		Class.constructor = Class;
       
 21114 
       
 21115 		// And make this class extendible
       
 21116 		Class.extend = extendClass;
       
 21117 
       
 21118 		return Class;
       
 21119 	};
       
 21120 
       
 21121 	return Class;
       
 21122 });
       
 21123 
       
 21124 // Included from: js/tinymce/classes/util/EventDispatcher.js
       
 21125 
       
 21126 /**
       
 21127  * EventDispatcher.js
       
 21128  *
       
 21129  * Copyright, Moxiecode Systems AB
       
 21130  * Released under LGPL License.
       
 21131  *
       
 21132  * License: http://www.tinymce.com/license
       
 21133  * Contributing: http://www.tinymce.com/contributing
       
 21134  */
       
 21135 
       
 21136 /**
       
 21137  * This class lets you add/remove and fire events by name on the specified scope. This makes
       
 21138  * it easy to add event listener logic to any class.
       
 21139  *
       
 21140  * @class tinymce.util.EventDispatcher
       
 21141  * @example
       
 21142  *  var eventDispatcher = new EventDispatcher();
       
 21143  *
       
 21144  *  eventDispatcher.on('click', function() {console.log('data');});
       
 21145  *  eventDispatcher.fire('click', {data: 123});
       
 21146  */
       
 21147 define("tinymce/util/EventDispatcher", [
       
 21148 	"tinymce/util/Tools"
       
 21149 ], function(Tools) {
       
 21150 	var nativeEvents = Tools.makeMap(
       
 21151 		"focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
       
 21152 		"mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
       
 21153 		"draggesture dragdrop drop drag submit " +
       
 21154 		"compositionstart compositionend compositionupdate touchstart touchend",
       
 21155 		' '
       
 21156 	);
       
 21157 
       
 21158 	function Dispatcher(settings) {
       
 21159 		var self = this, scope, bindings = {}, toggleEvent;
       
 21160 
       
 21161 		function returnFalse() {
       
 21162 			return false;
       
 21163 		}
       
 21164 
       
 21165 		function returnTrue() {
       
 21166 			return true;
       
 21167 		}
       
 21168 
       
 21169 		settings = settings || {};
       
 21170 		scope = settings.scope || self;
       
 21171 		toggleEvent = settings.toggleEvent || returnFalse;
       
 21172 
       
 21173 		/**
       
 21174 		 * Fires the specified event by name.
       
 21175 		 *
       
 21176 		 * @method fire
       
 21177 		 * @param {String} name Name of the event to fire.
       
 21178 		 * @param {Object?} args Event arguments.
       
 21179 		 * @return {Object} Event args instance passed in.
       
 21180 		 * @example
       
 21181 		 * instance.fire('event', {...});
       
 21182 		 */
       
 21183 		function fire(name, args) {
       
 21184 			var handlers, i, l, callback;
       
 21185 
       
 21186 			name = name.toLowerCase();
       
 21187 			args = args || {};
       
 21188 			args.type = name;
       
 21189 
       
 21190 			// Setup target is there isn't one
       
 21191 			if (!args.target) {
       
 21192 				args.target = scope;
       
 21193 			}
       
 21194 
       
 21195 			// Add event delegation methods if they are missing
       
 21196 			if (!args.preventDefault) {
       
 21197 				// Add preventDefault method
       
 21198 				args.preventDefault = function() {
       
 21199 					args.isDefaultPrevented = returnTrue;
       
 21200 				};
       
 21201 
       
 21202 				// Add stopPropagation
       
 21203 				args.stopPropagation = function() {
       
 21204 					args.isPropagationStopped = returnTrue;
       
 21205 				};
       
 21206 
       
 21207 				// Add stopImmediatePropagation
       
 21208 				args.stopImmediatePropagation = function() {
       
 21209 					args.isImmediatePropagationStopped = returnTrue;
       
 21210 				};
       
 21211 
       
 21212 				// Add event delegation states
       
 21213 				args.isDefaultPrevented = returnFalse;
       
 21214 				args.isPropagationStopped = returnFalse;
       
 21215 				args.isImmediatePropagationStopped = returnFalse;
       
 21216 			}
       
 21217 
       
 21218 			if (settings.beforeFire) {
       
 21219 				settings.beforeFire(args);
       
 21220 			}
       
 21221 
       
 21222 			handlers = bindings[name];
       
 21223 			if (handlers) {
       
 21224 				for (i = 0, l = handlers.length; i < l; i++) {
       
 21225 					callback = handlers[i];
       
 21226 
       
 21227 					// Unbind handlers marked with "once"
       
 21228 					if (callback.once) {
       
 21229 						off(name, callback.func);
       
 21230 					}
       
 21231 
       
 21232 					// Stop immediate propagation if needed
       
 21233 					if (args.isImmediatePropagationStopped()) {
       
 21234 						args.stopPropagation();
       
 21235 						return args;
       
 21236 					}
       
 21237 
       
 21238 					// If callback returns false then prevent default and stop all propagation
       
 21239 					if (callback.func.call(scope, args) === false) {
       
 21240 						args.preventDefault();
       
 21241 						return args;
       
 21242 					}
       
 21243 				}
       
 21244 			}
       
 21245 
       
 21246 			return args;
       
 21247 		}
       
 21248 
       
 21249 		/**
       
 21250 		 * Binds an event listener to a specific event by name.
       
 21251 		 *
       
 21252 		 * @method on
       
 21253 		 * @param {String} name Event name or space separated list of events to bind.
       
 21254 		 * @param {callback} callback Callback to be executed when the event occurs.
       
 21255 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
       
 21256 		 * @return {Object} Current class instance.
       
 21257 		 * @example
       
 21258 		 * instance.on('event', function(e) {
       
 21259 		 *     // Callback logic
       
 21260 		 * });
       
 21261 		 */
       
 21262 		function on(name, callback, prepend, extra) {
       
 21263 			var handlers, names, i;
       
 21264 
       
 21265 			if (callback === false) {
       
 21266 				callback = returnFalse;
       
 21267 			}
       
 21268 
       
 21269 			if (callback) {
       
 21270 				callback = {
       
 21271 					func: callback
       
 21272 				};
       
 21273 
       
 21274 				if (extra) {
       
 21275 					Tools.extend(callback, extra);
       
 21276 				}
       
 21277 
       
 21278 				names = name.toLowerCase().split(' ');
       
 21279 				i = names.length;
       
 21280 				while (i--) {
       
 21281 					name = names[i];
       
 21282 					handlers = bindings[name];
       
 21283 					if (!handlers) {
       
 21284 						handlers = bindings[name] = [];
       
 21285 						toggleEvent(name, true);
       
 21286 					}
       
 21287 
       
 21288 					if (prepend) {
       
 21289 						handlers.unshift(callback);
       
 21290 					} else {
       
 21291 						handlers.push(callback);
       
 21292 					}
       
 21293 				}
       
 21294 			}
       
 21295 
       
 21296 			return self;
       
 21297 		}
       
 21298 
       
 21299 		/**
       
 21300 		 * Unbinds an event listener to a specific event by name.
       
 21301 		 *
       
 21302 		 * @method off
       
 21303 		 * @param {String?} name Name of the event to unbind.
       
 21304 		 * @param {callback?} callback Callback to unbind.
       
 21305 		 * @return {Object} Current class instance.
       
 21306 		 * @example
       
 21307 		 * // Unbind specific callback
       
 21308 		 * instance.off('event', handler);
       
 21309 		 *
       
 21310 		 * // Unbind all listeners by name
       
 21311 		 * instance.off('event');
       
 21312 		 *
       
 21313 		 * // Unbind all events
       
 21314 		 * instance.off();
       
 21315 		 */
       
 21316 		function off(name, callback) {
       
 21317 			var i, handlers, bindingName, names, hi;
       
 21318 
       
 21319 			if (name) {
       
 21320 				names = name.toLowerCase().split(' ');
       
 21321 				i = names.length;
       
 21322 				while (i--) {
       
 21323 					name = names[i];
       
 21324 					handlers = bindings[name];
       
 21325 
       
 21326 					// Unbind all handlers
       
 21327 					if (!name) {
       
 21328 						for (bindingName in bindings) {
       
 21329 							toggleEvent(bindingName, false);
       
 21330 							delete bindings[bindingName];
       
 21331 						}
       
 21332 
       
 21333 						return self;
       
 21334 					}
       
 21335 
       
 21336 					if (handlers) {
       
 21337 						// Unbind all by name
       
 21338 						if (!callback) {
       
 21339 							handlers.length = 0;
       
 21340 						} else {
       
 21341 							// Unbind specific ones
       
 21342 							hi = handlers.length;
       
 21343 							while (hi--) {
       
 21344 								if (handlers[hi].func === callback) {
       
 21345 									handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1));
       
 21346 									bindings[name] = handlers;
       
 21347 								}
       
 21348 							}
       
 21349 						}
       
 21350 
       
 21351 						if (!handlers.length) {
       
 21352 							toggleEvent(name, false);
       
 21353 							delete bindings[name];
       
 21354 						}
       
 21355 					}
       
 21356 				}
       
 21357 			} else {
       
 21358 				for (name in bindings) {
       
 21359 					toggleEvent(name, false);
       
 21360 				}
       
 21361 
       
 21362 				bindings = {};
       
 21363 			}
       
 21364 
       
 21365 			return self;
       
 21366 		}
       
 21367 
       
 21368 		/**
       
 21369 		 * Binds an event listener to a specific event by name
       
 21370 		 * and automatically unbind the event once the callback fires.
       
 21371 		 *
       
 21372 		 * @method once
       
 21373 		 * @param {String} name Event name or space separated list of events to bind.
       
 21374 		 * @param {callback} callback Callback to be executed when the event occurs.
       
 21375 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
       
 21376 		 * @return {Object} Current class instance.
       
 21377 		 * @example
       
 21378 		 * instance.once('event', function(e) {
       
 21379 		 *     // Callback logic
       
 21380 		 * });
       
 21381 		 */
       
 21382 		function once(name, callback, prepend) {
       
 21383 			return on(name, callback, prepend, {once: true});
       
 21384 		}
       
 21385 
       
 21386 		/**
       
 21387 		 * Returns true/false if the dispatcher has a event of the specified name.
       
 21388 		 *
       
 21389 		 * @method has
       
 21390 		 * @param {String} name Name of the event to check for.
       
 21391 		 * @return {Boolean} true/false if the event exists or not.
       
 21392 		 */
       
 21393 		function has(name) {
       
 21394 			name = name.toLowerCase();
       
 21395 			return !(!bindings[name] || bindings[name].length === 0);
       
 21396 		}
       
 21397 
       
 21398 		// Expose
       
 21399 		self.fire = fire;
       
 21400 		self.on = on;
       
 21401 		self.off = off;
       
 21402 		self.once = once;
       
 21403 		self.has = has;
       
 21404 	}
       
 21405 
       
 21406 	/**
       
 21407 	 * Returns true/false if the specified event name is a native browser event or not.
       
 21408 	 *
       
 21409 	 * @method isNative
       
 21410 	 * @param {String} name Name to check if it's native.
       
 21411 	 * @return {Boolean} true/false if the event is native or not.
       
 21412 	 * @static
       
 21413 	 */
       
 21414 	Dispatcher.isNative = function(name) {
       
 21415 		return !!nativeEvents[name.toLowerCase()];
       
 21416 	};
       
 21417 
       
 21418 	return Dispatcher;
       
 21419 });
       
 21420 
       
 21421 // Included from: js/tinymce/classes/ui/Selector.js
       
 21422 
       
 21423 /**
       
 21424  * Selector.js
       
 21425  *
       
 21426  * Copyright, Moxiecode Systems AB
       
 21427  * Released under LGPL License.
       
 21428  *
       
 21429  * License: http://www.tinymce.com/license
       
 21430  * Contributing: http://www.tinymce.com/contributing
       
 21431  */
       
 21432 
       
 21433 /*eslint no-nested-ternary:0 */
       
 21434 
       
 21435 /**
       
 21436  * Selector engine, enables you to select controls by using CSS like expressions.
       
 21437  * We currently only support basic CSS expressions to reduce the size of the core
       
 21438  * and the ones we support should be enough for most cases.
       
 21439  *
       
 21440  * @example
       
 21441  * Supported expressions:
       
 21442  *  element
       
 21443  *  element#name
       
 21444  *  element.class
       
 21445  *  element[attr]
       
 21446  *  element[attr*=value]
       
 21447  *  element[attr~=value]
       
 21448  *  element[attr!=value]
       
 21449  *  element[attr^=value]
       
 21450  *  element[attr$=value]
       
 21451  *  element:<state>
       
 21452  *  element:not(<expression>)
       
 21453  *  element:first
       
 21454  *  element:last
       
 21455  *  element:odd
       
 21456  *  element:even
       
 21457  *  element element
       
 21458  *  element > element
       
 21459  *
       
 21460  * @class tinymce.ui.Selector
       
 21461  */
       
 21462 define("tinymce/ui/Selector", [
       
 21463 	"tinymce/util/Class"
       
 21464 ], function(Class) {
       
 21465 	"use strict";
       
 21466 
       
 21467 	/**
       
 21468 	 * Produces an array with a unique set of objects. It will not compare the values
       
 21469 	 * but the references of the objects.
       
 21470 	 *
       
 21471 	 * @private
       
 21472 	 * @method unqiue
       
 21473 	 * @param {Array} array Array to make into an array with unique items.
       
 21474 	 * @return {Array} Array with unique items.
       
 21475 	 */
       
 21476 	function unique(array) {
       
 21477 		var uniqueItems = [], i = array.length, item;
       
 21478 
       
 21479 		while (i--) {
       
 21480 			item = array[i];
       
 21481 
       
 21482 			if (!item.__checked) {
       
 21483 				uniqueItems.push(item);
       
 21484 				item.__checked = 1;
       
 21485 			}
       
 21486 		}
       
 21487 
       
 21488 		i = uniqueItems.length;
       
 21489 		while (i--) {
       
 21490 			delete uniqueItems[i].__checked;
       
 21491 		}
       
 21492 
       
 21493 		return uniqueItems;
       
 21494 	}
       
 21495 
       
 21496 	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
       
 21497 
       
 21498 	/*jshint maxlen:255 */
       
 21499 	/*eslint max-len:0 */
       
 21500 	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
       
 21501 		whiteSpace = /^\s*|\s*$/g,
       
 21502 		Collection;
       
 21503 
       
 21504 	var Selector = Class.extend({
       
 21505 		/**
       
 21506 		 * Constructs a new Selector instance.
       
 21507 		 *
       
 21508 		 * @constructor
       
 21509 		 * @method init
       
 21510 		 * @param {String} selector CSS like selector expression.
       
 21511 		 */
       
 21512 		init: function(selector) {
       
 21513 			var match = this.match;
       
 21514 
       
 21515 			function compileNameFilter(name) {
       
 21516 				if (name) {
       
 21517 					name = name.toLowerCase();
       
 21518 
       
 21519 					return function(item) {
       
 21520 						return name === '*' || item.type === name;
       
 21521 					};
       
 21522 				}
       
 21523 			}
       
 21524 
       
 21525 			function compileIdFilter(id) {
       
 21526 				if (id) {
       
 21527 					return function(item) {
       
 21528 						return item._name === id;
       
 21529 					};
       
 21530 				}
       
 21531 			}
       
 21532 
       
 21533 			function compileClassesFilter(classes) {
       
 21534 				if (classes) {
       
 21535 					classes = classes.split('.');
       
 21536 
       
 21537 					return function(item) {
       
 21538 						var i = classes.length;
       
 21539 
       
 21540 						while (i--) {
       
 21541 							if (!item.hasClass(classes[i])) {
       
 21542 								return false;
       
 21543 							}
       
 21544 						}
       
 21545 
       
 21546 						return true;
       
 21547 					};
       
 21548 				}
       
 21549 			}
       
 21550 
       
 21551 			function compileAttrFilter(name, cmp, check) {
       
 21552 				if (name) {
       
 21553 					return function(item) {
       
 21554 						var value = item[name] ? item[name]() : '';
       
 21555 
       
 21556 						return !cmp ? !!check :
       
 21557 							cmp === "=" ? value === check :
       
 21558 							cmp === "*=" ? value.indexOf(check) >= 0 :
       
 21559 							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
       
 21560 							cmp === "!=" ? value != check :
       
 21561 							cmp === "^=" ? value.indexOf(check) === 0 :
       
 21562 							cmp === "$=" ? value.substr(value.length - check.length) === check :
       
 21563 							false;
       
 21564 					};
       
 21565 				}
       
 21566 			}
       
 21567 
       
 21568 			function compilePsuedoFilter(name) {
       
 21569 				var notSelectors;
       
 21570 
       
 21571 				if (name) {
       
 21572 					name = /(?:not\((.+)\))|(.+)/i.exec(name);
       
 21573 
       
 21574 					if (!name[1]) {
       
 21575 						name = name[2];
       
 21576 
       
 21577 						return function(item, index, length) {
       
 21578 							return name === 'first' ? index === 0 :
       
 21579 								name === 'last' ? index === length - 1 :
       
 21580 								name === 'even' ? index % 2 === 0 :
       
 21581 								name === 'odd' ? index % 2 === 1 :
       
 21582 								item[name] ? item[name]() :
       
 21583 								false;
       
 21584 						};
       
 21585 					} else {
       
 21586 						// Compile not expression
       
 21587 						notSelectors = parseChunks(name[1], []);
       
 21588 
       
 21589 						return function(item) {
       
 21590 							return !match(item, notSelectors);
       
 21591 						};
       
 21592 					}
       
 21593 				}
       
 21594 			}
       
 21595 
       
 21596 			function compile(selector, filters, direct) {
       
 21597 				var parts;
       
 21598 
       
 21599 				function add(filter) {
       
 21600 					if (filter) {
       
 21601 						filters.push(filter);
       
 21602 					}
       
 21603 				}
       
 21604 
       
 21605 				// Parse expression into parts
       
 21606 				parts = expression.exec(selector.replace(whiteSpace, ''));
       
 21607 
       
 21608 				add(compileNameFilter(parts[1]));
       
 21609 				add(compileIdFilter(parts[2]));
       
 21610 				add(compileClassesFilter(parts[3]));
       
 21611 				add(compileAttrFilter(parts[4], parts[5], parts[6]));
       
 21612 				add(compilePsuedoFilter(parts[7]));
       
 21613 
       
 21614 				// Mark the filter with psuedo for performance
       
 21615 				filters.psuedo = !!parts[7];
       
 21616 				filters.direct = direct;
       
 21617 
       
 21618 				return filters;
       
 21619 			}
       
 21620 
       
 21621 			// Parser logic based on Sizzle by John Resig
       
 21622 			function parseChunks(selector, selectors) {
       
 21623 				var parts = [], extra, matches, i;
       
 21624 
       
 21625 				do {
       
 21626 					chunker.exec("");
       
 21627 					matches = chunker.exec(selector);
       
 21628 
       
 21629 					if (matches) {
       
 21630 						selector = matches[3];
       
 21631 						parts.push(matches[1]);
       
 21632 
       
 21633 						if (matches[2]) {
       
 21634 							extra = matches[3];
       
 21635 							break;
       
 21636 						}
       
 21637 					}
       
 21638 				} while (matches);
       
 21639 
       
 21640 				if (extra) {
       
 21641 					parseChunks(extra, selectors);
       
 21642 				}
       
 21643 
       
 21644 				selector = [];
       
 21645 				for (i = 0; i < parts.length; i++) {
       
 21646 					if (parts[i] != '>') {
       
 21647 						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
       
 21648 					}
       
 21649 				}
       
 21650 
       
 21651 				selectors.push(selector);
       
 21652 
       
 21653 				return selectors;
       
 21654 			}
       
 21655 
       
 21656 			this._selectors = parseChunks(selector, []);
       
 21657 		},
       
 21658 
       
 21659 		/**
       
 21660 		 * Returns true/false if the selector matches the specified control.
       
 21661 		 *
       
 21662 		 * @method match
       
 21663 		 * @param {tinymce.ui.Control} control Control to match agains the selector.
       
 21664 		 * @param {Array} selectors Optional array of selectors, mostly used internally.
       
 21665 		 * @return {Boolean} true/false state if the control matches or not.
       
 21666 		 */
       
 21667 		match: function(control, selectors) {
       
 21668 			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
       
 21669 
       
 21670 			selectors = selectors || this._selectors;
       
 21671 			for (i = 0, l = selectors.length; i < l; i++) {
       
 21672 				selector = selectors[i];
       
 21673 				sl = selector.length;
       
 21674 				item = control;
       
 21675 				count = 0;
       
 21676 
       
 21677 				for (si = sl - 1; si >= 0; si--) {
       
 21678 					filters = selector[si];
       
 21679 
       
 21680 					while (item) {
       
 21681 						// Find the index and length since a psuedo filter like :first needs it
       
 21682 						if (filters.psuedo) {
       
 21683 							siblings = item.parent().items();
       
 21684 							index = length = siblings.length;
       
 21685 							while (index--) {
       
 21686 								if (siblings[index] === item) {
       
 21687 									break;
       
 21688 								}
       
 21689 							}
       
 21690 						}
       
 21691 
       
 21692 						for (fi = 0, fl = filters.length; fi < fl; fi++) {
       
 21693 							if (!filters[fi](item, index, length)) {
       
 21694 								fi = fl + 1;
       
 21695 								break;
       
 21696 							}
       
 21697 						}
       
 21698 
       
 21699 						if (fi === fl) {
       
 21700 							count++;
       
 21701 							break;
       
 21702 						} else {
       
 21703 							// If it didn't match the right most expression then
       
 21704 							// break since it's no point looking at the parents
       
 21705 							if (si === sl - 1) {
       
 21706 								break;
       
 21707 							}
       
 21708 						}
       
 21709 
       
 21710 						item = item.parent();
       
 21711 					}
       
 21712 				}
       
 21713 
       
 21714 				// If we found all selectors then return true otherwise continue looking
       
 21715 				if (count === sl) {
       
 21716 					return true;
       
 21717 				}
       
 21718 			}
       
 21719 
       
 21720 			return false;
       
 21721 		},
       
 21722 
       
 21723 		/**
       
 21724 		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
       
 21725 		 *
       
 21726 		 * @method find
       
 21727 		 * @param {tinymce.ui.Control} container Container to look for items in.
       
 21728 		 * @return {tinymce.ui.Collection} Collection with matched elements.
       
 21729 		 */
       
 21730 		find: function(container) {
       
 21731 			var matches = [], i, l, selectors = this._selectors;
       
 21732 
       
 21733 			function collect(items, selector, index) {
       
 21734 				var i, l, fi, fl, item, filters = selector[index];
       
 21735 
       
 21736 				for (i = 0, l = items.length; i < l; i++) {
       
 21737 					item = items[i];
       
 21738 
       
 21739 					// Run each filter agains the item
       
 21740 					for (fi = 0, fl = filters.length; fi < fl; fi++) {
       
 21741 						if (!filters[fi](item, i, l)) {
       
 21742 							fi = fl + 1;
       
 21743 							break;
       
 21744 						}
       
 21745 					}
       
 21746 
       
 21747 					// All filters matched the item
       
 21748 					if (fi === fl) {
       
 21749 						// Matched item is on the last expression like: panel toolbar [button]
       
 21750 						if (index == selector.length - 1) {
       
 21751 							matches.push(item);
       
 21752 						} else {
       
 21753 							// Collect next expression type
       
 21754 							if (item.items) {
       
 21755 								collect(item.items(), selector, index + 1);
       
 21756 							}
       
 21757 						}
       
 21758 					} else if (filters.direct) {
       
 21759 						return;
       
 21760 					}
       
 21761 
       
 21762 					// Collect child items
       
 21763 					if (item.items) {
       
 21764 						collect(item.items(), selector, index);
       
 21765 					}
       
 21766 				}
       
 21767 			}
       
 21768 
       
 21769 			if (container.items) {
       
 21770 				for (i = 0, l = selectors.length; i < l; i++) {
       
 21771 					collect(container.items(), selectors[i], 0);
       
 21772 				}
       
 21773 
       
 21774 				// Unique the matches if needed
       
 21775 				if (l > 1) {
       
 21776 					matches = unique(matches);
       
 21777 				}
       
 21778 			}
       
 21779 
       
 21780 			// Fix for circular reference
       
 21781 			if (!Collection) {
       
 21782 				// TODO: Fix me!
       
 21783 				Collection = Selector.Collection;
       
 21784 			}
       
 21785 
       
 21786 			return new Collection(matches);
       
 21787 		}
       
 21788 	});
       
 21789 
       
 21790 	return Selector;
       
 21791 });
       
 21792 
       
 21793 // Included from: js/tinymce/classes/ui/Collection.js
       
 21794 
       
 21795 /**
       
 21796  * Collection.js
       
 21797  *
       
 21798  * Copyright, Moxiecode Systems AB
       
 21799  * Released under LGPL License.
       
 21800  *
       
 21801  * License: http://www.tinymce.com/license
       
 21802  * Contributing: http://www.tinymce.com/contributing
       
 21803  */
       
 21804 
       
 21805 /**
       
 21806  * Control collection, this class contains control instances and it enables you to
       
 21807  * perform actions on all the contained items. This is very similar to how jQuery works.
       
 21808  *
       
 21809  * @example
       
 21810  * someCollection.show().disabled(true);
       
 21811  *
       
 21812  * @class tinymce.ui.Collection
       
 21813  */
       
 21814 define("tinymce/ui/Collection", [
       
 21815 	"tinymce/util/Tools",
       
 21816 	"tinymce/ui/Selector",
       
 21817 	"tinymce/util/Class"
       
 21818 ], function(Tools, Selector, Class) {
       
 21819 	"use strict";
       
 21820 
       
 21821 	var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice;
       
 21822 
       
 21823 	proto = {
       
 21824 		/**
       
 21825 		 * Current number of contained control instances.
       
 21826 		 *
       
 21827 		 * @field length
       
 21828 		 * @type Number
       
 21829 		 */
       
 21830 		length: 0,
       
 21831 
       
 21832 		/**
       
 21833 		 * Constructor for the collection.
       
 21834 		 *
       
 21835 		 * @constructor
       
 21836 		 * @method init
       
 21837 		 * @param {Array} items Optional array with items to add.
       
 21838 		 */
       
 21839 		init: function(items) {
       
 21840 			if (items) {
       
 21841 				this.add(items);
       
 21842 			}
       
 21843 		},
       
 21844 
       
 21845 		/**
       
 21846 		 * Adds new items to the control collection.
       
 21847 		 *
       
 21848 		 * @method add
       
 21849 		 * @param {Array} items Array if items to add to collection.
       
 21850 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 21851 		 */
       
 21852 		add: function(items) {
       
 21853 			var self = this;
       
 21854 
       
 21855 			// Force single item into array
       
 21856 			if (!Tools.isArray(items)) {
       
 21857 				if (items instanceof Collection) {
       
 21858 					self.add(items.toArray());
       
 21859 				} else {
       
 21860 					push.call(self, items);
       
 21861 				}
       
 21862 			} else {
       
 21863 				push.apply(self, items);
       
 21864 			}
       
 21865 
       
 21866 			return self;
       
 21867 		},
       
 21868 
       
 21869 		/**
       
 21870 		 * Sets the contents of the collection. This will remove any existing items
       
 21871 		 * and replace them with the ones specified in the input array.
       
 21872 		 *
       
 21873 		 * @method set
       
 21874 		 * @param {Array} items Array with items to set into the Collection.
       
 21875 		 * @return {tinymce.ui.Collection} Collection instance.
       
 21876 		 */
       
 21877 		set: function(items) {
       
 21878 			var self = this, len = self.length, i;
       
 21879 
       
 21880 			self.length = 0;
       
 21881 			self.add(items);
       
 21882 
       
 21883 			// Remove old entries
       
 21884 			for (i = self.length; i < len; i++) {
       
 21885 				delete self[i];
       
 21886 			}
       
 21887 
       
 21888 			return self;
       
 21889 		},
       
 21890 
       
 21891 		/**
       
 21892 		 * Filters the collection item based on the specified selector expression or selector function.
       
 21893 		 *
       
 21894 		 * @method filter
       
 21895 		 * @param {String} selector Selector expression to filter items by.
       
 21896 		 * @return {tinymce.ui.Collection} Collection containing the filtered items.
       
 21897 		 */
       
 21898 		filter: function(selector) {
       
 21899 			var self = this, i, l, matches = [], item, match;
       
 21900 
       
 21901 			// Compile string into selector expression
       
 21902 			if (typeof selector === "string") {
       
 21903 				selector = new Selector(selector);
       
 21904 
       
 21905 				match = function(item) {
       
 21906 					return selector.match(item);
       
 21907 				};
       
 21908 			} else {
       
 21909 				// Use selector as matching function
       
 21910 				match = selector;
       
 21911 			}
       
 21912 
       
 21913 			for (i = 0, l = self.length; i < l; i++) {
       
 21914 				item = self[i];
       
 21915 
       
 21916 				if (match(item)) {
       
 21917 					matches.push(item);
       
 21918 				}
       
 21919 			}
       
 21920 
       
 21921 			return new Collection(matches);
       
 21922 		},
       
 21923 
       
 21924 		/**
       
 21925 		 * Slices the items within the collection.
       
 21926 		 *
       
 21927 		 * @method slice
       
 21928 		 * @param {Number} index Index to slice at.
       
 21929 		 * @param {Number} len Optional length to slice.
       
 21930 		 * @return {tinymce.ui.Collection} Current collection.
       
 21931 		 */
       
 21932 		slice: function() {
       
 21933 			return new Collection(slice.apply(this, arguments));
       
 21934 		},
       
 21935 
       
 21936 		/**
       
 21937 		 * Makes the current collection equal to the specified index.
       
 21938 		 *
       
 21939 		 * @method eq
       
 21940 		 * @param {Number} index Index of the item to set the collection to.
       
 21941 		 * @return {tinymce.ui.Collection} Current collection.
       
 21942 		 */
       
 21943 		eq: function(index) {
       
 21944 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
       
 21945 		},
       
 21946 
       
 21947 		/**
       
 21948 		 * Executes the specified callback on each item in collection.
       
 21949 		 *
       
 21950 		 * @method each
       
 21951 		 * @param {function} callback Callback to execute for each item in collection.
       
 21952 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 21953 		 */
       
 21954 		each: function(callback) {
       
 21955 			Tools.each(this, callback);
       
 21956 
       
 21957 			return this;
       
 21958 		},
       
 21959 
       
 21960 		/**
       
 21961 		 * Returns an JavaScript array object of the contents inside the collection.
       
 21962 		 *
       
 21963 		 * @method toArray
       
 21964 		 * @return {Array} Array with all items from collection.
       
 21965 		 */
       
 21966 		toArray: function() {
       
 21967 			return Tools.toArray(this);
       
 21968 		},
       
 21969 
       
 21970 		/**
       
 21971 		 * Finds the index of the specified control or return -1 if it isn't in the collection.
       
 21972 		 *
       
 21973 		 * @method indexOf
       
 21974 		 * @param {Control} ctrl Control instance to look for.
       
 21975 		 * @return {Number} Index of the specified control or -1.
       
 21976 		 */
       
 21977 		indexOf: function(ctrl) {
       
 21978 			var self = this, i = self.length;
       
 21979 
       
 21980 			while (i--) {
       
 21981 				if (self[i] === ctrl) {
       
 21982 					break;
       
 21983 				}
       
 21984 			}
       
 21985 
       
 21986 			return i;
       
 21987 		},
       
 21988 
       
 21989 		/**
       
 21990 		 * Returns a new collection of the contents in reverse order.
       
 21991 		 *
       
 21992 		 * @method reverse
       
 21993 		 * @return {tinymce.ui.Collection} Collection instance with reversed items.
       
 21994 		 */
       
 21995 		reverse: function() {
       
 21996 			return new Collection(Tools.toArray(this).reverse());
       
 21997 		},
       
 21998 
       
 21999 		/**
       
 22000 		 * Returns true/false if the class exists or not.
       
 22001 		 *
       
 22002 		 * @method hasClass
       
 22003 		 * @param {String} cls Class to check for.
       
 22004 		 * @return {Boolean} true/false state if the class exists or not.
       
 22005 		 */
       
 22006 		hasClass: function(cls) {
       
 22007 			return this[0] ? this[0].hasClass(cls) : false;
       
 22008 		},
       
 22009 
       
 22010 		/**
       
 22011 		 * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>);
       
 22012 		 *
       
 22013 		 * @method prop
       
 22014 		 * @param {String} name Property name to get/set.
       
 22015 		 * @param {Object} value Optional object value to set.
       
 22016 		 * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation.
       
 22017 		 */
       
 22018 		prop: function(name, value) {
       
 22019 			var self = this, undef, item;
       
 22020 
       
 22021 			if (value !== undef) {
       
 22022 				self.each(function(item) {
       
 22023 					if (item[name]) {
       
 22024 						item[name](value);
       
 22025 					}
       
 22026 				});
       
 22027 
       
 22028 				return self;
       
 22029 			}
       
 22030 
       
 22031 			item = self[0];
       
 22032 
       
 22033 			if (item && item[name]) {
       
 22034 				return item[name]();
       
 22035 			}
       
 22036 		},
       
 22037 
       
 22038 		/**
       
 22039 		 * Executes the specific function name with optional arguments an all items in collection if it exists.
       
 22040 		 *
       
 22041 		 * @example collection.exec("myMethod", arg1, arg2, arg3);
       
 22042 		 * @method exec
       
 22043 		 * @param {String} name Name of the function to execute.
       
 22044 		 * @param {Object} ... Multiple arguments to pass to each function.
       
 22045 		 * @return {tinymce.ui.Collection} Current collection.
       
 22046 		 */
       
 22047 		exec: function(name) {
       
 22048 			var self = this, args = Tools.toArray(arguments).slice(1);
       
 22049 
       
 22050 			self.each(function(item) {
       
 22051 				if (item[name]) {
       
 22052 					item[name].apply(item, args);
       
 22053 				}
       
 22054 			});
       
 22055 
       
 22056 			return self;
       
 22057 		},
       
 22058 
       
 22059 		/**
       
 22060 		 * Remove all items from collection and DOM.
       
 22061 		 *
       
 22062 		 * @method remove
       
 22063 		 * @return {tinymce.ui.Collection} Current collection.
       
 22064 		 */
       
 22065 		remove: function() {
       
 22066 			var i = this.length;
       
 22067 
       
 22068 			while (i--) {
       
 22069 				this[i].remove();
       
 22070 			}
       
 22071 
       
 22072 			return this;
       
 22073 		}
       
 22074 
       
 22075 		/**
       
 22076 		 * Fires the specified event by name and arguments on the control. This will execute all
       
 22077 		 * bound event handlers.
       
 22078 		 *
       
 22079 		 * @method fire
       
 22080 		 * @param {String} name Name of the event to fire.
       
 22081 		 * @param {Object} args Optional arguments to pass to the event.
       
 22082 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22083 		 */
       
 22084 		// fire: function(event, args) {}, -- Generated by code below
       
 22085 
       
 22086 		/**
       
 22087 		 * Binds a callback to the specified event. This event can both be
       
 22088 		 * native browser events like "click" or custom ones like PostRender.
       
 22089 		 *
       
 22090 		 * The callback function will have two parameters the first one being the control that received the event
       
 22091 		 * the second one will be the event object either the browsers native event object or a custom JS object.
       
 22092 		 *
       
 22093 		 * @method on
       
 22094 		 * @param {String} name Name of the event to bind. For example "click".
       
 22095 		 * @param {String/function} callback Callback function to execute ones the event occurs.
       
 22096 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22097 		 */
       
 22098 		// on: function(name, callback) {}, -- Generated by code below
       
 22099 
       
 22100 		/**
       
 22101 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
       
 22102 		 * parameter all event handlers will be removed. If you omit the callback all event handles
       
 22103 		 * by the specified name will be removed.
       
 22104 		 *
       
 22105 		 * @method off
       
 22106 		 * @param {String} name Optional name for the event to unbind.
       
 22107 		 * @param {function} callback Optional callback function to unbind.
       
 22108 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22109 		 */
       
 22110 		// off: function(name, callback) {}, -- Generated by code below
       
 22111 
       
 22112 		/**
       
 22113 		 * Shows the items in the current collection.
       
 22114 		 *
       
 22115 		 * @method show
       
 22116 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22117 		 */
       
 22118 		// show: function() {}, -- Generated by code below
       
 22119 
       
 22120 		/**
       
 22121 		 * Hides the items in the current collection.
       
 22122 		 *
       
 22123 		 * @method hide
       
 22124 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22125 		 */
       
 22126 		// hide: function() {}, -- Generated by code below
       
 22127 
       
 22128 		/**
       
 22129 		 * Sets/gets the text contents of the items in the current collection.
       
 22130 		 *
       
 22131 		 * @method text
       
 22132 		 * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation.
       
 22133 		 */
       
 22134 		// text: function(value) {}, -- Generated by code below
       
 22135 
       
 22136 		/**
       
 22137 		 * Sets/gets the name contents of the items in the current collection.
       
 22138 		 *
       
 22139 		 * @method name
       
 22140 		 * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation.
       
 22141 		 */
       
 22142 		// name: function(value) {}, -- Generated by code below
       
 22143 
       
 22144 		/**
       
 22145 		 * Sets/gets the disabled state on the items in the current collection.
       
 22146 		 *
       
 22147 		 * @method disabled
       
 22148 		 * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation.
       
 22149 		 */
       
 22150 		// disabled: function(state) {}, -- Generated by code below
       
 22151 
       
 22152 		/**
       
 22153 		 * Sets/gets the active state on the items in the current collection.
       
 22154 		 *
       
 22155 		 * @method active
       
 22156 		 * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation.
       
 22157 		 */
       
 22158 		// active: function(state) {}, -- Generated by code below
       
 22159 
       
 22160 		/**
       
 22161 		 * Sets/gets the selected state on the items in the current collection.
       
 22162 		 *
       
 22163 		 * @method selected
       
 22164 		 * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation.
       
 22165 		 */
       
 22166 		// selected: function(state) {}, -- Generated by code below
       
 22167 
       
 22168 		/**
       
 22169 		 * Sets/gets the selected state on the items in the current collection.
       
 22170 		 *
       
 22171 		 * @method visible
       
 22172 		 * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation.
       
 22173 		 */
       
 22174 		// visible: function(state) {}, -- Generated by code below
       
 22175 
       
 22176 		/**
       
 22177 		 * Adds a class to all items in the collection.
       
 22178 		 *
       
 22179 		 * @method addClass
       
 22180 		 * @param {String} cls Class to add to each item.
       
 22181 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22182 		 */
       
 22183 		// addClass: function(cls) {}, -- Generated by code below
       
 22184 
       
 22185 		/**
       
 22186 		 * Removes the specified class from all items in collection.
       
 22187 		 *
       
 22188 		 * @method removeClass
       
 22189 		 * @param {String} cls Class to remove from each item.
       
 22190 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 22191 		 */
       
 22192 		// removeClass: function(cls) {}, -- Generated by code below
       
 22193 	};
       
 22194 
       
 22195 	// Extend tinymce.ui.Collection prototype with some generated control specific methods
       
 22196 	Tools.each('fire on off show hide addClass removeClass append prepend before after reflow'.split(' '), function(name) {
       
 22197 		proto[name] = function() {
       
 22198 			var args = Tools.toArray(arguments);
       
 22199 
       
 22200 			this.each(function(ctrl) {
       
 22201 				if (name in ctrl) {
       
 22202 					ctrl[name].apply(ctrl, args);
       
 22203 				}
       
 22204 			});
       
 22205 
       
 22206 			return this;
       
 22207 		};
       
 22208 	});
       
 22209 
       
 22210 	// Extend tinymce.ui.Collection prototype with some property methods
       
 22211 	Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) {
       
 22212 		proto[name] = function(value) {
       
 22213 			return this.prop(name, value);
       
 22214 		};
       
 22215 	});
       
 22216 
       
 22217 	// Create class based on the new prototype
       
 22218 	Collection = Class.extend(proto);
       
 22219 
       
 22220 	// Stick Collection into Selector to prevent circual references
       
 22221 	Selector.Collection = Collection;
       
 22222 
       
 22223 	return Collection;
       
 22224 });
       
 22225 
       
 22226 // Included from: js/tinymce/classes/ui/DomUtils.js
       
 22227 
       
 22228 /**
       
 22229  * DOMUtils.js
       
 22230  *
       
 22231  * Copyright, Moxiecode Systems AB
       
 22232  * Released under LGPL License.
       
 22233  *
       
 22234  * License: http://www.tinymce.com/license
       
 22235  * Contributing: http://www.tinymce.com/contributing
       
 22236  */
       
 22237 
       
 22238 define("tinymce/ui/DomUtils", [
       
 22239 	"tinymce/util/Tools",
       
 22240 	"tinymce/dom/DOMUtils"
       
 22241 ], function(Tools, DOMUtils) {
       
 22242 	"use strict";
       
 22243 
       
 22244 	var count = 0;
       
 22245 
       
 22246 	return {
       
 22247 		id: function() {
       
 22248 			return 'mceu_' + (count++);
       
 22249 		},
       
 22250 
       
 22251 		createFragment: function(html) {
       
 22252 			return DOMUtils.DOM.createFragment(html);
       
 22253 		},
       
 22254 
       
 22255 		getWindowSize: function() {
       
 22256 			return DOMUtils.DOM.getViewPort();
       
 22257 		},
       
 22258 
       
 22259 		getSize: function(elm) {
       
 22260 			var width, height;
       
 22261 
       
 22262 			if (elm.getBoundingClientRect) {
       
 22263 				var rect = elm.getBoundingClientRect();
       
 22264 
       
 22265 				width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth);
       
 22266 				height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight);
       
 22267 			} else {
       
 22268 				width = elm.offsetWidth;
       
 22269 				height = elm.offsetHeight;
       
 22270 			}
       
 22271 
       
 22272 			return {width: width, height: height};
       
 22273 		},
       
 22274 
       
 22275 		getPos: function(elm, root) {
       
 22276 			return DOMUtils.DOM.getPos(elm, root);
       
 22277 		},
       
 22278 
       
 22279 		getViewPort: function(win) {
       
 22280 			return DOMUtils.DOM.getViewPort(win);
       
 22281 		},
       
 22282 
       
 22283 		get: function(id) {
       
 22284 			return document.getElementById(id);
       
 22285 		},
       
 22286 
       
 22287 		addClass: function(elm, cls) {
       
 22288 			return DOMUtils.DOM.addClass(elm, cls);
       
 22289 		},
       
 22290 
       
 22291 		removeClass: function(elm, cls) {
       
 22292 			return DOMUtils.DOM.removeClass(elm, cls);
       
 22293 		},
       
 22294 
       
 22295 		hasClass: function(elm, cls) {
       
 22296 			return DOMUtils.DOM.hasClass(elm, cls);
       
 22297 		},
       
 22298 
       
 22299 		toggleClass: function(elm, cls, state) {
       
 22300 			return DOMUtils.DOM.toggleClass(elm, cls, state);
       
 22301 		},
       
 22302 
       
 22303 		css: function(elm, name, value) {
       
 22304 			return DOMUtils.DOM.setStyle(elm, name, value);
       
 22305 		},
       
 22306 
       
 22307 		getRuntimeStyle: function(elm, name) {
       
 22308 			return DOMUtils.DOM.getStyle(elm, name, true);
       
 22309 		},
       
 22310 
       
 22311 		on: function(target, name, callback, scope) {
       
 22312 			return DOMUtils.DOM.bind(target, name, callback, scope);
       
 22313 		},
       
 22314 
       
 22315 		off: function(target, name, callback) {
       
 22316 			return DOMUtils.DOM.unbind(target, name, callback);
       
 22317 		},
       
 22318 
       
 22319 		fire: function(target, name, args) {
       
 22320 			return DOMUtils.DOM.fire(target, name, args);
       
 22321 		},
       
 22322 
       
 22323 		innerHtml: function(elm, html) {
       
 22324 			// Workaround for <div> in <p> bug on IE 8 #6178
       
 22325 			DOMUtils.DOM.setHTML(elm, html);
       
 22326 		}
       
 22327 	};
       
 22328 });
       
 22329 
       
 22330 // Included from: js/tinymce/classes/ui/Control.js
       
 22331 
       
 22332 /**
       
 22333  * Control.js
       
 22334  *
       
 22335  * Copyright, Moxiecode Systems AB
       
 22336  * Released under LGPL License.
       
 22337  *
       
 22338  * License: http://www.tinymce.com/license
       
 22339  * Contributing: http://www.tinymce.com/contributing
       
 22340  */
       
 22341 
       
 22342 /*eslint consistent-this:0 */
       
 22343 
       
 22344 /**
       
 22345  * This is the base class for all controls and containers. All UI control instances inherit
       
 22346  * from this one as it has the base logic needed by all of them.
       
 22347  *
       
 22348  * @class tinymce.ui.Control
       
 22349  */
       
 22350 define("tinymce/ui/Control", [
       
 22351 	"tinymce/util/Class",
       
 22352 	"tinymce/util/Tools",
       
 22353 	"tinymce/util/EventDispatcher",
       
 22354 	"tinymce/ui/Collection",
       
 22355 	"tinymce/ui/DomUtils"
       
 22356 ], function(Class, Tools, EventDispatcher, Collection, DomUtils) {
       
 22357 	"use strict";
       
 22358 
       
 22359 	var hasMouseWheelEventSupport = "onmousewheel" in document;
       
 22360 	var hasWheelEventSupport = false;
       
 22361 	var classPrefix = "mce-";
       
 22362 
       
 22363 	function getEventDispatcher(obj) {
       
 22364 		if (!obj._eventDispatcher) {
       
 22365 			obj._eventDispatcher = new EventDispatcher({
       
 22366 				scope: obj,
       
 22367 				toggleEvent: function(name, state) {
       
 22368 					if (state && EventDispatcher.isNative(name)) {
       
 22369 						if (!obj._nativeEvents) {
       
 22370 							obj._nativeEvents = {};
       
 22371 						}
       
 22372 
       
 22373 						obj._nativeEvents[name] = true;
       
 22374 
       
 22375 						if (obj._rendered) {
       
 22376 							obj.bindPendingEvents();
       
 22377 						}
       
 22378 					}
       
 22379 				}
       
 22380 			});
       
 22381 		}
       
 22382 
       
 22383 		return obj._eventDispatcher;
       
 22384 	}
       
 22385 
       
 22386 	var Control = Class.extend({
       
 22387 		Statics: {
       
 22388 			classPrefix: classPrefix
       
 22389 		},
       
 22390 
       
 22391 		isRtl: function() {
       
 22392 			return Control.rtl;
       
 22393 		},
       
 22394 
       
 22395 		/**
       
 22396 		 * Class/id prefix to use for all controls.
       
 22397 		 *
       
 22398 		 * @final
       
 22399 		 * @field {String} classPrefix
       
 22400 		 */
       
 22401 		classPrefix: classPrefix,
       
 22402 
       
 22403 		/**
       
 22404 		 * Constructs a new control instance with the specified settings.
       
 22405 		 *
       
 22406 		 * @constructor
       
 22407 		 * @param {Object} settings Name/value object with settings.
       
 22408 		 * @setting {String} style Style CSS properties to add.
       
 22409 		 * @setting {String} border Border box values example: 1 1 1 1
       
 22410 		 * @setting {String} padding Padding box values example: 1 1 1 1
       
 22411 		 * @setting {String} margin Margin box values example: 1 1 1 1
       
 22412 		 * @setting {Number} minWidth Minimal width for the control.
       
 22413 		 * @setting {Number} minHeight Minimal height for the control.
       
 22414 		 * @setting {String} classes Space separated list of classes to add.
       
 22415 		 * @setting {String} role WAI-ARIA role to use for control.
       
 22416 		 * @setting {Boolean} hidden Is the control hidden by default.
       
 22417 		 * @setting {Boolean} disabled Is the control disabled by default.
       
 22418 		 * @setting {String} name Name of the control instance.
       
 22419 		 */
       
 22420 		init: function(settings) {
       
 22421 			var self = this, classes, i;
       
 22422 
       
 22423 			self.settings = settings = Tools.extend({}, self.Defaults, settings);
       
 22424 
       
 22425 			// Initial states
       
 22426 			self._id = settings.id || DomUtils.id();
       
 22427 			self._text = self._name = '';
       
 22428 			self._width = self._height = 0;
       
 22429 			self._aria = {role: settings.role};
       
 22430 			this._elmCache = {};
       
 22431 
       
 22432 			// Setup classes
       
 22433 			classes = settings.classes;
       
 22434 			if (classes) {
       
 22435 				classes = classes.split(' ');
       
 22436 				classes.map = {};
       
 22437 				i = classes.length;
       
 22438 				while (i--) {
       
 22439 					classes.map[classes[i]] = true;
       
 22440 				}
       
 22441 			}
       
 22442 
       
 22443 			self._classes = classes || [];
       
 22444 			self.visible(true);
       
 22445 
       
 22446 			// Set some properties
       
 22447 			Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
       
 22448 				var value = settings[name], undef;
       
 22449 
       
 22450 				if (value !== undef) {
       
 22451 					self[name](value);
       
 22452 				} else if (self['_' + name] === undef) {
       
 22453 					self['_' + name] = false;
       
 22454 				}
       
 22455 			});
       
 22456 
       
 22457 			self.on('click', function() {
       
 22458 				if (self.disabled()) {
       
 22459 					return false;
       
 22460 				}
       
 22461 			});
       
 22462 
       
 22463 			// TODO: Is this needed duplicate code see above?
       
 22464 			if (settings.classes) {
       
 22465 				Tools.each(settings.classes.split(' '), function(cls) {
       
 22466 					self.addClass(cls);
       
 22467 				});
       
 22468 			}
       
 22469 
       
 22470 			/**
       
 22471 			 * Name/value object with settings for the current control.
       
 22472 			 *
       
 22473 			 * @field {Object} settings
       
 22474 			 */
       
 22475 			self.settings = settings;
       
 22476 
       
 22477 			self._borderBox = self.parseBox(settings.border);
       
 22478 			self._paddingBox = self.parseBox(settings.padding);
       
 22479 			self._marginBox = self.parseBox(settings.margin);
       
 22480 
       
 22481 			if (settings.hidden) {
       
 22482 				self.hide();
       
 22483 			}
       
 22484 		},
       
 22485 
       
 22486 		// Will generate getter/setter methods for these properties
       
 22487 		Properties: 'parent,title,text,width,height,disabled,active,name,value',
       
 22488 
       
 22489 		// Will generate empty dummy functions for these
       
 22490 		Methods: 'renderHtml',
       
 22491 
       
 22492 		/**
       
 22493 		 * Returns the root element to render controls into.
       
 22494 		 *
       
 22495 		 * @method getContainerElm
       
 22496 		 * @return {Element} HTML DOM element to render into.
       
 22497 		 */
       
 22498 		getContainerElm: function() {
       
 22499 			return document.body;
       
 22500 		},
       
 22501 
       
 22502 		/**
       
 22503 		 * Returns a control instance for the current DOM element.
       
 22504 		 *
       
 22505 		 * @method getParentCtrl
       
 22506 		 * @param {Element} elm HTML dom element to get parent control from.
       
 22507 		 * @return {tinymce.ui.Control} Control instance or undefined.
       
 22508 		 */
       
 22509 		getParentCtrl: function(elm) {
       
 22510 			var ctrl, lookup = this.getRoot().controlIdLookup;
       
 22511 
       
 22512 			while (elm && lookup) {
       
 22513 				ctrl = lookup[elm.id];
       
 22514 				if (ctrl) {
       
 22515 					break;
       
 22516 				}
       
 22517 
       
 22518 				elm = elm.parentNode;
       
 22519 			}
       
 22520 
       
 22521 			return ctrl;
       
 22522 		},
       
 22523 
       
 22524 		/**
       
 22525 		 * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
       
 22526 		 *
       
 22527 		 * @method parseBox
       
 22528 		 * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
       
 22529 		 * @return {Object} Object with top/right/bottom/left properties.
       
 22530 		 * @private
       
 22531 		 */
       
 22532 		parseBox: function(value) {
       
 22533 			var len, radix = 10;
       
 22534 
       
 22535 			if (!value) {
       
 22536 				return;
       
 22537 			}
       
 22538 
       
 22539 			if (typeof value === "number") {
       
 22540 				value = value || 0;
       
 22541 
       
 22542 				return {
       
 22543 					top: value,
       
 22544 					left: value,
       
 22545 					bottom: value,
       
 22546 					right: value
       
 22547 				};
       
 22548 			}
       
 22549 
       
 22550 			value = value.split(' ');
       
 22551 			len = value.length;
       
 22552 
       
 22553 			if (len === 1) {
       
 22554 				value[1] = value[2] = value[3] = value[0];
       
 22555 			} else if (len === 2) {
       
 22556 				value[2] = value[0];
       
 22557 				value[3] = value[1];
       
 22558 			} else if (len === 3) {
       
 22559 				value[3] = value[1];
       
 22560 			}
       
 22561 
       
 22562 			return {
       
 22563 				top: parseInt(value[0], radix) || 0,
       
 22564 				right: parseInt(value[1], radix) || 0,
       
 22565 				bottom: parseInt(value[2], radix) || 0,
       
 22566 				left: parseInt(value[3], radix) || 0
       
 22567 			};
       
 22568 		},
       
 22569 
       
 22570 		borderBox: function() {
       
 22571 			return this._borderBox;
       
 22572 		},
       
 22573 
       
 22574 		paddingBox: function() {
       
 22575 			return this._paddingBox;
       
 22576 		},
       
 22577 
       
 22578 		marginBox: function() {
       
 22579 			return this._marginBox;
       
 22580 		},
       
 22581 
       
 22582 		measureBox: function(elm, prefix) {
       
 22583 			function getStyle(name) {
       
 22584 				var defaultView = document.defaultView;
       
 22585 
       
 22586 				if (defaultView) {
       
 22587 					// Remove camelcase
       
 22588 					name = name.replace(/[A-Z]/g, function(a) {
       
 22589 						return '-' + a;
       
 22590 					});
       
 22591 
       
 22592 					return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
       
 22593 				}
       
 22594 
       
 22595 				return elm.currentStyle[name];
       
 22596 			}
       
 22597 
       
 22598 			function getSide(name) {
       
 22599 				var val = parseFloat(getStyle(name), 10);
       
 22600 
       
 22601 				return isNaN(val) ? 0 : val;
       
 22602 			}
       
 22603 
       
 22604 			return {
       
 22605 				top: getSide(prefix + "TopWidth"),
       
 22606 				right: getSide(prefix + "RightWidth"),
       
 22607 				bottom: getSide(prefix + "BottomWidth"),
       
 22608 				left: getSide(prefix + "LeftWidth")
       
 22609 			};
       
 22610 		},
       
 22611 
       
 22612 		/**
       
 22613 		 * Initializes the current controls layout rect.
       
 22614 		 * This will be executed by the layout managers to determine the
       
 22615 		 * default minWidth/minHeight etc.
       
 22616 		 *
       
 22617 		 * @method initLayoutRect
       
 22618 		 * @return {Object} Layout rect instance.
       
 22619 		 */
       
 22620 		initLayoutRect: function() {
       
 22621 			var self = this, settings = self.settings, borderBox, layoutRect;
       
 22622 			var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
       
 22623 			var startMinWidth, startMinHeight, initialSize;
       
 22624 
       
 22625 			// Measure the current element
       
 22626 			borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
       
 22627 			self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
       
 22628 			self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
       
 22629 			initialSize = DomUtils.getSize(elm);
       
 22630 
       
 22631 			// Setup minWidth/minHeight and width/height
       
 22632 			startMinWidth = settings.minWidth;
       
 22633 			startMinHeight = settings.minHeight;
       
 22634 			minWidth = startMinWidth || initialSize.width;
       
 22635 			minHeight = startMinHeight || initialSize.height;
       
 22636 			width = settings.width;
       
 22637 			height = settings.height;
       
 22638 			autoResize = settings.autoResize;
       
 22639 			autoResize = typeof autoResize != "undefined" ? autoResize : !width && !height;
       
 22640 
       
 22641 			width = width || minWidth;
       
 22642 			height = height || minHeight;
       
 22643 
       
 22644 			var deltaW = borderBox.left + borderBox.right;
       
 22645 			var deltaH = borderBox.top + borderBox.bottom;
       
 22646 
       
 22647 			var maxW = settings.maxWidth || 0xFFFF;
       
 22648 			var maxH = settings.maxHeight || 0xFFFF;
       
 22649 
       
 22650 			// Setup initial layout rect
       
 22651 			self._layoutRect = layoutRect = {
       
 22652 				x: settings.x || 0,
       
 22653 				y: settings.y || 0,
       
 22654 				w: width,
       
 22655 				h: height,
       
 22656 				deltaW: deltaW,
       
 22657 				deltaH: deltaH,
       
 22658 				contentW: width - deltaW,
       
 22659 				contentH: height - deltaH,
       
 22660 				innerW: width - deltaW,
       
 22661 				innerH: height - deltaH,
       
 22662 				startMinWidth: startMinWidth || 0,
       
 22663 				startMinHeight: startMinHeight || 0,
       
 22664 				minW: Math.min(minWidth, maxW),
       
 22665 				minH: Math.min(minHeight, maxH),
       
 22666 				maxW: maxW,
       
 22667 				maxH: maxH,
       
 22668 				autoResize: autoResize,
       
 22669 				scrollW: 0
       
 22670 			};
       
 22671 
       
 22672 			self._lastLayoutRect = {};
       
 22673 
       
 22674 			return layoutRect;
       
 22675 		},
       
 22676 
       
 22677 		/**
       
 22678 		 * Getter/setter for the current layout rect.
       
 22679 		 *
       
 22680 		 * @method layoutRect
       
 22681 		 * @param {Object} [newRect] Optional new layout rect.
       
 22682 		 * @return {tinymce.ui.Control/Object} Current control or rect object.
       
 22683 		 */
       
 22684 		layoutRect: function(newRect) {
       
 22685 			var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
       
 22686 
       
 22687 			// Initialize default layout rect
       
 22688 			if (!curRect) {
       
 22689 				curRect = self.initLayoutRect();
       
 22690 			}
       
 22691 
       
 22692 			// Set new rect values
       
 22693 			if (newRect) {
       
 22694 				// Calc deltas between inner and outer sizes
       
 22695 				deltaWidth = curRect.deltaW;
       
 22696 				deltaHeight = curRect.deltaH;
       
 22697 
       
 22698 				// Set x position
       
 22699 				if (newRect.x !== undef) {
       
 22700 					curRect.x = newRect.x;
       
 22701 				}
       
 22702 
       
 22703 				// Set y position
       
 22704 				if (newRect.y !== undef) {
       
 22705 					curRect.y = newRect.y;
       
 22706 				}
       
 22707 
       
 22708 				// Set minW
       
 22709 				if (newRect.minW !== undef) {
       
 22710 					curRect.minW = newRect.minW;
       
 22711 				}
       
 22712 
       
 22713 				// Set minH
       
 22714 				if (newRect.minH !== undef) {
       
 22715 					curRect.minH = newRect.minH;
       
 22716 				}
       
 22717 
       
 22718 				// Set new width and calculate inner width
       
 22719 				size = newRect.w;
       
 22720 				if (size !== undef) {
       
 22721 					size = size < curRect.minW ? curRect.minW : size;
       
 22722 					size = size > curRect.maxW ? curRect.maxW : size;
       
 22723 					curRect.w = size;
       
 22724 					curRect.innerW = size - deltaWidth;
       
 22725 				}
       
 22726 
       
 22727 				// Set new height and calculate inner height
       
 22728 				size = newRect.h;
       
 22729 				if (size !== undef) {
       
 22730 					size = size < curRect.minH ? curRect.minH : size;
       
 22731 					size = size > curRect.maxH ? curRect.maxH : size;
       
 22732 					curRect.h = size;
       
 22733 					curRect.innerH = size - deltaHeight;
       
 22734 				}
       
 22735 
       
 22736 				// Set new inner width and calculate width
       
 22737 				size = newRect.innerW;
       
 22738 				if (size !== undef) {
       
 22739 					size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
       
 22740 					size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
       
 22741 					curRect.innerW = size;
       
 22742 					curRect.w = size + deltaWidth;
       
 22743 				}
       
 22744 
       
 22745 				// Set new height and calculate inner height
       
 22746 				size = newRect.innerH;
       
 22747 				if (size !== undef) {
       
 22748 					size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
       
 22749 					size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
       
 22750 					curRect.innerH = size;
       
 22751 					curRect.h = size + deltaHeight;
       
 22752 				}
       
 22753 
       
 22754 				// Set new contentW
       
 22755 				if (newRect.contentW !== undef) {
       
 22756 					curRect.contentW = newRect.contentW;
       
 22757 				}
       
 22758 
       
 22759 				// Set new contentH
       
 22760 				if (newRect.contentH !== undef) {
       
 22761 					curRect.contentH = newRect.contentH;
       
 22762 				}
       
 22763 
       
 22764 				// Compare last layout rect with the current one to see if we need to repaint or not
       
 22765 				lastLayoutRect = self._lastLayoutRect;
       
 22766 				if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
       
 22767 					lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
       
 22768 					repaintControls = Control.repaintControls;
       
 22769 
       
 22770 					if (repaintControls) {
       
 22771 						if (repaintControls.map && !repaintControls.map[self._id]) {
       
 22772 							repaintControls.push(self);
       
 22773 							repaintControls.map[self._id] = true;
       
 22774 						}
       
 22775 					}
       
 22776 
       
 22777 					lastLayoutRect.x = curRect.x;
       
 22778 					lastLayoutRect.y = curRect.y;
       
 22779 					lastLayoutRect.w = curRect.w;
       
 22780 					lastLayoutRect.h = curRect.h;
       
 22781 				}
       
 22782 
       
 22783 				return self;
       
 22784 			}
       
 22785 
       
 22786 			return curRect;
       
 22787 		},
       
 22788 
       
 22789 		/**
       
 22790 		 * Repaints the control after a layout operation.
       
 22791 		 *
       
 22792 		 * @method repaint
       
 22793 		 */
       
 22794 		repaint: function() {
       
 22795 			var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
       
 22796 
       
 22797 			// Use Math.round on all values on IE < 9
       
 22798 			round = !document.createRange ? Math.round : function(value) {
       
 22799 				return value;
       
 22800 			};
       
 22801 
       
 22802 			style = self.getEl().style;
       
 22803 			rect = self._layoutRect;
       
 22804 			lastRepaintRect = self._lastRepaintRect || {};
       
 22805 
       
 22806 			borderBox = self._borderBox;
       
 22807 			borderW = borderBox.left + borderBox.right;
       
 22808 			borderH = borderBox.top + borderBox.bottom;
       
 22809 
       
 22810 			if (rect.x !== lastRepaintRect.x) {
       
 22811 				style.left = round(rect.x) + 'px';
       
 22812 				lastRepaintRect.x = rect.x;
       
 22813 			}
       
 22814 
       
 22815 			if (rect.y !== lastRepaintRect.y) {
       
 22816 				style.top = round(rect.y) + 'px';
       
 22817 				lastRepaintRect.y = rect.y;
       
 22818 			}
       
 22819 
       
 22820 			if (rect.w !== lastRepaintRect.w) {
       
 22821 				style.width = round(rect.w - borderW) + 'px';
       
 22822 				lastRepaintRect.w = rect.w;
       
 22823 			}
       
 22824 
       
 22825 			if (rect.h !== lastRepaintRect.h) {
       
 22826 				style.height = round(rect.h - borderH) + 'px';
       
 22827 				lastRepaintRect.h = rect.h;
       
 22828 			}
       
 22829 
       
 22830 			// Update body if needed
       
 22831 			if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
       
 22832 				bodyStyle = self.getEl('body').style;
       
 22833 				bodyStyle.width = round(rect.innerW) + 'px';
       
 22834 				lastRepaintRect.innerW = rect.innerW;
       
 22835 			}
       
 22836 
       
 22837 			if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
       
 22838 				bodyStyle = bodyStyle || self.getEl('body').style;
       
 22839 				bodyStyle.height = round(rect.innerH) + 'px';
       
 22840 				lastRepaintRect.innerH = rect.innerH;
       
 22841 			}
       
 22842 
       
 22843 			self._lastRepaintRect = lastRepaintRect;
       
 22844 			self.fire('repaint', {}, false);
       
 22845 		},
       
 22846 
       
 22847 		/**
       
 22848 		 * Binds a callback to the specified event. This event can both be
       
 22849 		 * native browser events like "click" or custom ones like PostRender.
       
 22850 		 *
       
 22851 		 * The callback function will be passed a DOM event like object that enables yout do stop propagation.
       
 22852 		 *
       
 22853 		 * @method on
       
 22854 		 * @param {String} name Name of the event to bind. For example "click".
       
 22855 		 * @param {String/function} callback Callback function to execute ones the event occurs.
       
 22856 		 * @return {tinymce.ui.Control} Current control object.
       
 22857 		 */
       
 22858 		on: function(name, callback) {
       
 22859 			var self = this;
       
 22860 
       
 22861 			function resolveCallbackName(name) {
       
 22862 				var callback, scope;
       
 22863 
       
 22864 				if (typeof name != 'string') {
       
 22865 					return name;
       
 22866 				}
       
 22867 
       
 22868 				return function(e) {
       
 22869 					if (!callback) {
       
 22870 						self.parentsAndSelf().each(function(ctrl) {
       
 22871 							var callbacks = ctrl.settings.callbacks;
       
 22872 
       
 22873 							if (callbacks && (callback = callbacks[name])) {
       
 22874 								scope = ctrl;
       
 22875 								return false;
       
 22876 							}
       
 22877 						});
       
 22878 					}
       
 22879 
       
 22880 					return callback.call(scope, e);
       
 22881 				};
       
 22882 			}
       
 22883 
       
 22884 			getEventDispatcher(self).on(name, resolveCallbackName(callback));
       
 22885 
       
 22886 			return self;
       
 22887 		},
       
 22888 
       
 22889 		/**
       
 22890 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
       
 22891 		 * parameter all event handlers will be removed. If you omit the callback all event handles
       
 22892 		 * by the specified name will be removed.
       
 22893 		 *
       
 22894 		 * @method off
       
 22895 		 * @param {String} [name] Name for the event to unbind.
       
 22896 		 * @param {function} [callback] Callback function to unbind.
       
 22897 		 * @return {mxex.ui.Control} Current control object.
       
 22898 		 */
       
 22899 		off: function(name, callback) {
       
 22900 			getEventDispatcher(this).off(name, callback);
       
 22901 			return this;
       
 22902 		},
       
 22903 
       
 22904 		/**
       
 22905 		 * Fires the specified event by name and arguments on the control. This will execute all
       
 22906 		 * bound event handlers.
       
 22907 		 *
       
 22908 		 * @method fire
       
 22909 		 * @param {String} name Name of the event to fire.
       
 22910 		 * @param {Object} [args] Arguments to pass to the event.
       
 22911 		 * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
       
 22912 		 * @return {Object} Current arguments object.
       
 22913 		 */
       
 22914 		fire: function(name, args, bubble) {
       
 22915 			var self = this;
       
 22916 
       
 22917 			args = args || {};
       
 22918 
       
 22919 			if (!args.control) {
       
 22920 				args.control = self;
       
 22921 			}
       
 22922 
       
 22923 			args = getEventDispatcher(self).fire(name, args);
       
 22924 
       
 22925 			// Bubble event up to parents
       
 22926 			if (bubble !== false && self.parent) {
       
 22927 				var parent = self.parent();
       
 22928 				while (parent && !args.isPropagationStopped()) {
       
 22929 					parent.fire(name, args, false);
       
 22930 					parent = parent.parent();
       
 22931 				}
       
 22932 			}
       
 22933 
       
 22934 			return args;
       
 22935 		},
       
 22936 
       
 22937 		/**
       
 22938 		 * Returns true/false if the specified event has any listeners.
       
 22939 		 *
       
 22940 		 * @method hasEventListeners
       
 22941 		 * @param {String} name Name of the event to check for.
       
 22942 		 * @return {Boolean} True/false state if the event has listeners.
       
 22943 		 */
       
 22944 		hasEventListeners: function(name) {
       
 22945 			return getEventDispatcher(this).has(name);
       
 22946 		},
       
 22947 
       
 22948 		/**
       
 22949 		 * Returns a control collection with all parent controls.
       
 22950 		 *
       
 22951 		 * @method parents
       
 22952 		 * @param {String} selector Optional selector expression to find parents.
       
 22953 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
       
 22954 		 */
       
 22955 		parents: function(selector) {
       
 22956 			var self = this, ctrl, parents = new Collection();
       
 22957 
       
 22958 			// Add each parent to collection
       
 22959 			for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) {
       
 22960 				parents.add(ctrl);
       
 22961 			}
       
 22962 
       
 22963 			// Filter away everything that doesn't match the selector
       
 22964 			if (selector) {
       
 22965 				parents = parents.filter(selector);
       
 22966 			}
       
 22967 
       
 22968 			return parents;
       
 22969 		},
       
 22970 
       
 22971 		/**
       
 22972 		 * Returns the current control and it's parents.
       
 22973 		 *
       
 22974 		 * @method parentsAndSelf
       
 22975 		 * @param {String} selector Optional selector expression to find parents.
       
 22976 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
       
 22977 		 */
       
 22978 		parentsAndSelf: function(selector) {
       
 22979 			return new Collection(this).add(this.parents(selector));
       
 22980 		},
       
 22981 
       
 22982 		/**
       
 22983 		 * Returns the control next to the current control.
       
 22984 		 *
       
 22985 		 * @method next
       
 22986 		 * @return {tinymce.ui.Control} Next control instance.
       
 22987 		 */
       
 22988 		next: function() {
       
 22989 			var parentControls = this.parent().items();
       
 22990 
       
 22991 			return parentControls[parentControls.indexOf(this) + 1];
       
 22992 		},
       
 22993 
       
 22994 		/**
       
 22995 		 * Returns the control previous to the current control.
       
 22996 		 *
       
 22997 		 * @method prev
       
 22998 		 * @return {tinymce.ui.Control} Previous control instance.
       
 22999 		 */
       
 23000 		prev: function() {
       
 23001 			var parentControls = this.parent().items();
       
 23002 
       
 23003 			return parentControls[parentControls.indexOf(this) - 1];
       
 23004 		},
       
 23005 
       
 23006 		/**
       
 23007 		 * Find the common ancestor for two control instances.
       
 23008 		 *
       
 23009 		 * @method findCommonAncestor
       
 23010 		 * @param {tinymce.ui.Control} ctrl1 First control.
       
 23011 		 * @param {tinymce.ui.Control} ctrl2 Second control.
       
 23012 		 * @return {tinymce.ui.Control} Ancestor control instance.
       
 23013 		 */
       
 23014 		findCommonAncestor: function(ctrl1, ctrl2) {
       
 23015 			var parentCtrl;
       
 23016 
       
 23017 			while (ctrl1) {
       
 23018 				parentCtrl = ctrl2;
       
 23019 
       
 23020 				while (parentCtrl && ctrl1 != parentCtrl) {
       
 23021 					parentCtrl = parentCtrl.parent();
       
 23022 				}
       
 23023 
       
 23024 				if (ctrl1 == parentCtrl) {
       
 23025 					break;
       
 23026 				}
       
 23027 
       
 23028 				ctrl1 = ctrl1.parent();
       
 23029 			}
       
 23030 
       
 23031 			return ctrl1;
       
 23032 		},
       
 23033 
       
 23034 		/**
       
 23035 		 * Returns true/false if the specific control has the specific class.
       
 23036 		 *
       
 23037 		 * @method hasClass
       
 23038 		 * @param {String} cls Class to check for.
       
 23039 		 * @param {String} [group] Sub element group name.
       
 23040 		 * @return {Boolean} True/false if the control has the specified class.
       
 23041 		 */
       
 23042 		hasClass: function(cls, group) {
       
 23043 			var classes = this._classes[group || 'control'];
       
 23044 
       
 23045 			cls = this.classPrefix + cls;
       
 23046 
       
 23047 			return classes && !!classes.map[cls];
       
 23048 		},
       
 23049 
       
 23050 		/**
       
 23051 		 * Adds the specified class to the control
       
 23052 		 *
       
 23053 		 * @method addClass
       
 23054 		 * @param {String} cls Class to check for.
       
 23055 		 * @param {String} [group] Sub element group name.
       
 23056 		 * @return {tinymce.ui.Control} Current control object.
       
 23057 		 */
       
 23058 		addClass: function(cls, group) {
       
 23059 			var self = this, classes, elm;
       
 23060 
       
 23061 			cls = this.classPrefix + cls;
       
 23062 			classes = self._classes[group || 'control'];
       
 23063 
       
 23064 			if (!classes) {
       
 23065 				classes = [];
       
 23066 				classes.map = {};
       
 23067 				self._classes[group || 'control'] = classes;
       
 23068 			}
       
 23069 
       
 23070 			if (!classes.map[cls]) {
       
 23071 				classes.map[cls] = cls;
       
 23072 				classes.push(cls);
       
 23073 
       
 23074 				if (self._rendered) {
       
 23075 					elm = self.getEl(group);
       
 23076 
       
 23077 					if (elm) {
       
 23078 						elm.className = classes.join(' ');
       
 23079 					}
       
 23080 				}
       
 23081 			}
       
 23082 
       
 23083 			return self;
       
 23084 		},
       
 23085 
       
 23086 		/**
       
 23087 		 * Removes the specified class from the control.
       
 23088 		 *
       
 23089 		 * @method removeClass
       
 23090 		 * @param {String} cls Class to remove.
       
 23091 		 * @param {String} [group] Sub element group name.
       
 23092 		 * @return {tinymce.ui.Control} Current control object.
       
 23093 		 */
       
 23094 		removeClass: function(cls, group) {
       
 23095 			var self = this, classes, i, elm;
       
 23096 
       
 23097 			cls = this.classPrefix + cls;
       
 23098 			classes = self._classes[group || 'control'];
       
 23099 			if (classes && classes.map[cls]) {
       
 23100 				delete classes.map[cls];
       
 23101 
       
 23102 				i = classes.length;
       
 23103 				while (i--) {
       
 23104 					if (classes[i] === cls) {
       
 23105 						classes.splice(i, 1);
       
 23106 					}
       
 23107 				}
       
 23108 			}
       
 23109 
       
 23110 			if (self._rendered) {
       
 23111 				elm = self.getEl(group);
       
 23112 
       
 23113 				if (elm) {
       
 23114 					elm.className = classes.join(' ');
       
 23115 				}
       
 23116 			}
       
 23117 
       
 23118 			return self;
       
 23119 		},
       
 23120 
       
 23121 		/**
       
 23122 		 * Toggles the specified class on the control.
       
 23123 		 *
       
 23124 		 * @method toggleClass
       
 23125 		 * @param {String} cls Class to remove.
       
 23126 		 * @param {Boolean} state True/false state to add/remove class.
       
 23127 		 * @param {String} [group] Sub element group name.
       
 23128 		 * @return {tinymce.ui.Control} Current control object.
       
 23129 		 */
       
 23130 		toggleClass: function(cls, state, group) {
       
 23131 			var self = this;
       
 23132 
       
 23133 			if (state) {
       
 23134 				self.addClass(cls, group);
       
 23135 			} else {
       
 23136 				self.removeClass(cls, group);
       
 23137 			}
       
 23138 
       
 23139 			return self;
       
 23140 		},
       
 23141 
       
 23142 		/**
       
 23143 		 * Returns the class string for the specified group name.
       
 23144 		 *
       
 23145 		 * @method classes
       
 23146 		 * @param {String} [group] Group to get clases by.
       
 23147 		 * @return {String} Classes for the specified group.
       
 23148 		 */
       
 23149 		classes: function(group) {
       
 23150 			var classes = this._classes[group || 'control'];
       
 23151 
       
 23152 			return classes ? classes.join(' ') : '';
       
 23153 		},
       
 23154 
       
 23155 		/**
       
 23156 		 * Sets the inner HTML of the control element.
       
 23157 		 *
       
 23158 		 * @method innerHtml
       
 23159 		 * @param {String} html Html string to set as inner html.
       
 23160 		 * @return {tinymce.ui.Control} Current control object.
       
 23161 		 */
       
 23162 		innerHtml: function(html) {
       
 23163 			DomUtils.innerHtml(this.getEl(), html);
       
 23164 			return this;
       
 23165 		},
       
 23166 
       
 23167 		/**
       
 23168 		 * Returns the control DOM element or sub element.
       
 23169 		 *
       
 23170 		 * @method getEl
       
 23171 		 * @param {String} [suffix] Suffix to get element by.
       
 23172 		 * @return {Element} HTML DOM element for the current control or it's children.
       
 23173 		 */
       
 23174 		getEl: function(suffix) {
       
 23175 			var id = suffix ? this._id + '-' + suffix : this._id;
       
 23176 
       
 23177 			if (!this._elmCache[id]) {
       
 23178 				this._elmCache[id] = DomUtils.get(id);
       
 23179 			}
       
 23180 
       
 23181 			return this._elmCache[id];
       
 23182 		},
       
 23183 
       
 23184 		/**
       
 23185 		 * Sets/gets the visible for the control.
       
 23186 		 *
       
 23187 		 * @method visible
       
 23188 		 * @param {Boolean} state Value to set to control.
       
 23189 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
       
 23190 		 */
       
 23191 		visible: function(state) {
       
 23192 			var self = this, parentCtrl;
       
 23193 
       
 23194 			if (typeof state !== "undefined") {
       
 23195 				if (self._visible !== state) {
       
 23196 					if (self._rendered) {
       
 23197 						self.getEl().style.display = state ? '' : 'none';
       
 23198 					}
       
 23199 
       
 23200 					self._visible = state;
       
 23201 
       
 23202 					// Parent container needs to reflow
       
 23203 					parentCtrl = self.parent();
       
 23204 					if (parentCtrl) {
       
 23205 						parentCtrl._lastRect = null;
       
 23206 					}
       
 23207 
       
 23208 					self.fire(state ? 'show' : 'hide');
       
 23209 				}
       
 23210 
       
 23211 				return self;
       
 23212 			}
       
 23213 
       
 23214 			return self._visible;
       
 23215 		},
       
 23216 
       
 23217 		/**
       
 23218 		 * Sets the visible state to true.
       
 23219 		 *
       
 23220 		 * @method show
       
 23221 		 * @return {tinymce.ui.Control} Current control instance.
       
 23222 		 */
       
 23223 		show: function() {
       
 23224 			return this.visible(true);
       
 23225 		},
       
 23226 
       
 23227 		/**
       
 23228 		 * Sets the visible state to false.
       
 23229 		 *
       
 23230 		 * @method hide
       
 23231 		 * @return {tinymce.ui.Control} Current control instance.
       
 23232 		 */
       
 23233 		hide: function() {
       
 23234 			return this.visible(false);
       
 23235 		},
       
 23236 
       
 23237 		/**
       
 23238 		 * Focuses the current control.
       
 23239 		 *
       
 23240 		 * @method focus
       
 23241 		 * @return {tinymce.ui.Control} Current control instance.
       
 23242 		 */
       
 23243 		focus: function() {
       
 23244 			try {
       
 23245 				this.getEl().focus();
       
 23246 			} catch (ex) {
       
 23247 				// Ignore IE error
       
 23248 			}
       
 23249 
       
 23250 			return this;
       
 23251 		},
       
 23252 
       
 23253 		/**
       
 23254 		 * Blurs the current control.
       
 23255 		 *
       
 23256 		 * @method blur
       
 23257 		 * @return {tinymce.ui.Control} Current control instance.
       
 23258 		 */
       
 23259 		blur: function() {
       
 23260 			this.getEl().blur();
       
 23261 
       
 23262 			return this;
       
 23263 		},
       
 23264 
       
 23265 		/**
       
 23266 		 * Sets the specified aria property.
       
 23267 		 *
       
 23268 		 * @method aria
       
 23269 		 * @param {String} name Name of the aria property to set.
       
 23270 		 * @param {String} value Value of the aria property.
       
 23271 		 * @return {tinymce.ui.Control} Current control instance.
       
 23272 		 */
       
 23273 		aria: function(name, value) {
       
 23274 			var self = this, elm = self.getEl(self.ariaTarget);
       
 23275 
       
 23276 			if (typeof value === "undefined") {
       
 23277 				return self._aria[name];
       
 23278 			} else {
       
 23279 				self._aria[name] = value;
       
 23280 			}
       
 23281 
       
 23282 			if (self._rendered) {
       
 23283 				elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
       
 23284 			}
       
 23285 
       
 23286 			return self;
       
 23287 		},
       
 23288 
       
 23289 		/**
       
 23290 		 * Encodes the specified string with HTML entities. It will also
       
 23291 		 * translate the string to different languages.
       
 23292 		 *
       
 23293 		 * @method encode
       
 23294 		 * @param {String/Object/Array} text Text to entity encode.
       
 23295 		 * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
       
 23296 		 * @return {String} Encoded and possible traslated string.
       
 23297 		 */
       
 23298 		encode: function(text, translate) {
       
 23299 			if (translate !== false) {
       
 23300 				text = this.translate(text);
       
 23301 			}
       
 23302 
       
 23303 			return (text || '').replace(/[&<>"]/g, function(match) {
       
 23304 				return '&#' + match.charCodeAt(0) + ';';
       
 23305 			});
       
 23306 		},
       
 23307 
       
 23308 		/**
       
 23309 		 * Returns the translated string.
       
 23310 		 *
       
 23311 		 * @method translate
       
 23312 		 * @param {String} text Text to translate.
       
 23313 		 * @return {String} Translated string or the same as the input.
       
 23314 		 */
       
 23315 		translate: function(text) {
       
 23316 			return Control.translate ? Control.translate(text) : text;
       
 23317 		},
       
 23318 
       
 23319 		/**
       
 23320 		 * Adds items before the current control.
       
 23321 		 *
       
 23322 		 * @method before
       
 23323 		 * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
       
 23324 		 * @return {tinymce.ui.Control} Current control instance.
       
 23325 		 */
       
 23326 		before: function(items) {
       
 23327 			var self = this, parent = self.parent();
       
 23328 
       
 23329 			if (parent) {
       
 23330 				parent.insert(items, parent.items().indexOf(self), true);
       
 23331 			}
       
 23332 
       
 23333 			return self;
       
 23334 		},
       
 23335 
       
 23336 		/**
       
 23337 		 * Adds items after the current control.
       
 23338 		 *
       
 23339 		 * @method after
       
 23340 		 * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
       
 23341 		 * @return {tinymce.ui.Control} Current control instance.
       
 23342 		 */
       
 23343 		after: function(items) {
       
 23344 			var self = this, parent = self.parent();
       
 23345 
       
 23346 			if (parent) {
       
 23347 				parent.insert(items, parent.items().indexOf(self));
       
 23348 			}
       
 23349 
       
 23350 			return self;
       
 23351 		},
       
 23352 
       
 23353 		/**
       
 23354 		 * Removes the current control from DOM and from UI collections.
       
 23355 		 *
       
 23356 		 * @method remove
       
 23357 		 * @return {tinymce.ui.Control} Current control instance.
       
 23358 		 */
       
 23359 		remove: function() {
       
 23360 			var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
       
 23361 
       
 23362 			if (self.items) {
       
 23363 				var controls = self.items().toArray();
       
 23364 				i = controls.length;
       
 23365 				while (i--) {
       
 23366 					controls[i].remove();
       
 23367 				}
       
 23368 			}
       
 23369 
       
 23370 			if (parent && parent.items) {
       
 23371 				newItems = [];
       
 23372 
       
 23373 				parent.items().each(function(item) {
       
 23374 					if (item !== self) {
       
 23375 						newItems.push(item);
       
 23376 					}
       
 23377 				});
       
 23378 
       
 23379 				parent.items().set(newItems);
       
 23380 				parent._lastRect = null;
       
 23381 			}
       
 23382 
       
 23383 			if (self._eventsRoot && self._eventsRoot == self) {
       
 23384 				DomUtils.off(elm);
       
 23385 			}
       
 23386 
       
 23387 			var lookup = self.getRoot().controlIdLookup;
       
 23388 			if (lookup) {
       
 23389 				delete lookup[self._id];
       
 23390 			}
       
 23391 
       
 23392 			if (elm && elm.parentNode) {
       
 23393 				elm.parentNode.removeChild(elm);
       
 23394 			}
       
 23395 
       
 23396 			self._rendered = false;
       
 23397 
       
 23398 			return self;
       
 23399 		},
       
 23400 
       
 23401 		/**
       
 23402 		 * Renders the control before the specified element.
       
 23403 		 *
       
 23404 		 * @method renderBefore
       
 23405 		 * @param {Element} elm Element to render before.
       
 23406 		 * @return {tinymce.ui.Control} Current control instance.
       
 23407 		 */
       
 23408 		renderBefore: function(elm) {
       
 23409 			var self = this;
       
 23410 
       
 23411 			elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
       
 23412 			self.postRender();
       
 23413 
       
 23414 			return self;
       
 23415 		},
       
 23416 
       
 23417 		/**
       
 23418 		 * Renders the control to the specified element.
       
 23419 		 *
       
 23420 		 * @method renderBefore
       
 23421 		 * @param {Element} elm Element to render to.
       
 23422 		 * @return {tinymce.ui.Control} Current control instance.
       
 23423 		 */
       
 23424 		renderTo: function(elm) {
       
 23425 			var self = this;
       
 23426 
       
 23427 			elm = elm || self.getContainerElm();
       
 23428 			elm.appendChild(DomUtils.createFragment(self.renderHtml()));
       
 23429 			self.postRender();
       
 23430 
       
 23431 			return self;
       
 23432 		},
       
 23433 
       
 23434 		/**
       
 23435 		 * Post render method. Called after the control has been rendered to the target.
       
 23436 		 *
       
 23437 		 * @method postRender
       
 23438 		 * @return {tinymce.ui.Control} Current control instance.
       
 23439 		 */
       
 23440 		postRender: function() {
       
 23441 			var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
       
 23442 
       
 23443 			// Bind on<event> settings
       
 23444 			for (name in settings) {
       
 23445 				if (name.indexOf("on") === 0) {
       
 23446 					self.on(name.substr(2), settings[name]);
       
 23447 				}
       
 23448 			}
       
 23449 
       
 23450 			if (self._eventsRoot) {
       
 23451 				for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
       
 23452 					parentEventsRoot = parent._eventsRoot;
       
 23453 				}
       
 23454 
       
 23455 				if (parentEventsRoot) {
       
 23456 					for (name in parentEventsRoot._nativeEvents) {
       
 23457 						self._nativeEvents[name] = true;
       
 23458 					}
       
 23459 				}
       
 23460 			}
       
 23461 
       
 23462 			self.bindPendingEvents();
       
 23463 
       
 23464 			if (settings.style) {
       
 23465 				elm = self.getEl();
       
 23466 				if (elm) {
       
 23467 					elm.setAttribute('style', settings.style);
       
 23468 					elm.style.cssText = settings.style;
       
 23469 				}
       
 23470 			}
       
 23471 
       
 23472 			if (!self._visible) {
       
 23473 				DomUtils.css(self.getEl(), 'display', 'none');
       
 23474 			}
       
 23475 
       
 23476 			if (self.settings.border) {
       
 23477 				box = self.borderBox();
       
 23478 				DomUtils.css(self.getEl(), {
       
 23479 					'border-top-width': box.top,
       
 23480 					'border-right-width': box.right,
       
 23481 					'border-bottom-width': box.bottom,
       
 23482 					'border-left-width': box.left
       
 23483 				});
       
 23484 			}
       
 23485 
       
 23486 			// Add instance to lookup
       
 23487 			var root = self.getRoot();
       
 23488 			if (!root.controlIdLookup) {
       
 23489 				root.controlIdLookup = {};
       
 23490 			}
       
 23491 
       
 23492 			root.controlIdLookup[self._id] = self;
       
 23493 
       
 23494 			for (var key in self._aria) {
       
 23495 				self.aria(key, self._aria[key]);
       
 23496 			}
       
 23497 
       
 23498 			self.fire('postrender', {}, false);
       
 23499 		},
       
 23500 
       
 23501 		/**
       
 23502 		 * Scrolls the current control into view.
       
 23503 		 *
       
 23504 		 * @method scrollIntoView
       
 23505 		 * @param {String} align Alignment in view top|center|bottom.
       
 23506 		 * @return {tinymce.ui.Control} Current control instance.
       
 23507 		 */
       
 23508 		scrollIntoView: function(align) {
       
 23509 			function getOffset(elm, rootElm) {
       
 23510 				var x, y, parent = elm;
       
 23511 
       
 23512 				x = y = 0;
       
 23513 				while (parent && parent != rootElm && parent.nodeType) {
       
 23514 					x += parent.offsetLeft || 0;
       
 23515 					y += parent.offsetTop || 0;
       
 23516 					parent = parent.offsetParent;
       
 23517 				}
       
 23518 
       
 23519 				return {x: x, y: y};
       
 23520 			}
       
 23521 
       
 23522 			var elm = this.getEl(), parentElm = elm.parentNode;
       
 23523 			var x, y, width, height, parentWidth, parentHeight;
       
 23524 			var pos = getOffset(elm, parentElm);
       
 23525 
       
 23526 			x = pos.x;
       
 23527 			y = pos.y;
       
 23528 			width = elm.offsetWidth;
       
 23529 			height = elm.offsetHeight;
       
 23530 			parentWidth = parentElm.clientWidth;
       
 23531 			parentHeight = parentElm.clientHeight;
       
 23532 
       
 23533 			if (align == "end") {
       
 23534 				x -= parentWidth - width;
       
 23535 				y -= parentHeight - height;
       
 23536 			} else if (align == "center") {
       
 23537 				x -= (parentWidth / 2) - (width / 2);
       
 23538 				y -= (parentHeight / 2) - (height / 2);
       
 23539 			}
       
 23540 
       
 23541 			parentElm.scrollLeft = x;
       
 23542 			parentElm.scrollTop = y;
       
 23543 
       
 23544 			return this;
       
 23545 		},
       
 23546 
       
 23547 		/**
       
 23548 		 * Binds pending DOM events.
       
 23549 		 *
       
 23550 		 * @private
       
 23551 		 */
       
 23552 		bindPendingEvents: function() {
       
 23553 			var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
       
 23554 
       
 23555 			function delegate(e) {
       
 23556 				var control = self.getParentCtrl(e.target);
       
 23557 
       
 23558 				if (control) {
       
 23559 					control.fire(e.type, e);
       
 23560 				}
       
 23561 			}
       
 23562 
       
 23563 			function mouseLeaveHandler() {
       
 23564 				var ctrl = eventRootCtrl._lastHoverCtrl;
       
 23565 
       
 23566 				if (ctrl) {
       
 23567 					ctrl.fire("mouseleave", {target: ctrl.getEl()});
       
 23568 
       
 23569 					ctrl.parents().each(function(ctrl) {
       
 23570 						ctrl.fire("mouseleave", {target: ctrl.getEl()});
       
 23571 					});
       
 23572 
       
 23573 					eventRootCtrl._lastHoverCtrl = null;
       
 23574 				}
       
 23575 			}
       
 23576 
       
 23577 			function mouseEnterHandler(e) {
       
 23578 				var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
       
 23579 
       
 23580 				// Over on a new control
       
 23581 				if (ctrl !== lastCtrl) {
       
 23582 					eventRootCtrl._lastHoverCtrl = ctrl;
       
 23583 
       
 23584 					parents = ctrl.parents().toArray().reverse();
       
 23585 					parents.push(ctrl);
       
 23586 
       
 23587 					if (lastCtrl) {
       
 23588 						lastParents = lastCtrl.parents().toArray().reverse();
       
 23589 						lastParents.push(lastCtrl);
       
 23590 
       
 23591 						for (idx = 0; idx < lastParents.length; idx++) {
       
 23592 							if (parents[idx] !== lastParents[idx]) {
       
 23593 								break;
       
 23594 							}
       
 23595 						}
       
 23596 
       
 23597 						for (i = lastParents.length - 1; i >= idx; i--) {
       
 23598 							lastCtrl = lastParents[i];
       
 23599 							lastCtrl.fire("mouseleave", {
       
 23600 								target: lastCtrl.getEl()
       
 23601 							});
       
 23602 						}
       
 23603 					}
       
 23604 
       
 23605 					for (i = idx; i < parents.length; i++) {
       
 23606 						ctrl = parents[i];
       
 23607 						ctrl.fire("mouseenter", {
       
 23608 							target: ctrl.getEl()
       
 23609 						});
       
 23610 					}
       
 23611 				}
       
 23612 			}
       
 23613 
       
 23614 			function fixWheelEvent(e) {
       
 23615 				e.preventDefault();
       
 23616 
       
 23617 				if (e.type == "mousewheel") {
       
 23618 					e.deltaY = -1 / 40 * e.wheelDelta;
       
 23619 
       
 23620 					if (e.wheelDeltaX) {
       
 23621 						e.deltaX = -1 / 40 * e.wheelDeltaX;
       
 23622 					}
       
 23623 				} else {
       
 23624 					e.deltaX = 0;
       
 23625 					e.deltaY = e.detail;
       
 23626 				}
       
 23627 
       
 23628 				e = self.fire("wheel", e);
       
 23629 			}
       
 23630 
       
 23631 			self._rendered = true;
       
 23632 
       
 23633 			nativeEvents = self._nativeEvents;
       
 23634 			if (nativeEvents) {
       
 23635 				// Find event root element if it exists
       
 23636 				parents = self.parents().toArray();
       
 23637 				parents.unshift(self);
       
 23638 				for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
       
 23639 					eventRootCtrl = parents[i]._eventsRoot;
       
 23640 				}
       
 23641 
       
 23642 				// Event root wasn't found the use the root control
       
 23643 				if (!eventRootCtrl) {
       
 23644 					eventRootCtrl = parents[parents.length - 1] || self;
       
 23645 				}
       
 23646 
       
 23647 				// Set the eventsRoot property on children that didn't have it
       
 23648 				self._eventsRoot = eventRootCtrl;
       
 23649 				for (l = i, i = 0; i < l; i++) {
       
 23650 					parents[i]._eventsRoot = eventRootCtrl;
       
 23651 				}
       
 23652 
       
 23653 				var eventRootDelegates = eventRootCtrl._delegates;
       
 23654 				if (!eventRootDelegates) {
       
 23655 					eventRootDelegates = eventRootCtrl._delegates = {};
       
 23656 				}
       
 23657 
       
 23658 				// Bind native event delegates
       
 23659 				for (name in nativeEvents) {
       
 23660 					if (!nativeEvents) {
       
 23661 						return false;
       
 23662 					}
       
 23663 
       
 23664 					if (name === "wheel" && !hasWheelEventSupport) {
       
 23665 						if (hasMouseWheelEventSupport) {
       
 23666 							DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
       
 23667 						} else {
       
 23668 							DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
       
 23669 						}
       
 23670 
       
 23671 						continue;
       
 23672 					}
       
 23673 
       
 23674 					// Special treatment for mousenter/mouseleave since these doesn't bubble
       
 23675 					if (name === "mouseenter" || name === "mouseleave") {
       
 23676 						// Fake mousenter/mouseleave
       
 23677 						if (!eventRootCtrl._hasMouseEnter) {
       
 23678 							DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
       
 23679 							DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
       
 23680 							eventRootCtrl._hasMouseEnter = 1;
       
 23681 						}
       
 23682 					} else if (!eventRootDelegates[name]) {
       
 23683 						DomUtils.on(eventRootCtrl.getEl(), name, delegate);
       
 23684 						eventRootDelegates[name] = true;
       
 23685 					}
       
 23686 
       
 23687 					// Remove the event once it's bound
       
 23688 					nativeEvents[name] = false;
       
 23689 				}
       
 23690 			}
       
 23691 		},
       
 23692 
       
 23693 		getRoot: function() {
       
 23694 			var ctrl = this, rootControl, parents = [];
       
 23695 
       
 23696 			while (ctrl) {
       
 23697 				if (ctrl.rootControl) {
       
 23698 					rootControl = ctrl.rootControl;
       
 23699 					break;
       
 23700 				}
       
 23701 
       
 23702 				parents.push(ctrl);
       
 23703 				rootControl = ctrl;
       
 23704 				ctrl = ctrl.parent();
       
 23705 			}
       
 23706 
       
 23707 			if (!rootControl) {
       
 23708 				rootControl = this;
       
 23709 			}
       
 23710 
       
 23711 			var i = parents.length;
       
 23712 			while (i--) {
       
 23713 				parents[i].rootControl = rootControl;
       
 23714 			}
       
 23715 
       
 23716 			return rootControl;
       
 23717 		},
       
 23718 
       
 23719 		/**
       
 23720 		 * Reflows the current control and it's parents.
       
 23721 		 * This should be used after you for example append children to the current control so
       
 23722 		 * that the layout managers know that they need to reposition everything.
       
 23723 		 *
       
 23724 		 * @example
       
 23725 		 * container.append({type: 'button', text: 'My button'}).reflow();
       
 23726 		 *
       
 23727 		 * @method reflow
       
 23728 		 * @return {tinymce.ui.Control} Current control instance.
       
 23729 		 */
       
 23730 		reflow: function() {
       
 23731 			this.repaint();
       
 23732 
       
 23733 			return this;
       
 23734 		}
       
 23735 
       
 23736 		/**
       
 23737 		 * Sets/gets the parent container for the control.
       
 23738 		 *
       
 23739 		 * @method parent
       
 23740 		 * @param {tinymce.ui.Container} parent Optional parent to set.
       
 23741 		 * @return {tinymce.ui.Control} Parent control or the current control on a set action.
       
 23742 		 */
       
 23743 		// parent: function(parent) {} -- Generated
       
 23744 
       
 23745 		/**
       
 23746 		 * Sets/gets the text for the control.
       
 23747 		 *
       
 23748 		 * @method text
       
 23749 		 * @param {String} value Value to set to control.
       
 23750 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 23751 		 */
       
 23752 		// text: function(value) {} -- Generated
       
 23753 
       
 23754 		/**
       
 23755 		 * Sets/gets the width for the control.
       
 23756 		 *
       
 23757 		 * @method width
       
 23758 		 * @param {Number} value Value to set to control.
       
 23759 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 23760 		 */
       
 23761 		// width: function(value) {} -- Generated
       
 23762 
       
 23763 		/**
       
 23764 		 * Sets/gets the height for the control.
       
 23765 		 *
       
 23766 		 * @method height
       
 23767 		 * @param {Number} value Value to set to control.
       
 23768 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 23769 		 */
       
 23770 		// height: function(value) {} -- Generated
       
 23771 
       
 23772 		/**
       
 23773 		 * Sets/gets the disabled state on the control.
       
 23774 		 *
       
 23775 		 * @method disabled
       
 23776 		 * @param {Boolean} state Value to set to control.
       
 23777 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
       
 23778 		 */
       
 23779 		// disabled: function(state) {} -- Generated
       
 23780 
       
 23781 		/**
       
 23782 		 * Sets/gets the active for the control.
       
 23783 		 *
       
 23784 		 * @method active
       
 23785 		 * @param {Boolean} state Value to set to control.
       
 23786 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
       
 23787 		 */
       
 23788 		// active: function(state) {} -- Generated
       
 23789 
       
 23790 		/**
       
 23791 		 * Sets/gets the name for the control.
       
 23792 		 *
       
 23793 		 * @method name
       
 23794 		 * @param {String} value Value to set to control.
       
 23795 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 23796 		 */
       
 23797 		// name: function(value) {} -- Generated
       
 23798 
       
 23799 		/**
       
 23800 		 * Sets/gets the title for the control.
       
 23801 		 *
       
 23802 		 * @method title
       
 23803 		 * @param {String} value Value to set to control.
       
 23804 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 23805 		 */
       
 23806 		// title: function(value) {} -- Generated
       
 23807 	});
       
 23808 
       
 23809 	return Control;
       
 23810 });
       
 23811 
       
 23812 // Included from: js/tinymce/classes/ui/Factory.js
       
 23813 
       
 23814 /**
       
 23815  * Factory.js
       
 23816  *
       
 23817  * Copyright, Moxiecode Systems AB
       
 23818  * Released under LGPL License.
       
 23819  *
       
 23820  * License: http://www.tinymce.com/license
       
 23821  * Contributing: http://www.tinymce.com/contributing
       
 23822  */
       
 23823 
       
 23824 /*global tinymce:true */
       
 23825 
       
 23826 /**
       
 23827  * This class is a factory for control instances. This enables you
       
 23828  * to create instances of controls without having to require the UI controls directly.
       
 23829  *
       
 23830  * It also allow you to override or add new control types.
       
 23831  *
       
 23832  * @class tinymce.ui.Factory
       
 23833  */
       
 23834 define("tinymce/ui/Factory", [], function() {
       
 23835 	"use strict";
       
 23836 
       
 23837 	var types = {}, namespaceInit;
       
 23838 
       
 23839 	return {
       
 23840 		/**
       
 23841 		 * Adds a new control instance type to the factory.
       
 23842 		 *
       
 23843 		 * @method add
       
 23844 		 * @param {String} type Type name for example "button".
       
 23845 		 * @param {function} typeClass Class type function.
       
 23846 		 */
       
 23847 		add: function(type, typeClass) {
       
 23848 			types[type.toLowerCase()] = typeClass;
       
 23849 		},
       
 23850 
       
 23851 		/**
       
 23852 		 * Returns true/false if the specified type exists or not.
       
 23853 		 *
       
 23854 		 * @method has
       
 23855 		 * @param {String} type Type to look for.
       
 23856 		 * @return {Boolean} true/false if the control by name exists.
       
 23857 		 */
       
 23858 		has: function(type) {
       
 23859 			return !!types[type.toLowerCase()];
       
 23860 		},
       
 23861 
       
 23862 		/**
       
 23863 		 * Creates a new control instance based on the settings provided. The instance created will be
       
 23864 		 * based on the specified type property it can also create whole structures of components out of
       
 23865 		 * the specified JSON object.
       
 23866 		 *
       
 23867 		 * @example
       
 23868 		 * tinymce.ui.Factory.create({
       
 23869 		 *     type: 'button',
       
 23870 		 *     text: 'Hello world!'
       
 23871 		 * });
       
 23872 		 *
       
 23873 		 * @method create
       
 23874 		 * @param {Object/String} settings Name/Value object with items used to create the type.
       
 23875 		 * @return {tinymce.ui.Control} Control instance based on the specified type.
       
 23876 		 */
       
 23877 		create: function(type, settings) {
       
 23878 			var ControlType, name, namespace;
       
 23879 
       
 23880 			// Build type lookup
       
 23881 			if (!namespaceInit) {
       
 23882 				namespace = tinymce.ui;
       
 23883 
       
 23884 				for (name in namespace) {
       
 23885 					types[name.toLowerCase()] = namespace[name];
       
 23886 				}
       
 23887 
       
 23888 				namespaceInit = true;
       
 23889 			}
       
 23890 
       
 23891 			// If string is specified then use it as the type
       
 23892 			if (typeof type == 'string') {
       
 23893 				settings = settings || {};
       
 23894 				settings.type = type;
       
 23895 			} else {
       
 23896 				settings = type;
       
 23897 				type = settings.type;
       
 23898 			}
       
 23899 
       
 23900 			// Find control type
       
 23901 			type = type.toLowerCase();
       
 23902 			ControlType = types[type];
       
 23903 
       
 23904 			// #if debug
       
 23905 
       
 23906 			if (!ControlType) {
       
 23907 				throw new Error("Could not find control by type: " + type);
       
 23908 			}
       
 23909 
       
 23910 			// #endif
       
 23911 
       
 23912 			ControlType = new ControlType(settings);
       
 23913 			ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine
       
 23914 
       
 23915 			return ControlType;
       
 23916 		}
       
 23917 	};
       
 23918 });
       
 23919 
       
 23920 // Included from: js/tinymce/classes/ui/KeyboardNavigation.js
       
 23921 
       
 23922 /**
       
 23923  * KeyboardNavigation.js
       
 23924  *
       
 23925  * Copyright, Moxiecode Systems AB
       
 23926  * Released under LGPL License.
       
 23927  *
       
 23928  * License: http://www.tinymce.com/license
       
 23929  * Contributing: http://www.tinymce.com/contributing
       
 23930  */
       
 23931 
       
 23932 /**
       
 23933  * This class handles keyboard navigation of controls and elements.
       
 23934  *
       
 23935  * @class tinymce.ui.KeyboardNavigation
       
 23936  */
       
 23937 define("tinymce/ui/KeyboardNavigation", [
       
 23938 ], function() {
       
 23939 	"use strict";
       
 23940 
       
 23941 	/**
       
 23942 	 * This class handles all keyboard navigation for WAI-ARIA support. Each root container
       
 23943 	 * gets an instance of this class.
       
 23944 	 *
       
 23945 	 * @constructor
       
 23946 	 */
       
 23947 	return function(settings) {
       
 23948 		var root = settings.root, focusedElement, focusedControl;
       
 23949 
       
 23950 		try {
       
 23951 			focusedElement = document.activeElement;
       
 23952 		} catch (ex) {
       
 23953 			// IE sometimes fails to return a proper element
       
 23954 			focusedElement = document.body;
       
 23955 		}
       
 23956 
       
 23957 		focusedControl = root.getParentCtrl(focusedElement);
       
 23958 
       
 23959 		/**
       
 23960 		 * Returns the currently focused elements wai aria role of the currently
       
 23961 		 * focused element or specified element.
       
 23962 		 *
       
 23963 		 * @private
       
 23964 		 * @param {Element} elm Optional element to get role from.
       
 23965 		 * @return {String} Role of specified element.
       
 23966 		 */
       
 23967 		function getRole(elm) {
       
 23968 			elm = elm || focusedElement;
       
 23969 
       
 23970 			return elm && elm.getAttribute('role');
       
 23971 		}
       
 23972 
       
 23973 		/**
       
 23974 		 * Returns the wai role of the parent element of the currently
       
 23975 		 * focused element or specified element.
       
 23976 		 *
       
 23977 		 * @private
       
 23978 		 * @param {Element} elm Optional element to get parent role from.
       
 23979 		 * @return {String} Role of the first parent that has a role.
       
 23980 		 */
       
 23981 		function getParentRole(elm) {
       
 23982 			var role, parent = elm || focusedElement;
       
 23983 
       
 23984 			while ((parent = parent.parentNode)) {
       
 23985 				if ((role = getRole(parent))) {
       
 23986 					return role;
       
 23987 				}
       
 23988 			}
       
 23989 		}
       
 23990 
       
 23991 		/**
       
 23992 		 * Returns a wai aria property by name for example aria-selected.
       
 23993 		 *
       
 23994 		 * @private
       
 23995 		 * @param {String} name Name of the aria property to get for example "disabled".
       
 23996 		 * @return {String} Aria property value.
       
 23997 		 */
       
 23998 		function getAriaProp(name) {
       
 23999 			var elm = focusedElement;
       
 24000 
       
 24001 			if (elm) {
       
 24002 				return elm.getAttribute('aria-' + name);
       
 24003 			}
       
 24004 		}
       
 24005 
       
 24006 		/**
       
 24007 		 * Is the element a text input element or not.
       
 24008 		 *
       
 24009 		 * @private
       
 24010 		 * @param {Element} elm Element to check if it's an text input element or not.
       
 24011 		 * @return {Boolean} True/false if the element is a text element or not.
       
 24012 		 */
       
 24013 		function isTextInputElement(elm) {
       
 24014 			var tagName = elm.tagName.toUpperCase();
       
 24015 
       
 24016 			// Notice: since type can be "email" etc we don't check the type
       
 24017 			// So all input elements gets treated as text input elements
       
 24018 			return tagName == "INPUT" || tagName == "TEXTAREA";
       
 24019 		}
       
 24020 
       
 24021 		/**
       
 24022 		 * Returns true/false if the specified element can be focused or not.
       
 24023 		 *
       
 24024 		 * @private
       
 24025 		 * @param {Element} elm DOM element to check if it can be focused or not.
       
 24026 		 * @return {Boolean} True/false if the element can have focus.
       
 24027 		 */
       
 24028 		function canFocus(elm) {
       
 24029 			if (isTextInputElement(elm) && !elm.hidden) {
       
 24030 				return true;
       
 24031 			}
       
 24032 
       
 24033 			if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(getRole(elm))) {
       
 24034 				return true;
       
 24035 			}
       
 24036 
       
 24037 			return false;
       
 24038 		}
       
 24039 
       
 24040 		/**
       
 24041 		 * Returns an array of focusable visible elements within the specified container element.
       
 24042 		 *
       
 24043 		 * @private
       
 24044 		 * @param {Element} elm DOM element to find focusable elements within.
       
 24045 		 * @return {Array} Array of focusable elements.
       
 24046 		 */
       
 24047 		function getFocusElements(elm) {
       
 24048 			var elements = [];
       
 24049 
       
 24050 			function collect(elm) {
       
 24051 				if (elm.nodeType != 1 || elm.style.display == 'none') {
       
 24052 					return;
       
 24053 				}
       
 24054 
       
 24055 				if (canFocus(elm)) {
       
 24056 					elements.push(elm);
       
 24057 				}
       
 24058 
       
 24059 				for (var i = 0; i < elm.childNodes.length; i++) {
       
 24060 					collect(elm.childNodes[i]);
       
 24061 				}
       
 24062 			}
       
 24063 
       
 24064 			collect(elm || root.getEl());
       
 24065 
       
 24066 			return elements;
       
 24067 		}
       
 24068 
       
 24069 		/**
       
 24070 		 * Returns the navigation root control for the specified control. The navigation root
       
 24071 		 * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group.
       
 24072 		 * It will look for parents of the specified target control or the currently focused control if this option is omitted.
       
 24073 		 *
       
 24074 		 * @private
       
 24075 		 * @param {tinymce.ui.Control} targetControl Optional target control to find root of.
       
 24076 		 * @return {tinymce.ui.Control} Navigation root control.
       
 24077 		 */
       
 24078 		function getNavigationRoot(targetControl) {
       
 24079 			var navigationRoot, controls;
       
 24080 
       
 24081 			targetControl = targetControl || focusedControl;
       
 24082 			controls = targetControl.parents().toArray();
       
 24083 			controls.unshift(targetControl);
       
 24084 
       
 24085 			for (var i = 0; i < controls.length; i++) {
       
 24086 				navigationRoot = controls[i];
       
 24087 
       
 24088 				if (navigationRoot.settings.ariaRoot) {
       
 24089 					break;
       
 24090 				}
       
 24091 			}
       
 24092 
       
 24093 			return navigationRoot;
       
 24094 		}
       
 24095 
       
 24096 		/**
       
 24097 		 * Focuses the first item in the specified targetControl element or the last aria index if the
       
 24098 		 * navigation root has the ariaRemember option enabled.
       
 24099 		 *
       
 24100 		 * @private
       
 24101 		 * @param {tinymce.ui.Control} targetControl Target control to focus the first item in.
       
 24102 		 */
       
 24103 		function focusFirst(targetControl) {
       
 24104 			var navigationRoot = getNavigationRoot(targetControl);
       
 24105 			var focusElements = getFocusElements(navigationRoot.getEl());
       
 24106 
       
 24107 			if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) {
       
 24108 				moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements);
       
 24109 			} else {
       
 24110 				moveFocusToIndex(0, focusElements);
       
 24111 			}
       
 24112 		}
       
 24113 
       
 24114 		/**
       
 24115 		 * Moves the focus to the specified index within the elements list.
       
 24116 		 * This will scope the index to the size of the element list if it changed.
       
 24117 		 *
       
 24118 		 * @private
       
 24119 		 * @param {Number} idx Specified index to move to.
       
 24120 		 * @param {Array} elements Array with dom elements to move focus within.
       
 24121 		 * @return {Number} Input index or a changed index if it was out of range.
       
 24122 		 */
       
 24123 		function moveFocusToIndex(idx, elements) {
       
 24124 			if (idx < 0) {
       
 24125 				idx = elements.length - 1;
       
 24126 			} else if (idx >= elements.length) {
       
 24127 				idx = 0;
       
 24128 			}
       
 24129 
       
 24130 			if (elements[idx]) {
       
 24131 				elements[idx].focus();
       
 24132 			}
       
 24133 
       
 24134 			return idx;
       
 24135 		}
       
 24136 
       
 24137 		/**
       
 24138 		 * Moves the focus forwards or backwards.
       
 24139 		 *
       
 24140 		 * @private
       
 24141 		 * @param {Number} dir Direction to move in positive means forward, negative means backwards.
       
 24142 		 * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements.
       
 24143 		 */
       
 24144 		function moveFocus(dir, elements) {
       
 24145 			var idx = -1, navigationRoot = getNavigationRoot();
       
 24146 
       
 24147 			elements = elements || getFocusElements(navigationRoot.getEl());
       
 24148 
       
 24149 			for (var i = 0; i < elements.length; i++) {
       
 24150 				if (elements[i] === focusedElement) {
       
 24151 					idx = i;
       
 24152 				}
       
 24153 			}
       
 24154 
       
 24155 			idx += dir;
       
 24156 			navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements);
       
 24157 		}
       
 24158 
       
 24159 		/**
       
 24160 		 * Moves the focus to the left this is called by the left key.
       
 24161 		 *
       
 24162 		 * @private
       
 24163 		 */
       
 24164 		function left() {
       
 24165 			var parentRole = getParentRole();
       
 24166 
       
 24167 			if (parentRole == "tablist") {
       
 24168 				moveFocus(-1, getFocusElements(focusedElement.parentNode));
       
 24169 			} else if (focusedControl.parent().submenu) {
       
 24170 				cancel();
       
 24171 			} else {
       
 24172 				moveFocus(-1);
       
 24173 			}
       
 24174 		}
       
 24175 
       
 24176 		/**
       
 24177 		 * Moves the focus to the right this is called by the right key.
       
 24178 		 *
       
 24179 		 * @private
       
 24180 		 */
       
 24181 		function right() {
       
 24182 			var role = getRole(), parentRole = getParentRole();
       
 24183 
       
 24184 			if (parentRole == "tablist") {
       
 24185 				moveFocus(1, getFocusElements(focusedElement.parentNode));
       
 24186 			} else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) {
       
 24187 				enter();
       
 24188 			} else {
       
 24189 				moveFocus(1);
       
 24190 			}
       
 24191 		}
       
 24192 
       
 24193 		/**
       
 24194 		 * Moves the focus to the up this is called by the up key.
       
 24195 		 *
       
 24196 		 * @private
       
 24197 		 */
       
 24198 		function up() {
       
 24199 			moveFocus(-1);
       
 24200 		}
       
 24201 
       
 24202 		/**
       
 24203 		 * Moves the focus to the up this is called by the down key.
       
 24204 		 *
       
 24205 		 * @private
       
 24206 		 */
       
 24207 		function down() {
       
 24208 			var role = getRole(), parentRole = getParentRole();
       
 24209 
       
 24210 			if (role == "menuitem" && parentRole == "menubar") {
       
 24211 				enter();
       
 24212 			} else if (role == "button" && getAriaProp('haspopup')) {
       
 24213 				enter({key: 'down'});
       
 24214 			} else {
       
 24215 				moveFocus(1);
       
 24216 			}
       
 24217 		}
       
 24218 
       
 24219 		/**
       
 24220 		 * Moves the focus to the next item or previous item depending on shift key.
       
 24221 		 *
       
 24222 		 * @private
       
 24223 		 * @param {DOMEvent} e DOM event object.
       
 24224 		 */
       
 24225 		function tab(e) {
       
 24226 			var parentRole = getParentRole();
       
 24227 
       
 24228 			if (parentRole == "tablist") {
       
 24229 				var elm = getFocusElements(focusedControl.getEl('body'))[0];
       
 24230 
       
 24231 				if (elm) {
       
 24232 					elm.focus();
       
 24233 				}
       
 24234 			} else {
       
 24235 				moveFocus(e.shiftKey ? -1 : 1);
       
 24236 			}
       
 24237 		}
       
 24238 
       
 24239 		/**
       
 24240 		 * Calls the cancel event on the currently focused control. This is normally done using the Esc key.
       
 24241 		 *
       
 24242 		 * @private
       
 24243 		 */
       
 24244 		function cancel() {
       
 24245 			focusedControl.fire('cancel');
       
 24246 		}
       
 24247 
       
 24248 		/**
       
 24249 		 * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys.
       
 24250 		 *
       
 24251 		 * @private
       
 24252 		 * @param {Object} aria Optional aria data to pass along with the enter event.
       
 24253 		 */
       
 24254 		function enter(aria) {
       
 24255 			aria = aria || {};
       
 24256 			focusedControl.fire('click', {target: focusedElement, aria: aria});
       
 24257 		}
       
 24258 
       
 24259 		root.on('keydown', function(e) {
       
 24260 			function handleNonTabOrEscEvent(e, handler) {
       
 24261 				// Ignore non tab keys for text elements
       
 24262 				if (isTextInputElement(focusedElement)) {
       
 24263 					return;
       
 24264 				}
       
 24265 
       
 24266 				if (handler(e) !== false) {
       
 24267 					e.preventDefault();
       
 24268 				}
       
 24269 			}
       
 24270 
       
 24271 			if (e.isDefaultPrevented()) {
       
 24272 				return;
       
 24273 			}
       
 24274 
       
 24275 			switch (e.keyCode) {
       
 24276 				case 37: // DOM_VK_LEFT
       
 24277 					handleNonTabOrEscEvent(e, left);
       
 24278 					break;
       
 24279 
       
 24280 				case 39: // DOM_VK_RIGHT
       
 24281 					handleNonTabOrEscEvent(e, right);
       
 24282 					break;
       
 24283 
       
 24284 				case 38: // DOM_VK_UP
       
 24285 					handleNonTabOrEscEvent(e, up);
       
 24286 					break;
       
 24287 
       
 24288 				case 40: // DOM_VK_DOWN
       
 24289 					handleNonTabOrEscEvent(e, down);
       
 24290 					break;
       
 24291 
       
 24292 				case 27: // DOM_VK_ESCAPE
       
 24293 					cancel();
       
 24294 					break;
       
 24295 
       
 24296 				case 14: // DOM_VK_ENTER
       
 24297 				case 13: // DOM_VK_RETURN
       
 24298 				case 32: // DOM_VK_SPACE
       
 24299 					handleNonTabOrEscEvent(e, enter);
       
 24300 					break;
       
 24301 
       
 24302 				case 9: // DOM_VK_TAB
       
 24303 					if (tab(e) !== false) {
       
 24304 						e.preventDefault();
       
 24305 					}
       
 24306 					break;
       
 24307 			}
       
 24308 		});
       
 24309 
       
 24310 		root.on('focusin', function(e) {
       
 24311 			focusedElement = e.target;
       
 24312 			focusedControl = e.control;
       
 24313 		});
       
 24314 
       
 24315 		return {
       
 24316 			focusFirst: focusFirst
       
 24317 		};
       
 24318 	};
       
 24319 });
       
 24320 
       
 24321 // Included from: js/tinymce/classes/ui/Container.js
       
 24322 
       
 24323 /**
       
 24324  * Container.js
       
 24325  *
       
 24326  * Copyright, Moxiecode Systems AB
       
 24327  * Released under LGPL License.
       
 24328  *
       
 24329  * License: http://www.tinymce.com/license
       
 24330  * Contributing: http://www.tinymce.com/contributing
       
 24331  */
       
 24332 
       
 24333 /**
       
 24334  * Container control. This is extended by all controls that can have
       
 24335  * children such as panels etc. You can also use this class directly as an
       
 24336  * generic container instance. The container doesn't have any specific role or style.
       
 24337  *
       
 24338  * @-x-less Container.less
       
 24339  * @class tinymce.ui.Container
       
 24340  * @extends tinymce.ui.Control
       
 24341  */
       
 24342 define("tinymce/ui/Container", [
       
 24343 	"tinymce/ui/Control",
       
 24344 	"tinymce/ui/Collection",
       
 24345 	"tinymce/ui/Selector",
       
 24346 	"tinymce/ui/Factory",
       
 24347 	"tinymce/ui/KeyboardNavigation",
       
 24348 	"tinymce/util/Tools",
       
 24349 	"tinymce/ui/DomUtils"
       
 24350 ], function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, DomUtils) {
       
 24351 	"use strict";
       
 24352 
       
 24353 	var selectorCache = {};
       
 24354 
       
 24355 	return Control.extend({
       
 24356 		layout: '',
       
 24357 		innerClass: 'container-inner',
       
 24358 
       
 24359 		/**
       
 24360 		 * Constructs a new control instance with the specified settings.
       
 24361 		 *
       
 24362 		 * @constructor
       
 24363 		 * @param {Object} settings Name/value object with settings.
       
 24364 		 * @setting {Array} items Items to add to container in JSON format or control instances.
       
 24365 		 * @setting {String} layout Layout manager by name to use.
       
 24366 		 * @setting {Object} defaults Default settings to apply to all items.
       
 24367 		 */
       
 24368 		init: function(settings) {
       
 24369 			var self = this;
       
 24370 
       
 24371 			self._super(settings);
       
 24372 			settings = self.settings;
       
 24373 			self._fixed = settings.fixed;
       
 24374 			self._items = new Collection();
       
 24375 
       
 24376 			if (self.isRtl()) {
       
 24377 				self.addClass('rtl');
       
 24378 			}
       
 24379 
       
 24380 			self.addClass('container');
       
 24381 			self.addClass('container-body', 'body');
       
 24382 
       
 24383 			if (settings.containerCls) {
       
 24384 				self.addClass(settings.containerCls);
       
 24385 			}
       
 24386 
       
 24387 			self._layout = Factory.create((settings.layout || self.layout) + 'layout');
       
 24388 
       
 24389 			if (self.settings.items) {
       
 24390 				self.add(self.settings.items);
       
 24391 			}
       
 24392 
       
 24393 			// TODO: Fix this!
       
 24394 			self._hasBody = true;
       
 24395 		},
       
 24396 
       
 24397 		/**
       
 24398 		 * Returns a collection of child items that the container currently have.
       
 24399 		 *
       
 24400 		 * @method items
       
 24401 		 * @return {tinymce.ui.Collection} Control collection direct child controls.
       
 24402 		 */
       
 24403 		items: function() {
       
 24404 			return this._items;
       
 24405 		},
       
 24406 
       
 24407 		/**
       
 24408 		 * Find child controls by selector.
       
 24409 		 *
       
 24410 		 * @method find
       
 24411 		 * @param {String} selector Selector CSS pattern to find children by.
       
 24412 		 * @return {tinymce.ui.Collection} Control collection with child controls.
       
 24413 		 */
       
 24414 		find: function(selector) {
       
 24415 			selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector);
       
 24416 
       
 24417 			return selector.find(this);
       
 24418 		},
       
 24419 
       
 24420 		/**
       
 24421 		 * Adds one or many items to the current container. This will create instances of
       
 24422 		 * the object representations if needed.
       
 24423 		 *
       
 24424 		 * @method add
       
 24425 		 * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container.
       
 24426 		 * @return {tinymce.ui.Collection} Current collection control.
       
 24427 		 */
       
 24428 		add: function(items) {
       
 24429 			var self = this;
       
 24430 
       
 24431 			self.items().add(self.create(items)).parent(self);
       
 24432 
       
 24433 			return self;
       
 24434 		},
       
 24435 
       
 24436 		/**
       
 24437 		 * Focuses the current container instance. This will look
       
 24438 		 * for the first control in the container and focus that.
       
 24439 		 *
       
 24440 		 * @method focus
       
 24441 		 * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not.
       
 24442 		 * @return {tinymce.ui.Collection} Current instance.
       
 24443 		 */
       
 24444 		focus: function(keyboard) {
       
 24445 			var self = this, focusCtrl, keyboardNav, items;
       
 24446 
       
 24447 			if (keyboard) {
       
 24448 				keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav;
       
 24449 
       
 24450 				if (keyboardNav) {
       
 24451 					keyboardNav.focusFirst(self);
       
 24452 					return;
       
 24453 				}
       
 24454 			}
       
 24455 
       
 24456 			items = self.find('*');
       
 24457 
       
 24458 			// TODO: Figure out a better way to auto focus alert dialog buttons
       
 24459 			if (self.statusbar) {
       
 24460 				items.add(self.statusbar.items());
       
 24461 			}
       
 24462 
       
 24463 			items.each(function(ctrl) {
       
 24464 				if (ctrl.settings.autofocus) {
       
 24465 					focusCtrl = null;
       
 24466 					return false;
       
 24467 				}
       
 24468 
       
 24469 				if (ctrl.canFocus) {
       
 24470 					focusCtrl = focusCtrl || ctrl;
       
 24471 				}
       
 24472 			});
       
 24473 
       
 24474 			if (focusCtrl) {
       
 24475 				focusCtrl.focus();
       
 24476 			}
       
 24477 
       
 24478 			return self;
       
 24479 		},
       
 24480 
       
 24481 		/**
       
 24482 		 * Replaces the specified child control with a new control.
       
 24483 		 *
       
 24484 		 * @method replace
       
 24485 		 * @param {tinymce.ui.Control} oldItem Old item to be replaced.
       
 24486 		 * @param {tinymce.ui.Control} newItem New item to be inserted.
       
 24487 		 */
       
 24488 		replace: function(oldItem, newItem) {
       
 24489 			var ctrlElm, items = this.items(), i = items.length;
       
 24490 
       
 24491 			// Replace the item in collection
       
 24492 			while (i--) {
       
 24493 				if (items[i] === oldItem) {
       
 24494 					items[i] = newItem;
       
 24495 					break;
       
 24496 				}
       
 24497 			}
       
 24498 
       
 24499 			if (i >= 0) {
       
 24500 				// Remove new item from DOM
       
 24501 				ctrlElm = newItem.getEl();
       
 24502 				if (ctrlElm) {
       
 24503 					ctrlElm.parentNode.removeChild(ctrlElm);
       
 24504 				}
       
 24505 
       
 24506 				// Remove old item from DOM
       
 24507 				ctrlElm = oldItem.getEl();
       
 24508 				if (ctrlElm) {
       
 24509 					ctrlElm.parentNode.removeChild(ctrlElm);
       
 24510 				}
       
 24511 			}
       
 24512 
       
 24513 			// Adopt the item
       
 24514 			newItem.parent(this);
       
 24515 		},
       
 24516 
       
 24517 		/**
       
 24518 		 * Creates the specified items. If any of the items is plain JSON style objects
       
 24519 		 * it will convert these into real tinymce.ui.Control instances.
       
 24520 		 *
       
 24521 		 * @method create
       
 24522 		 * @param {Array} items Array of items to convert into control instances.
       
 24523 		 * @return {Array} Array with control instances.
       
 24524 		 */
       
 24525 		create: function(items) {
       
 24526 			var self = this, settings, ctrlItems = [];
       
 24527 
       
 24528 			// Non array structure, then force it into an array
       
 24529 			if (!Tools.isArray(items)) {
       
 24530 				items = [items];
       
 24531 			}
       
 24532 
       
 24533 			// Add default type to each child control
       
 24534 			Tools.each(items, function(item) {
       
 24535 				if (item) {
       
 24536 					// Construct item if needed
       
 24537 					if (!(item instanceof Control)) {
       
 24538 						// Name only then convert it to an object
       
 24539 						if (typeof item == "string") {
       
 24540 							item = {type: item};
       
 24541 						}
       
 24542 
       
 24543 						// Create control instance based on input settings and default settings
       
 24544 						settings = Tools.extend({}, self.settings.defaults, item);
       
 24545 						item.type = settings.type = settings.type || item.type || self.settings.defaultType ||
       
 24546 							(settings.defaults ? settings.defaults.type : null);
       
 24547 						item = Factory.create(settings);
       
 24548 					}
       
 24549 
       
 24550 					ctrlItems.push(item);
       
 24551 				}
       
 24552 			});
       
 24553 
       
 24554 			return ctrlItems;
       
 24555 		},
       
 24556 
       
 24557 		/**
       
 24558 		 * Renders new control instances.
       
 24559 		 *
       
 24560 		 * @private
       
 24561 		 */
       
 24562 		renderNew: function() {
       
 24563 			var self = this;
       
 24564 
       
 24565 			// Render any new items
       
 24566 			self.items().each(function(ctrl, index) {
       
 24567 				var containerElm, fragment;
       
 24568 
       
 24569 				ctrl.parent(self);
       
 24570 
       
 24571 				if (!ctrl._rendered) {
       
 24572 					containerElm = self.getEl('body');
       
 24573 					fragment = DomUtils.createFragment(ctrl.renderHtml());
       
 24574 
       
 24575 					// Insert or append the item
       
 24576 					if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) {
       
 24577 						containerElm.insertBefore(fragment, containerElm.childNodes[index]);
       
 24578 					} else {
       
 24579 						containerElm.appendChild(fragment);
       
 24580 					}
       
 24581 
       
 24582 					ctrl.postRender();
       
 24583 				}
       
 24584 			});
       
 24585 
       
 24586 			self._layout.applyClasses(self);
       
 24587 			self._lastRect = null;
       
 24588 
       
 24589 			return self;
       
 24590 		},
       
 24591 
       
 24592 		/**
       
 24593 		 * Appends new instances to the current container.
       
 24594 		 *
       
 24595 		 * @method append
       
 24596 		 * @param {Array/tinymce.ui.Collection} items Array if controls to append.
       
 24597 		 * @return {tinymce.ui.Container} Current container instance.
       
 24598 		 */
       
 24599 		append: function(items) {
       
 24600 			return this.add(items).renderNew();
       
 24601 		},
       
 24602 
       
 24603 		/**
       
 24604 		 * Prepends new instances to the current container.
       
 24605 		 *
       
 24606 		 * @method prepend
       
 24607 		 * @param {Array/tinymce.ui.Collection} items Array if controls to prepend.
       
 24608 		 * @return {tinymce.ui.Container} Current container instance.
       
 24609 		 */
       
 24610 		prepend: function(items) {
       
 24611 			var self = this;
       
 24612 
       
 24613 			self.items().set(self.create(items).concat(self.items().toArray()));
       
 24614 
       
 24615 			return self.renderNew();
       
 24616 		},
       
 24617 
       
 24618 		/**
       
 24619 		 * Inserts an control at a specific index.
       
 24620 		 *
       
 24621 		 * @method insert
       
 24622 		 * @param {Array/tinymce.ui.Collection} items Array if controls to insert.
       
 24623 		 * @param {Number} index Index to insert controls at.
       
 24624 		 * @param {Boolean} [before=false] Inserts controls before the index.
       
 24625 		 */
       
 24626 		insert: function(items, index, before) {
       
 24627 			var self = this, curItems, beforeItems, afterItems;
       
 24628 
       
 24629 			items = self.create(items);
       
 24630 			curItems = self.items();
       
 24631 
       
 24632 			if (!before && index < curItems.length - 1) {
       
 24633 				index += 1;
       
 24634 			}
       
 24635 
       
 24636 			if (index >= 0 && index < curItems.length) {
       
 24637 				beforeItems = curItems.slice(0, index).toArray();
       
 24638 				afterItems = curItems.slice(index).toArray();
       
 24639 				curItems.set(beforeItems.concat(items, afterItems));
       
 24640 			}
       
 24641 
       
 24642 			return self.renderNew();
       
 24643 		},
       
 24644 
       
 24645 		/**
       
 24646 		 * Populates the form fields from the specified JSON data object.
       
 24647 		 *
       
 24648 		 * Control items in the form that matches the data will have it's value set.
       
 24649 		 *
       
 24650 		 * @method fromJSON
       
 24651 		 * @param {Object} data JSON data object to set control values by.
       
 24652 		 * @return {tinymce.ui.Container} Current form instance.
       
 24653 		 */
       
 24654 		fromJSON: function(data) {
       
 24655 			var self = this;
       
 24656 
       
 24657 			for (var name in data) {
       
 24658 				self.find('#' + name).value(data[name]);
       
 24659 			}
       
 24660 
       
 24661 			return self;
       
 24662 		},
       
 24663 
       
 24664 		/**
       
 24665 		 * Serializes the form into a JSON object by getting all items
       
 24666 		 * that has a name and a value.
       
 24667 		 *
       
 24668 		 * @method toJSON
       
 24669 		 * @return {Object} JSON object with form data.
       
 24670 		 */
       
 24671 		toJSON: function() {
       
 24672 			var self = this, data = {};
       
 24673 
       
 24674 			self.find('*').each(function(ctrl) {
       
 24675 				var name = ctrl.name(), value = ctrl.value();
       
 24676 
       
 24677 				if (name && typeof value != "undefined") {
       
 24678 					data[name] = value;
       
 24679 				}
       
 24680 			});
       
 24681 
       
 24682 			return data;
       
 24683 		},
       
 24684 
       
 24685 		preRender: function() {
       
 24686 		},
       
 24687 
       
 24688 		/**
       
 24689 		 * Renders the control as a HTML string.
       
 24690 		 *
       
 24691 		 * @method renderHtml
       
 24692 		 * @return {String} HTML representing the control.
       
 24693 		 */
       
 24694 		renderHtml: function() {
       
 24695 			var self = this, layout = self._layout, role = this.settings.role;
       
 24696 
       
 24697 			self.preRender();
       
 24698 			layout.preRender(self);
       
 24699 
       
 24700 			return (
       
 24701 				'<div id="' + self._id + '" class="' + self.classes() + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' +
       
 24702 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 24703 						(self.settings.html || '') + layout.renderHtml(self) +
       
 24704 					'</div>' +
       
 24705 				'</div>'
       
 24706 			);
       
 24707 		},
       
 24708 
       
 24709 		/**
       
 24710 		 * Post render method. Called after the control has been rendered to the target.
       
 24711 		 *
       
 24712 		 * @method postRender
       
 24713 		 * @return {tinymce.ui.Container} Current combobox instance.
       
 24714 		 */
       
 24715 		postRender: function() {
       
 24716 			var self = this, box;
       
 24717 
       
 24718 			self.items().exec('postRender');
       
 24719 			self._super();
       
 24720 
       
 24721 			self._layout.postRender(self);
       
 24722 			self._rendered = true;
       
 24723 
       
 24724 			if (self.settings.style) {
       
 24725 				DomUtils.css(self.getEl(), self.settings.style);
       
 24726 			}
       
 24727 
       
 24728 			if (self.settings.border) {
       
 24729 				box = self.borderBox();
       
 24730 				DomUtils.css(self.getEl(), {
       
 24731 					'border-top-width': box.top,
       
 24732 					'border-right-width': box.right,
       
 24733 					'border-bottom-width': box.bottom,
       
 24734 					'border-left-width': box.left
       
 24735 				});
       
 24736 			}
       
 24737 
       
 24738 			if (!self.parent()) {
       
 24739 				self.keyboardNav = new KeyboardNavigation({
       
 24740 					root: self
       
 24741 				});
       
 24742 			}
       
 24743 
       
 24744 			return self;
       
 24745 		},
       
 24746 
       
 24747 		/**
       
 24748 		 * Initializes the current controls layout rect.
       
 24749 		 * This will be executed by the layout managers to determine the
       
 24750 		 * default minWidth/minHeight etc.
       
 24751 		 *
       
 24752 		 * @method initLayoutRect
       
 24753 		 * @return {Object} Layout rect instance.
       
 24754 		 */
       
 24755 		initLayoutRect: function() {
       
 24756 			var self = this, layoutRect = self._super();
       
 24757 
       
 24758 			// Recalc container size by asking layout manager
       
 24759 			self._layout.recalc(self);
       
 24760 
       
 24761 			return layoutRect;
       
 24762 		},
       
 24763 
       
 24764 		/**
       
 24765 		 * Recalculates the positions of the controls in the current container.
       
 24766 		 * This is invoked by the reflow method and shouldn't be called directly.
       
 24767 		 *
       
 24768 		 * @method recalc
       
 24769 		 */
       
 24770 		recalc: function() {
       
 24771 			var self = this, rect = self._layoutRect, lastRect = self._lastRect;
       
 24772 
       
 24773 			if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
       
 24774 				self._layout.recalc(self);
       
 24775 				rect = self.layoutRect();
       
 24776 				self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h};
       
 24777 				return true;
       
 24778 			}
       
 24779 		},
       
 24780 
       
 24781 		/**
       
 24782 		 * Reflows the current container and it's children and possible parents.
       
 24783 		 * This should be used after you for example append children to the current control so
       
 24784 		 * that the layout managers know that they need to reposition everything.
       
 24785 		 *
       
 24786 		 * @example
       
 24787 		 * container.append({type: 'button', text: 'My button'}).reflow();
       
 24788 		 *
       
 24789 		 * @method reflow
       
 24790 		 * @return {tinymce.ui.Container} Current container instance.
       
 24791 		 */
       
 24792 		reflow: function() {
       
 24793 			var i;
       
 24794 
       
 24795 			if (this.visible()) {
       
 24796 				Control.repaintControls = [];
       
 24797 				Control.repaintControls.map = {};
       
 24798 
       
 24799 				this.recalc();
       
 24800 				i = Control.repaintControls.length;
       
 24801 
       
 24802 				while (i--) {
       
 24803 					Control.repaintControls[i].repaint();
       
 24804 				}
       
 24805 
       
 24806 				// TODO: Fix me!
       
 24807 				if (this.settings.layout !== "flow" && this.settings.layout !== "stack") {
       
 24808 					this.repaint();
       
 24809 				}
       
 24810 
       
 24811 				Control.repaintControls = [];
       
 24812 			}
       
 24813 
       
 24814 			return this;
       
 24815 		}
       
 24816 	});
       
 24817 });
       
 24818 
       
 24819 // Included from: js/tinymce/classes/ui/DragHelper.js
       
 24820 
       
 24821 /**
       
 24822  * DragHelper.js
       
 24823  *
       
 24824  * Copyright, Moxiecode Systems AB
       
 24825  * Released under LGPL License.
       
 24826  *
       
 24827  * License: http://www.tinymce.com/license
       
 24828  * Contributing: http://www.tinymce.com/contributing
       
 24829  */
       
 24830 
       
 24831 /**
       
 24832  * Drag/drop helper class.
       
 24833  *
       
 24834  * @example
       
 24835  * var dragHelper = new tinymce.ui.DragHelper('mydiv', {
       
 24836  *     start: function(evt) {
       
 24837  *     },
       
 24838  *
       
 24839  *     drag: function(evt) {
       
 24840  *     },
       
 24841  *
       
 24842  *     end: function(evt) {
       
 24843  *     }
       
 24844  * });
       
 24845  *
       
 24846  * @class tinymce.ui.DragHelper
       
 24847  */
       
 24848 define("tinymce/ui/DragHelper", [
       
 24849 	"tinymce/ui/DomUtils"
       
 24850 ], function(DomUtils) {
       
 24851 	"use strict";
       
 24852 
       
 24853 	function getDocumentSize() {
       
 24854 		var doc = document, documentElement, body, scrollWidth, clientWidth;
       
 24855 		var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max;
       
 24856 
       
 24857 		documentElement = doc.documentElement;
       
 24858 		body = doc.body;
       
 24859 
       
 24860 		scrollWidth = max(documentElement.scrollWidth, body.scrollWidth);
       
 24861 		clientWidth = max(documentElement.clientWidth, body.clientWidth);
       
 24862 		offsetWidth = max(documentElement.offsetWidth, body.offsetWidth);
       
 24863 
       
 24864 		scrollHeight = max(documentElement.scrollHeight, body.scrollHeight);
       
 24865 		clientHeight = max(documentElement.clientHeight, body.clientHeight);
       
 24866 		offsetHeight = max(documentElement.offsetHeight, body.offsetHeight);
       
 24867 
       
 24868 		return {
       
 24869 			width: scrollWidth < offsetWidth ? clientWidth : scrollWidth,
       
 24870 			height: scrollHeight < offsetHeight ? clientHeight : scrollHeight
       
 24871 		};
       
 24872 	}
       
 24873 
       
 24874 	return function(id, settings) {
       
 24875 		var eventOverlayElm, doc = document, downButton, start, stop, drag, startX, startY;
       
 24876 
       
 24877 		settings = settings || {};
       
 24878 
       
 24879 		function getHandleElm() {
       
 24880 			return doc.getElementById(settings.handle || id);
       
 24881 		}
       
 24882 
       
 24883 		start = function(e) {
       
 24884 			var docSize = getDocumentSize(), handleElm, cursor;
       
 24885 
       
 24886 			e.preventDefault();
       
 24887 			downButton = e.button;
       
 24888 			handleElm = getHandleElm();
       
 24889 			startX = e.screenX;
       
 24890 			startY = e.screenY;
       
 24891 
       
 24892 			// Grab cursor from handle
       
 24893 			if (window.getComputedStyle) {
       
 24894 				cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor");
       
 24895 			} else {
       
 24896 				cursor = handleElm.runtimeStyle.cursor;
       
 24897 			}
       
 24898 
       
 24899 			// Create event overlay and add it to document
       
 24900 			eventOverlayElm = doc.createElement('div');
       
 24901 			DomUtils.css(eventOverlayElm, {
       
 24902 				position: "absolute",
       
 24903 				top: 0, left: 0,
       
 24904 				width: docSize.width,
       
 24905 				height: docSize.height,
       
 24906 				zIndex: 0x7FFFFFFF,
       
 24907 				opacity: 0.0001,
       
 24908 				cursor: cursor
       
 24909 			});
       
 24910 
       
 24911 			doc.body.appendChild(eventOverlayElm);
       
 24912 
       
 24913 			// Bind mouse events
       
 24914 			DomUtils.on(doc, 'mousemove', drag);
       
 24915 			DomUtils.on(doc, 'mouseup', stop);
       
 24916 
       
 24917 			// Begin drag
       
 24918 			settings.start(e);
       
 24919 		};
       
 24920 
       
 24921 		drag = function(e) {
       
 24922 			if (e.button !== downButton) {
       
 24923 				return stop(e);
       
 24924 			}
       
 24925 
       
 24926 			e.deltaX = e.screenX - startX;
       
 24927 			e.deltaY = e.screenY - startY;
       
 24928 
       
 24929 			e.preventDefault();
       
 24930 			settings.drag(e);
       
 24931 		};
       
 24932 
       
 24933 		stop = function(e) {
       
 24934 			DomUtils.off(doc, 'mousemove', drag);
       
 24935 			DomUtils.off(doc, 'mouseup', stop);
       
 24936 
       
 24937 			eventOverlayElm.parentNode.removeChild(eventOverlayElm);
       
 24938 
       
 24939 			if (settings.stop) {
       
 24940 				settings.stop(e);
       
 24941 			}
       
 24942 		};
       
 24943 
       
 24944 		/**
       
 24945 		 * Destroys the drag/drop helper instance.
       
 24946 		 *
       
 24947 		 * @method destroy
       
 24948 		 */
       
 24949 		this.destroy = function() {
       
 24950 			DomUtils.off(getHandleElm());
       
 24951 		};
       
 24952 
       
 24953 		DomUtils.on(getHandleElm(), 'mousedown', start);
       
 24954 	};
       
 24955 });
       
 24956 
       
 24957 // Included from: js/tinymce/classes/ui/Scrollable.js
       
 24958 
       
 24959 /**
       
 24960  * Scrollable.js
       
 24961  *
       
 24962  * Copyright, Moxiecode Systems AB
       
 24963  * Released under LGPL License.
       
 24964  *
       
 24965  * License: http://www.tinymce.com/license
       
 24966  * Contributing: http://www.tinymce.com/contributing
       
 24967  */
       
 24968 
       
 24969 /**
       
 24970  * This mixin makes controls scrollable using custom scrollbars.
       
 24971  *
       
 24972  * @-x-less Scrollable.less
       
 24973  * @mixin tinymce.ui.Scrollable
       
 24974  */
       
 24975 define("tinymce/ui/Scrollable", [
       
 24976 	"tinymce/ui/DomUtils",
       
 24977 	"tinymce/ui/DragHelper"
       
 24978 ], function(DomUtils, DragHelper) {
       
 24979 	"use strict";
       
 24980 
       
 24981 	return {
       
 24982 		init: function() {
       
 24983 			var self = this;
       
 24984 			self.on('repaint', self.renderScroll);
       
 24985 		},
       
 24986 
       
 24987 		renderScroll: function() {
       
 24988 			var self = this, margin = 2;
       
 24989 
       
 24990 			function repaintScroll() {
       
 24991 				var hasScrollH, hasScrollV, bodyElm;
       
 24992 
       
 24993 				function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
       
 24994 					var containerElm, scrollBarElm, scrollThumbElm;
       
 24995 					var containerSize, scrollSize, ratio, rect;
       
 24996 					var posNameLower, sizeNameLower;
       
 24997 
       
 24998 					scrollBarElm = self.getEl('scroll' + axisName);
       
 24999 					if (scrollBarElm) {
       
 25000 						posNameLower = posName.toLowerCase();
       
 25001 						sizeNameLower = sizeName.toLowerCase();
       
 25002 
       
 25003 						if (self.getEl('absend')) {
       
 25004 							DomUtils.css(self.getEl('absend'), posNameLower, self.layoutRect()[contentSizeName] - 1);
       
 25005 						}
       
 25006 
       
 25007 						if (!hasScroll) {
       
 25008 							DomUtils.css(scrollBarElm, 'display', 'none');
       
 25009 							return;
       
 25010 						}
       
 25011 
       
 25012 						DomUtils.css(scrollBarElm, 'display', 'block');
       
 25013 						containerElm = self.getEl('body');
       
 25014 						scrollThumbElm = self.getEl('scroll' + axisName + "t");
       
 25015 						containerSize = containerElm["client" + sizeName] - (margin * 2);
       
 25016 						containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
       
 25017 						scrollSize = containerElm["scroll" + sizeName];
       
 25018 						ratio = containerSize / scrollSize;
       
 25019 
       
 25020 						rect = {};
       
 25021 						rect[posNameLower] = containerElm["offset" + posName] + margin;
       
 25022 						rect[sizeNameLower] = containerSize;
       
 25023 						DomUtils.css(scrollBarElm, rect);
       
 25024 
       
 25025 						rect = {};
       
 25026 						rect[posNameLower] = containerElm["scroll" + posName] * ratio;
       
 25027 						rect[sizeNameLower] = containerSize * ratio;
       
 25028 						DomUtils.css(scrollThumbElm, rect);
       
 25029 					}
       
 25030 				}
       
 25031 
       
 25032 				bodyElm = self.getEl('body');
       
 25033 				hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
       
 25034 				hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
       
 25035 
       
 25036 				repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
       
 25037 				repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
       
 25038 			}
       
 25039 
       
 25040 			function addScroll() {
       
 25041 				function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
       
 25042 					var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
       
 25043 
       
 25044 					self.getEl().appendChild(DomUtils.createFragment(
       
 25045 						'<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
       
 25046 							'<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
       
 25047 						'</div>'
       
 25048 					));
       
 25049 
       
 25050 					self.draghelper = new DragHelper(axisId + 't', {
       
 25051 						start: function() {
       
 25052 							scrollStart = self.getEl('body')["scroll" + posName];
       
 25053 							DomUtils.addClass(DomUtils.get(axisId), prefix + 'active');
       
 25054 						},
       
 25055 
       
 25056 						drag: function(e) {
       
 25057 							var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
       
 25058 
       
 25059 							hasScrollH = layoutRect.contentW > layoutRect.innerW;
       
 25060 							hasScrollV = layoutRect.contentH > layoutRect.innerH;
       
 25061 							containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
       
 25062 							containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
       
 25063 
       
 25064 							ratio = containerSize / self.getEl('body')["scroll" + sizeName];
       
 25065 							self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
       
 25066 						},
       
 25067 
       
 25068 						stop: function() {
       
 25069 							DomUtils.removeClass(DomUtils.get(axisId), prefix + 'active');
       
 25070 						}
       
 25071 					});
       
 25072 /*
       
 25073 					self.on('click', function(e) {
       
 25074 						if (e.target.id == self._id + '-scrollv') {
       
 25075 
       
 25076 						}
       
 25077 					});*/
       
 25078 				}
       
 25079 
       
 25080 				self.addClass('scroll');
       
 25081 
       
 25082 				addScrollAxis("v", "Top", "Height", "Y", "Width");
       
 25083 				addScrollAxis("h", "Left", "Width", "X", "Height");
       
 25084 			}
       
 25085 
       
 25086 			if (self.settings.autoScroll) {
       
 25087 				if (!self._hasScroll) {
       
 25088 					self._hasScroll = true;
       
 25089 					addScroll();
       
 25090 
       
 25091 					self.on('wheel', function(e) {
       
 25092 						var bodyEl = self.getEl('body');
       
 25093 
       
 25094 						bodyEl.scrollLeft += (e.deltaX || 0) * 10;
       
 25095 						bodyEl.scrollTop += e.deltaY * 10;
       
 25096 
       
 25097 						repaintScroll();
       
 25098 					});
       
 25099 
       
 25100 					DomUtils.on(self.getEl('body'), "scroll", repaintScroll);
       
 25101 				}
       
 25102 
       
 25103 				repaintScroll();
       
 25104 			}
       
 25105 		}
       
 25106 	};
       
 25107 });
       
 25108 
       
 25109 // Included from: js/tinymce/classes/ui/Panel.js
       
 25110 
       
 25111 /**
       
 25112  * Panel.js
       
 25113  *
       
 25114  * Copyright, Moxiecode Systems AB
       
 25115  * Released under LGPL License.
       
 25116  *
       
 25117  * License: http://www.tinymce.com/license
       
 25118  * Contributing: http://www.tinymce.com/contributing
       
 25119  */
       
 25120 
       
 25121 /**
       
 25122  * Creates a new panel.
       
 25123  *
       
 25124  * @-x-less Panel.less
       
 25125  * @class tinymce.ui.Panel
       
 25126  * @extends tinymce.ui.Container
       
 25127  * @mixes tinymce.ui.Scrollable
       
 25128  */
       
 25129 define("tinymce/ui/Panel", [
       
 25130 	"tinymce/ui/Container",
       
 25131 	"tinymce/ui/Scrollable"
       
 25132 ], function(Container, Scrollable) {
       
 25133 	"use strict";
       
 25134 
       
 25135 	return Container.extend({
       
 25136 		Defaults: {
       
 25137 			layout: 'fit',
       
 25138 			containerCls: 'panel'
       
 25139 		},
       
 25140 
       
 25141 		Mixins: [Scrollable],
       
 25142 
       
 25143 		/**
       
 25144 		 * Renders the control as a HTML string.
       
 25145 		 *
       
 25146 		 * @method renderHtml
       
 25147 		 * @return {String} HTML representing the control.
       
 25148 		 */
       
 25149 		renderHtml: function() {
       
 25150 			var self = this, layout = self._layout, innerHtml = self.settings.html;
       
 25151 
       
 25152 			self.preRender();
       
 25153 			layout.preRender(self);
       
 25154 
       
 25155 			if (typeof innerHtml == "undefined") {
       
 25156 				innerHtml = (
       
 25157 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 25158 						layout.renderHtml(self) +
       
 25159 					'</div>'
       
 25160 				);
       
 25161 			} else {
       
 25162 				if (typeof innerHtml == 'function') {
       
 25163 					innerHtml = innerHtml.call(self);
       
 25164 				}
       
 25165 
       
 25166 				self._hasBody = false;
       
 25167 			}
       
 25168 
       
 25169 			return (
       
 25170 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1" role="group">' +
       
 25171 					(self._preBodyHtml || '') +
       
 25172 					innerHtml +
       
 25173 				'</div>'
       
 25174 			);
       
 25175 		}
       
 25176 	});
       
 25177 });
       
 25178 
       
 25179 // Included from: js/tinymce/classes/ui/Movable.js
       
 25180 
       
 25181 /**
       
 25182  * Movable.js
       
 25183  *
       
 25184  * Copyright, Moxiecode Systems AB
       
 25185  * Released under LGPL License.
       
 25186  *
       
 25187  * License: http://www.tinymce.com/license
       
 25188  * Contributing: http://www.tinymce.com/contributing
       
 25189  */
       
 25190 
       
 25191 /**
       
 25192  * Movable mixin. Makes controls movable absolute and relative to other elements.
       
 25193  *
       
 25194  * @mixin tinymce.ui.Movable
       
 25195  */
       
 25196 define("tinymce/ui/Movable", [
       
 25197 	"tinymce/ui/DomUtils"
       
 25198 ], function(DomUtils) {
       
 25199 	"use strict";
       
 25200 
       
 25201 	function calculateRelativePosition(ctrl, targetElm, rel) {
       
 25202 		var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
       
 25203 
       
 25204 		viewport = DomUtils.getViewPort();
       
 25205 
       
 25206 		// Get pos of target
       
 25207 		pos = DomUtils.getPos(targetElm);
       
 25208 		x = pos.x;
       
 25209 		y = pos.y;
       
 25210 
       
 25211 		if (ctrl._fixed && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') {
       
 25212 			x -= viewport.x;
       
 25213 			y -= viewport.y;
       
 25214 		}
       
 25215 
       
 25216 		// Get size of self
       
 25217 		ctrlElm = ctrl.getEl();
       
 25218 		size = DomUtils.getSize(ctrlElm);
       
 25219 		selfW = size.width;
       
 25220 		selfH = size.height;
       
 25221 
       
 25222 		// Get size of target
       
 25223 		size = DomUtils.getSize(targetElm);
       
 25224 		targetW = size.width;
       
 25225 		targetH = size.height;
       
 25226 
       
 25227 		// Parse align string
       
 25228 		rel = (rel || '').split('');
       
 25229 
       
 25230 		// Target corners
       
 25231 		if (rel[0] === 'b') {
       
 25232 			y += targetH;
       
 25233 		}
       
 25234 
       
 25235 		if (rel[1] === 'r') {
       
 25236 			x += targetW;
       
 25237 		}
       
 25238 
       
 25239 		if (rel[0] === 'c') {
       
 25240 			y += Math.round(targetH / 2);
       
 25241 		}
       
 25242 
       
 25243 		if (rel[1] === 'c') {
       
 25244 			x += Math.round(targetW / 2);
       
 25245 		}
       
 25246 
       
 25247 		// Self corners
       
 25248 		if (rel[3] === 'b') {
       
 25249 			y -= selfH;
       
 25250 		}
       
 25251 
       
 25252 		if (rel[4] === 'r') {
       
 25253 			x -= selfW;
       
 25254 		}
       
 25255 
       
 25256 		if (rel[3] === 'c') {
       
 25257 			y -= Math.round(selfH / 2);
       
 25258 		}
       
 25259 
       
 25260 		if (rel[4] === 'c') {
       
 25261 			x -= Math.round(selfW / 2);
       
 25262 		}
       
 25263 
       
 25264 		return {
       
 25265 			x: x,
       
 25266 			y: y,
       
 25267 			w: selfW,
       
 25268 			h: selfH
       
 25269 		};
       
 25270 	}
       
 25271 
       
 25272 	return {
       
 25273 		/**
       
 25274 		 * Tests various positions to get the most suitable one.
       
 25275 		 *
       
 25276 		 * @method testMoveRel
       
 25277 		 * @param {DOMElement} elm Element to position against.
       
 25278 		 * @param {Array} rels Array with relative positions.
       
 25279 		 * @return {String} Best suitable relative position.
       
 25280 		 */
       
 25281 		testMoveRel: function(elm, rels) {
       
 25282 			var viewPortRect = DomUtils.getViewPort();
       
 25283 
       
 25284 			for (var i = 0; i < rels.length; i++) {
       
 25285 				var pos = calculateRelativePosition(this, elm, rels[i]);
       
 25286 
       
 25287 				if (this._fixed) {
       
 25288 					if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
       
 25289 						return rels[i];
       
 25290 					}
       
 25291 				} else {
       
 25292 					if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
       
 25293 						pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
       
 25294 						return rels[i];
       
 25295 					}
       
 25296 				}
       
 25297 			}
       
 25298 
       
 25299 			return rels[0];
       
 25300 		},
       
 25301 
       
 25302 		/**
       
 25303 		 * Move relative to the specified element.
       
 25304 		 *
       
 25305 		 * @method moveRel
       
 25306 		 * @param {Element} elm Element to move relative to.
       
 25307 		 * @param {String} rel Relative mode. For example: br-tl.
       
 25308 		 * @return {tinymce.ui.Control} Current control instance.
       
 25309 		 */
       
 25310 		moveRel: function(elm, rel) {
       
 25311 			if (typeof rel != 'string') {
       
 25312 				rel = this.testMoveRel(elm, rel);
       
 25313 			}
       
 25314 
       
 25315 			var pos = calculateRelativePosition(this, elm, rel);
       
 25316 			return this.moveTo(pos.x, pos.y);
       
 25317 		},
       
 25318 
       
 25319 		/**
       
 25320 		 * Move by a relative x, y values.
       
 25321 		 *
       
 25322 		 * @method moveBy
       
 25323 		 * @param {Number} dx Relative x position.
       
 25324 		 * @param {Number} dy Relative y position.
       
 25325 		 * @return {tinymce.ui.Control} Current control instance.
       
 25326 		 */
       
 25327 		moveBy: function(dx, dy) {
       
 25328 			var self = this, rect = self.layoutRect();
       
 25329 
       
 25330 			self.moveTo(rect.x + dx, rect.y + dy);
       
 25331 
       
 25332 			return self;
       
 25333 		},
       
 25334 
       
 25335 		/**
       
 25336 		 * Move to absolute position.
       
 25337 		 *
       
 25338 		 * @method moveTo
       
 25339 		 * @param {Number} x Absolute x position.
       
 25340 		 * @param {Number} y Absolute y position.
       
 25341 		 * @return {tinymce.ui.Control} Current control instance.
       
 25342 		 */
       
 25343 		moveTo: function(x, y) {
       
 25344 			var self = this;
       
 25345 
       
 25346 			// TODO: Move this to some global class
       
 25347 			function contrain(value, max, size) {
       
 25348 				if (value < 0) {
       
 25349 					return 0;
       
 25350 				}
       
 25351 
       
 25352 				if (value + size > max) {
       
 25353 					value = max - size;
       
 25354 					return value < 0 ? 0 : value;
       
 25355 				}
       
 25356 
       
 25357 				return value;
       
 25358 			}
       
 25359 
       
 25360 			if (self.settings.constrainToViewport) {
       
 25361 				var viewPortRect = DomUtils.getViewPort(window);
       
 25362 				var layoutRect = self.layoutRect();
       
 25363 
       
 25364 				x = contrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
       
 25365 				y = contrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
       
 25366 			}
       
 25367 
       
 25368 			if (self._rendered) {
       
 25369 				self.layoutRect({x: x, y: y}).repaint();
       
 25370 			} else {
       
 25371 				self.settings.x = x;
       
 25372 				self.settings.y = y;
       
 25373 			}
       
 25374 
       
 25375 			self.fire('move', {x: x, y: y});
       
 25376 
       
 25377 			return self;
       
 25378 		}
       
 25379 	};
       
 25380 });
       
 25381 
       
 25382 // Included from: js/tinymce/classes/ui/Resizable.js
       
 25383 
       
 25384 /**
       
 25385  * Resizable.js
       
 25386  *
       
 25387  * Copyright, Moxiecode Systems AB
       
 25388  * Released under LGPL License.
       
 25389  *
       
 25390  * License: http://www.tinymce.com/license
       
 25391  * Contributing: http://www.tinymce.com/contributing
       
 25392  */
       
 25393 
       
 25394 /**
       
 25395  * Resizable mixin. Enables controls to be resized.
       
 25396  *
       
 25397  * @mixin tinymce.ui.Resizable
       
 25398  */
       
 25399 define("tinymce/ui/Resizable", [
       
 25400 	"tinymce/ui/DomUtils"
       
 25401 ], function(DomUtils) {
       
 25402 	"use strict";
       
 25403 
       
 25404 	return {
       
 25405 		/**
       
 25406 		 * Resizes the control to contents.
       
 25407 		 *
       
 25408 		 * @method resizeToContent
       
 25409 		 */
       
 25410 		resizeToContent: function() {
       
 25411 			this._layoutRect.autoResize = true;
       
 25412 			this._lastRect = null;
       
 25413 			this.reflow();
       
 25414 		},
       
 25415 
       
 25416 		/**
       
 25417 		 * Resizes the control to a specific width/height.
       
 25418 		 *
       
 25419 		 * @method resizeTo
       
 25420 		 * @param {Number} w Control width.
       
 25421 		 * @param {Number} h Control height.
       
 25422 		 * @return {tinymce.ui.Control} Current control instance.
       
 25423 		 */
       
 25424 		resizeTo: function(w, h) {
       
 25425 			// TODO: Fix hack
       
 25426 			if (w <= 1 || h <= 1) {
       
 25427 				var rect = DomUtils.getWindowSize();
       
 25428 
       
 25429 				w = w <= 1 ? w * rect.w : w;
       
 25430 				h = h <= 1 ? h * rect.h : h;
       
 25431 			}
       
 25432 
       
 25433 			this._layoutRect.autoResize = false;
       
 25434 			return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow();
       
 25435 		},
       
 25436 
       
 25437 		/**
       
 25438 		 * Resizes the control to a specific relative width/height.
       
 25439 		 *
       
 25440 		 * @method resizeBy
       
 25441 		 * @param {Number} dw Relative control width.
       
 25442 		 * @param {Number} dh Relative control height.
       
 25443 		 * @return {tinymce.ui.Control} Current control instance.
       
 25444 		 */
       
 25445 		resizeBy: function(dw, dh) {
       
 25446 			var self = this, rect = self.layoutRect();
       
 25447 
       
 25448 			return self.resizeTo(rect.w + dw, rect.h + dh);
       
 25449 		}
       
 25450 	};
       
 25451 });
       
 25452 
       
 25453 // Included from: js/tinymce/classes/ui/FloatPanel.js
       
 25454 
       
 25455 /**
       
 25456  * FloatPanel.js
       
 25457  *
       
 25458  * Copyright, Moxiecode Systems AB
       
 25459  * Released under LGPL License.
       
 25460  *
       
 25461  * License: http://www.tinymce.com/license
       
 25462  * Contributing: http://www.tinymce.com/contributing
       
 25463  */
       
 25464 
       
 25465 /**
       
 25466  * This class creates a floating panel.
       
 25467  *
       
 25468  * @-x-less FloatPanel.less
       
 25469  * @class tinymce.ui.FloatPanel
       
 25470  * @extends tinymce.ui.Panel
       
 25471  * @mixes tinymce.ui.Movable
       
 25472  * @mixes tinymce.ui.Resizable
       
 25473  */
       
 25474 define("tinymce/ui/FloatPanel", [
       
 25475 	"tinymce/ui/Panel",
       
 25476 	"tinymce/ui/Movable",
       
 25477 	"tinymce/ui/Resizable",
       
 25478 	"tinymce/ui/DomUtils"
       
 25479 ], function(Panel, Movable, Resizable, DomUtils) {
       
 25480 	"use strict";
       
 25481 
       
 25482 	var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = [];
       
 25483 	var zOrder = [], hasModal;
       
 25484 
       
 25485 	function bindDocumentClickHandler() {
       
 25486 		function isChildOf(ctrl, parent) {
       
 25487 			while (ctrl) {
       
 25488 				if (ctrl == parent) {
       
 25489 					return true;
       
 25490 				}
       
 25491 
       
 25492 				ctrl = ctrl.parent();
       
 25493 			}
       
 25494 		}
       
 25495 
       
 25496 		if (!documentClickHandler) {
       
 25497 			documentClickHandler = function(e) {
       
 25498 				// Gecko fires click event and in the wrong order on Mac so lets normalize
       
 25499 				if (e.button == 2) {
       
 25500 					return;
       
 25501 				}
       
 25502 
       
 25503 				// Hide any float panel when a click is out side that float panel and the
       
 25504 				// float panels direct parent for example a click on a menu button
       
 25505 				var i = visiblePanels.length;
       
 25506 				while (i--) {
       
 25507 					var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
       
 25508 
       
 25509 					if (panel.settings.autohide) {
       
 25510 						if (clickCtrl) {
       
 25511 							if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
       
 25512 								continue;
       
 25513 							}
       
 25514 						}
       
 25515 
       
 25516 						e = panel.fire('autohide', {target: e.target});
       
 25517 						if (!e.isDefaultPrevented()) {
       
 25518 							panel.hide();
       
 25519 						}
       
 25520 					}
       
 25521 				}
       
 25522 			};
       
 25523 
       
 25524 			DomUtils.on(document, 'click', documentClickHandler);
       
 25525 		}
       
 25526 	}
       
 25527 
       
 25528 	function bindDocumentScrollHandler() {
       
 25529 		if (!documentScrollHandler) {
       
 25530 			documentScrollHandler = function() {
       
 25531 				var i;
       
 25532 
       
 25533 				i = visiblePanels.length;
       
 25534 				while (i--) {
       
 25535 					repositionPanel(visiblePanels[i]);
       
 25536 				}
       
 25537 			};
       
 25538 
       
 25539 			DomUtils.on(window, 'scroll', documentScrollHandler);
       
 25540 		}
       
 25541 	}
       
 25542 
       
 25543 	function bindWindowResizeHandler() {
       
 25544 		if (!windowResizeHandler) {
       
 25545 			var docElm = document.documentElement, clientWidth = docElm.clientWidth, clientHeight = docElm.clientHeight;
       
 25546 
       
 25547 			windowResizeHandler = function() {
       
 25548 				// Workaround for #7065 IE 7 fires resize events event though the window wasn't resized
       
 25549 				if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) {
       
 25550 					clientWidth = docElm.clientWidth;
       
 25551 					clientHeight = docElm.clientHeight;
       
 25552 					FloatPanel.hideAll();
       
 25553 				}
       
 25554 			};
       
 25555 
       
 25556 			DomUtils.on(window, 'resize', windowResizeHandler);
       
 25557 		}
       
 25558 	}
       
 25559 
       
 25560 	/**
       
 25561 	 * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
       
 25562 	 * also reposition all child panels of the current panel.
       
 25563 	 */
       
 25564 	function repositionPanel(panel) {
       
 25565 		var scrollY = DomUtils.getViewPort().y;
       
 25566 
       
 25567 		function toggleFixedChildPanels(fixed, deltaY) {
       
 25568 			var parent;
       
 25569 
       
 25570 			for (var i = 0; i < visiblePanels.length; i++) {
       
 25571 				if (visiblePanels[i] != panel) {
       
 25572 					parent = visiblePanels[i].parent();
       
 25573 
       
 25574 					while (parent && (parent = parent.parent())) {
       
 25575 						if (parent == panel) {
       
 25576 							visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
       
 25577 						}
       
 25578 					}
       
 25579 				}
       
 25580 			}
       
 25581 		}
       
 25582 
       
 25583 		if (panel.settings.autofix) {
       
 25584 			if (!panel._fixed) {
       
 25585 				panel._autoFixY = panel.layoutRect().y;
       
 25586 
       
 25587 				if (panel._autoFixY < scrollY) {
       
 25588 					panel.fixed(true).layoutRect({y: 0}).repaint();
       
 25589 					toggleFixedChildPanels(true, scrollY - panel._autoFixY);
       
 25590 				}
       
 25591 			} else {
       
 25592 				if (panel._autoFixY > scrollY) {
       
 25593 					panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
       
 25594 					toggleFixedChildPanels(false, panel._autoFixY - scrollY);
       
 25595 				}
       
 25596 			}
       
 25597 		}
       
 25598 	}
       
 25599 
       
 25600 	function addRemove(add, ctrl) {
       
 25601 		var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
       
 25602 
       
 25603 		if (add) {
       
 25604 			zOrder.push(ctrl);
       
 25605 		} else {
       
 25606 			i = zOrder.length;
       
 25607 
       
 25608 			while (i--) {
       
 25609 				if (zOrder[i] === ctrl) {
       
 25610 					zOrder.splice(i, 1);
       
 25611 				}
       
 25612 			}
       
 25613 		}
       
 25614 
       
 25615 		if (zOrder.length) {
       
 25616 			for (i = 0; i < zOrder.length; i++) {
       
 25617 				if (zOrder[i].modal) {
       
 25618 					zIndex++;
       
 25619 					topModal = zOrder[i];
       
 25620 				}
       
 25621 
       
 25622 				zOrder[i].getEl().style.zIndex = zIndex;
       
 25623 				zOrder[i].zIndex = zIndex;
       
 25624 				zIndex++;
       
 25625 			}
       
 25626 		}
       
 25627 
       
 25628 		var modalBlockEl = document.getElementById(ctrl.classPrefix + 'modal-block');
       
 25629 
       
 25630 		if (topModal) {
       
 25631 			DomUtils.css(modalBlockEl, 'z-index', topModal.zIndex - 1);
       
 25632 		} else if (modalBlockEl) {
       
 25633 			modalBlockEl.parentNode.removeChild(modalBlockEl);
       
 25634 			hasModal = false;
       
 25635 		}
       
 25636 
       
 25637 		FloatPanel.currentZIndex = zIndex;
       
 25638 	}
       
 25639 
       
 25640 	var FloatPanel = Panel.extend({
       
 25641 		Mixins: [Movable, Resizable],
       
 25642 
       
 25643 		/**
       
 25644 		 * Constructs a new control instance with the specified settings.
       
 25645 		 *
       
 25646 		 * @constructor
       
 25647 		 * @param {Object} settings Name/value object with settings.
       
 25648 		 * @setting {Boolean} autohide Automatically hide the panel.
       
 25649 		 */
       
 25650 		init: function(settings) {
       
 25651 			var self = this;
       
 25652 
       
 25653 			self._super(settings);
       
 25654 			self._eventsRoot = self;
       
 25655 
       
 25656 			self.addClass('floatpanel');
       
 25657 
       
 25658 			// Hide floatpanes on click out side the root button
       
 25659 			if (settings.autohide) {
       
 25660 				bindDocumentClickHandler();
       
 25661 				bindWindowResizeHandler();
       
 25662 				visiblePanels.push(self);
       
 25663 			}
       
 25664 
       
 25665 			if (settings.autofix) {
       
 25666 				bindDocumentScrollHandler();
       
 25667 
       
 25668 				self.on('move', function() {
       
 25669 					repositionPanel(this);
       
 25670 				});
       
 25671 			}
       
 25672 
       
 25673 			self.on('postrender show', function(e) {
       
 25674 				if (e.control == self) {
       
 25675 					var modalBlockEl, prefix = self.classPrefix;
       
 25676 
       
 25677 					if (self.modal && !hasModal) {
       
 25678 						modalBlockEl = DomUtils.createFragment('<div id="' + prefix + 'modal-block" class="' +
       
 25679 							prefix + 'reset ' + prefix + 'fade"></div>');
       
 25680 						modalBlockEl = modalBlockEl.firstChild;
       
 25681 
       
 25682 						self.getContainerElm().appendChild(modalBlockEl);
       
 25683 
       
 25684 						setTimeout(function() {
       
 25685 							DomUtils.addClass(modalBlockEl, prefix + 'in');
       
 25686 							DomUtils.addClass(self.getEl(), prefix + 'in');
       
 25687 						}, 0);
       
 25688 
       
 25689 						hasModal = true;
       
 25690 					}
       
 25691 
       
 25692 					addRemove(true, self);
       
 25693 				}
       
 25694 			});
       
 25695 
       
 25696 			self.on('show', function() {
       
 25697 				self.parents().each(function(ctrl) {
       
 25698 					if (ctrl._fixed) {
       
 25699 						self.fixed(true);
       
 25700 						return false;
       
 25701 					}
       
 25702 				});
       
 25703 			});
       
 25704 
       
 25705 			if (settings.popover) {
       
 25706 				self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
       
 25707 				self.addClass('popover').addClass('bottom').addClass(self.isRtl() ? 'end' : 'start');
       
 25708 			}
       
 25709 		},
       
 25710 
       
 25711 		fixed: function(state) {
       
 25712 			var self = this;
       
 25713 
       
 25714 			if (self._fixed != state) {
       
 25715 				if (self._rendered) {
       
 25716 					var viewport = DomUtils.getViewPort();
       
 25717 
       
 25718 					if (state) {
       
 25719 						self.layoutRect().y -= viewport.y;
       
 25720 					} else {
       
 25721 						self.layoutRect().y += viewport.y;
       
 25722 					}
       
 25723 				}
       
 25724 
       
 25725 				self.toggleClass('fixed', state);
       
 25726 				self._fixed = state;
       
 25727 			}
       
 25728 
       
 25729 			return self;
       
 25730 		},
       
 25731 
       
 25732 		/**
       
 25733 		 * Shows the current float panel.
       
 25734 		 *
       
 25735 		 * @method show
       
 25736 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
       
 25737 		 */
       
 25738 		show: function() {
       
 25739 			var self = this, i, state = self._super();
       
 25740 
       
 25741 			i = visiblePanels.length;
       
 25742 			while (i--) {
       
 25743 				if (visiblePanels[i] === self) {
       
 25744 					break;
       
 25745 				}
       
 25746 			}
       
 25747 
       
 25748 			if (i === -1) {
       
 25749 				visiblePanels.push(self);
       
 25750 			}
       
 25751 
       
 25752 			return state;
       
 25753 		},
       
 25754 
       
 25755 		/**
       
 25756 		 * Hides the current float panel.
       
 25757 		 *
       
 25758 		 * @method hide
       
 25759 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
       
 25760 		 */
       
 25761 		hide: function() {
       
 25762 			removeVisiblePanel(this);
       
 25763 			addRemove(false, this);
       
 25764 
       
 25765 			return this._super();
       
 25766 		},
       
 25767 
       
 25768 		/**
       
 25769 		 * Hide all visible float panels with he autohide setting enabled. This is for
       
 25770 		 * manually hiding floating menus or panels.
       
 25771 		 *
       
 25772 		 * @method hideAll
       
 25773 		 */
       
 25774 		hideAll: function() {
       
 25775 			FloatPanel.hideAll();
       
 25776 		},
       
 25777 
       
 25778 		/**
       
 25779 		 * Closes the float panel. This will remove the float panel from page and fire the close event.
       
 25780 		 *
       
 25781 		 * @method close
       
 25782 		 */
       
 25783 		close: function() {
       
 25784 			var self = this;
       
 25785 
       
 25786 			if (!self.fire('close').isDefaultPrevented()) {
       
 25787 				self.remove();
       
 25788 				addRemove(false, self);
       
 25789 			}
       
 25790 
       
 25791 			return self;
       
 25792 		},
       
 25793 
       
 25794 		/**
       
 25795 		 * Removes the float panel from page.
       
 25796 		 *
       
 25797 		 * @method remove
       
 25798 		 */
       
 25799 		remove: function() {
       
 25800 			removeVisiblePanel(this);
       
 25801 			this._super();
       
 25802 		},
       
 25803 
       
 25804 		postRender: function() {
       
 25805 			var self = this;
       
 25806 
       
 25807 			if (self.settings.bodyRole) {
       
 25808 				this.getEl('body').setAttribute('role', self.settings.bodyRole);
       
 25809 			}
       
 25810 
       
 25811 			return self._super();
       
 25812 		}
       
 25813 	});
       
 25814 
       
 25815 	/**
       
 25816 	 * Hide all visible float panels with he autohide setting enabled. This is for
       
 25817 	 * manually hiding floating menus or panels.
       
 25818 	 *
       
 25819 	 * @static
       
 25820 	 * @method hideAll
       
 25821 	 */
       
 25822 	FloatPanel.hideAll = function() {
       
 25823 		var i = visiblePanels.length;
       
 25824 
       
 25825 		while (i--) {
       
 25826 			var panel = visiblePanels[i];
       
 25827 
       
 25828 			if (panel && panel.settings.autohide) {
       
 25829 				panel.hide();
       
 25830 				visiblePanels.splice(i, 1);
       
 25831 			}
       
 25832 		}
       
 25833 	};
       
 25834 
       
 25835 	function removeVisiblePanel(panel) {
       
 25836 		var i;
       
 25837 
       
 25838 		i = visiblePanels.length;
       
 25839 		while (i--) {
       
 25840 			if (visiblePanels[i] === panel) {
       
 25841 				visiblePanels.splice(i, 1);
       
 25842 			}
       
 25843 		}
       
 25844 
       
 25845 		i = zOrder.length;
       
 25846 		while (i--) {
       
 25847 			if (zOrder[i] === panel) {
       
 25848 				zOrder.splice(i, 1);
       
 25849 			}
       
 25850 		}
       
 25851 	}
       
 25852 
       
 25853 	return FloatPanel;
       
 25854 });
       
 25855 
       
 25856 // Included from: js/tinymce/classes/ui/Window.js
       
 25857 
       
 25858 /**
       
 25859  * Window.js
       
 25860  *
       
 25861  * Copyright, Moxiecode Systems AB
       
 25862  * Released under LGPL License.
       
 25863  *
       
 25864  * License: http://www.tinymce.com/license
       
 25865  * Contributing: http://www.tinymce.com/contributing
       
 25866  */
       
 25867 
       
 25868 /**
       
 25869  * Creates a new window.
       
 25870  *
       
 25871  * @-x-less Window.less
       
 25872  * @class tinymce.ui.Window
       
 25873  * @extends tinymce.ui.FloatPanel
       
 25874  */
       
 25875 define("tinymce/ui/Window", [
       
 25876 	"tinymce/ui/FloatPanel",
       
 25877 	"tinymce/ui/Panel",
       
 25878 	"tinymce/ui/DomUtils",
       
 25879 	"tinymce/ui/DragHelper"
       
 25880 ], function(FloatPanel, Panel, DomUtils, DragHelper) {
       
 25881 	"use strict";
       
 25882 
       
 25883 	var Window = FloatPanel.extend({
       
 25884 		modal: true,
       
 25885 
       
 25886 		Defaults: {
       
 25887 			border: 1,
       
 25888 			layout: 'flex',
       
 25889 			containerCls: 'panel',
       
 25890 			role: 'dialog',
       
 25891 			callbacks: {
       
 25892 				submit: function() {
       
 25893 					this.fire('submit', {data: this.toJSON()});
       
 25894 				},
       
 25895 
       
 25896 				close: function() {
       
 25897 					this.close();
       
 25898 				}
       
 25899 			}
       
 25900 		},
       
 25901 
       
 25902 		/**
       
 25903 		 * Constructs a instance with the specified settings.
       
 25904 		 *
       
 25905 		 * @constructor
       
 25906 		 * @param {Object} settings Name/value object with settings.
       
 25907 		 */
       
 25908 		init: function(settings) {
       
 25909 			var self = this;
       
 25910 
       
 25911 			self._super(settings);
       
 25912 
       
 25913 			if (self.isRtl()) {
       
 25914 				self.addClass('rtl');
       
 25915 			}
       
 25916 
       
 25917 			self.addClass('window');
       
 25918 			self._fixed = true;
       
 25919 
       
 25920 			// Create statusbar
       
 25921 			if (settings.buttons) {
       
 25922 				self.statusbar = new Panel({
       
 25923 					layout: 'flex',
       
 25924 					border: '1 0 0 0',
       
 25925 					spacing: 3,
       
 25926 					padding: 10,
       
 25927 					align: 'center',
       
 25928 					pack: self.isRtl() ? 'start' : 'end',
       
 25929 					defaults: {
       
 25930 						type: 'button'
       
 25931 					},
       
 25932 					items: settings.buttons
       
 25933 				});
       
 25934 
       
 25935 				self.statusbar.addClass('foot');
       
 25936 				self.statusbar.parent(self);
       
 25937 			}
       
 25938 
       
 25939 			self.on('click', function(e) {
       
 25940 				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
       
 25941 					self.close();
       
 25942 				}
       
 25943 			});
       
 25944 
       
 25945 			self.on('cancel', function() {
       
 25946 				self.close();
       
 25947 			});
       
 25948 
       
 25949 			self.aria('describedby', self.describedBy || self._id + '-none');
       
 25950 			self.aria('label', settings.title);
       
 25951 			self._fullscreen = false;
       
 25952 		},
       
 25953 
       
 25954 		/**
       
 25955 		 * Recalculates the positions of the controls in the current container.
       
 25956 		 * This is invoked by the reflow method and shouldn't be called directly.
       
 25957 		 *
       
 25958 		 * @method recalc
       
 25959 		 */
       
 25960 		recalc: function() {
       
 25961 			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
       
 25962 
       
 25963 			if (self._fullscreen) {
       
 25964 				self.layoutRect(DomUtils.getWindowSize());
       
 25965 				self.layoutRect().contentH = self.layoutRect().innerH;
       
 25966 			}
       
 25967 
       
 25968 			self._super();
       
 25969 
       
 25970 			layoutRect = self.layoutRect();
       
 25971 
       
 25972 			// Resize window based on title width
       
 25973 			if (self.settings.title && !self._fullscreen) {
       
 25974 				width = layoutRect.headerW;
       
 25975 				if (width > layoutRect.w) {
       
 25976 					x = layoutRect.x - Math.max(0, width / 2);
       
 25977 					self.layoutRect({w: width, x: x});
       
 25978 					needsRecalc = true;
       
 25979 				}
       
 25980 			}
       
 25981 
       
 25982 			// Resize window based on statusbar width
       
 25983 			if (statusbar) {
       
 25984 				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
       
 25985 
       
 25986 				width = statusbar.layoutRect().minW + layoutRect.deltaW;
       
 25987 				if (width > layoutRect.w) {
       
 25988 					x = layoutRect.x - Math.max(0, width - layoutRect.w);
       
 25989 					self.layoutRect({w: width, x: x});
       
 25990 					needsRecalc = true;
       
 25991 				}
       
 25992 			}
       
 25993 
       
 25994 			// Recalc body and disable auto resize
       
 25995 			if (needsRecalc) {
       
 25996 				self.recalc();
       
 25997 			}
       
 25998 		},
       
 25999 
       
 26000 		/**
       
 26001 		 * Initializes the current controls layout rect.
       
 26002 		 * This will be executed by the layout managers to determine the
       
 26003 		 * default minWidth/minHeight etc.
       
 26004 		 *
       
 26005 		 * @method initLayoutRect
       
 26006 		 * @return {Object} Layout rect instance.
       
 26007 		 */
       
 26008 		initLayoutRect: function() {
       
 26009 			var self = this, layoutRect = self._super(), deltaH = 0, headEl;
       
 26010 
       
 26011 			// Reserve vertical space for title
       
 26012 			if (self.settings.title && !self._fullscreen) {
       
 26013 				headEl = self.getEl('head');
       
 26014 
       
 26015 				var size = DomUtils.getSize(headEl);
       
 26016 
       
 26017 				layoutRect.headerW = size.width;
       
 26018 				layoutRect.headerH = size.height;
       
 26019 
       
 26020 				deltaH += layoutRect.headerH;
       
 26021 			}
       
 26022 
       
 26023 			// Reserve vertical space for statusbar
       
 26024 			if (self.statusbar) {
       
 26025 				deltaH += self.statusbar.layoutRect().h;
       
 26026 			}
       
 26027 
       
 26028 			layoutRect.deltaH += deltaH;
       
 26029 			layoutRect.minH += deltaH;
       
 26030 			//layoutRect.innerH -= deltaH;
       
 26031 			layoutRect.h += deltaH;
       
 26032 
       
 26033 			var rect = DomUtils.getWindowSize();
       
 26034 
       
 26035 			layoutRect.x = Math.max(0, rect.w / 2 - layoutRect.w / 2);
       
 26036 			layoutRect.y = Math.max(0, rect.h / 2 - layoutRect.h / 2);
       
 26037 
       
 26038 			return layoutRect;
       
 26039 		},
       
 26040 
       
 26041 		/**
       
 26042 		 * Renders the control as a HTML string.
       
 26043 		 *
       
 26044 		 * @method renderHtml
       
 26045 		 * @return {String} HTML representing the control.
       
 26046 		 */
       
 26047 		renderHtml: function() {
       
 26048 			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
       
 26049 			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
       
 26050 
       
 26051 			self.preRender();
       
 26052 			layout.preRender(self);
       
 26053 
       
 26054 			if (settings.title) {
       
 26055 				headerHtml = (
       
 26056 					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
       
 26057 						'<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
       
 26058 						'<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>' +
       
 26059 						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
       
 26060 					'</div>'
       
 26061 				);
       
 26062 			}
       
 26063 
       
 26064 			if (settings.url) {
       
 26065 				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
       
 26066 			}
       
 26067 
       
 26068 			if (typeof html == "undefined") {
       
 26069 				html = layout.renderHtml(self);
       
 26070 			}
       
 26071 
       
 26072 			if (self.statusbar) {
       
 26073 				footerHtml = self.statusbar.renderHtml();
       
 26074 			}
       
 26075 
       
 26076 			return (
       
 26077 				'<div id="' + id + '" class="' + self.classes() + '" hidefocus="1">' +
       
 26078 					'<div class="' + self.classPrefix + 'reset" role="application">' +
       
 26079 						headerHtml +
       
 26080 						'<div id="' + id + '-body" class="' + self.classes('body') + '">' +
       
 26081 							html +
       
 26082 						'</div>' +
       
 26083 						footerHtml +
       
 26084 					'</div>' +
       
 26085 				'</div>'
       
 26086 			);
       
 26087 		},
       
 26088 
       
 26089 		/**
       
 26090 		 * Switches the window fullscreen mode.
       
 26091 		 *
       
 26092 		 * @method fullscreen
       
 26093 		 * @param {Boolean} state True/false state.
       
 26094 		 * @return {tinymce.ui.Window} Current window instance.
       
 26095 		 */
       
 26096 		fullscreen: function(state) {
       
 26097 			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
       
 26098 
       
 26099 			if (state != self._fullscreen) {
       
 26100 				DomUtils.on(window, 'resize', function() {
       
 26101 					var time;
       
 26102 
       
 26103 					if (self._fullscreen) {
       
 26104 						// Time the layout time if it's to slow use a timeout to not hog the CPU
       
 26105 						if (!slowRendering) {
       
 26106 							time = new Date().getTime();
       
 26107 
       
 26108 							var rect = DomUtils.getWindowSize();
       
 26109 							self.moveTo(0, 0).resizeTo(rect.w, rect.h);
       
 26110 
       
 26111 							if ((new Date().getTime()) - time > 50) {
       
 26112 								slowRendering = true;
       
 26113 							}
       
 26114 						} else {
       
 26115 							if (!self._timer) {
       
 26116 								self._timer = setTimeout(function() {
       
 26117 									var rect = DomUtils.getWindowSize();
       
 26118 									self.moveTo(0, 0).resizeTo(rect.w, rect.h);
       
 26119 
       
 26120 									self._timer = 0;
       
 26121 								}, 50);
       
 26122 							}
       
 26123 						}
       
 26124 					}
       
 26125 				});
       
 26126 
       
 26127 				layoutRect = self.layoutRect();
       
 26128 				self._fullscreen = state;
       
 26129 
       
 26130 				if (!state) {
       
 26131 					self._borderBox = self.parseBox(self.settings.border);
       
 26132 					self.getEl('head').style.display = '';
       
 26133 					layoutRect.deltaH += layoutRect.headerH;
       
 26134 					DomUtils.removeClass(documentElement, prefix + 'fullscreen');
       
 26135 					DomUtils.removeClass(document.body, prefix + 'fullscreen');
       
 26136 					self.removeClass('fullscreen');
       
 26137 					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
       
 26138 				} else {
       
 26139 					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
       
 26140 
       
 26141 					self._borderBox = self.parseBox('0');
       
 26142 					self.getEl('head').style.display = 'none';
       
 26143 					layoutRect.deltaH -= layoutRect.headerH + 2;
       
 26144 					DomUtils.addClass(documentElement, prefix + 'fullscreen');
       
 26145 					DomUtils.addClass(document.body, prefix + 'fullscreen');
       
 26146 					self.addClass('fullscreen');
       
 26147 
       
 26148 					var rect = DomUtils.getWindowSize();
       
 26149 					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
       
 26150 				}
       
 26151 			}
       
 26152 
       
 26153 			return self.reflow();
       
 26154 		},
       
 26155 
       
 26156 		/**
       
 26157 		 * Called after the control has been rendered.
       
 26158 		 *
       
 26159 		 * @method postRender
       
 26160 		 */
       
 26161 		postRender: function() {
       
 26162 			var self = this, startPos;
       
 26163 
       
 26164 			setTimeout(function() {
       
 26165 				self.addClass('in');
       
 26166 			}, 0);
       
 26167 
       
 26168 			self._super();
       
 26169 
       
 26170 			if (self.statusbar) {
       
 26171 				self.statusbar.postRender();
       
 26172 			}
       
 26173 
       
 26174 			self.focus();
       
 26175 
       
 26176 			this.dragHelper = new DragHelper(self._id + '-dragh', {
       
 26177 				start: function() {
       
 26178 					startPos = {
       
 26179 						x: self.layoutRect().x,
       
 26180 						y: self.layoutRect().y
       
 26181 					};
       
 26182 				},
       
 26183 
       
 26184 				drag: function(e) {
       
 26185 					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
       
 26186 				}
       
 26187 			});
       
 26188 
       
 26189 			self.on('submit', function(e) {
       
 26190 				if (!e.isDefaultPrevented()) {
       
 26191 					self.close();
       
 26192 				}
       
 26193 			});
       
 26194 		},
       
 26195 
       
 26196 		/**
       
 26197 		 * Fires a submit event with the serialized form.
       
 26198 		 *
       
 26199 		 * @method submit
       
 26200 		 * @return {Object} Event arguments object.
       
 26201 		 */
       
 26202 		submit: function() {
       
 26203 			return this.fire('submit', {data: this.toJSON()});
       
 26204 		},
       
 26205 
       
 26206 		/**
       
 26207 		 * Removes the current control from DOM and from UI collections.
       
 26208 		 *
       
 26209 		 * @method remove
       
 26210 		 * @return {tinymce.ui.Control} Current control instance.
       
 26211 		 */
       
 26212 		remove: function() {
       
 26213 			var self = this, prefix = self.classPrefix;
       
 26214 
       
 26215 			self.dragHelper.destroy();
       
 26216 			self._super();
       
 26217 
       
 26218 			if (self.statusbar) {
       
 26219 				this.statusbar.remove();
       
 26220 			}
       
 26221 
       
 26222 			if (self._fullscreen) {
       
 26223 				DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
       
 26224 				DomUtils.removeClass(document.body, prefix + 'fullscreen');
       
 26225 			}
       
 26226 		},
       
 26227 
       
 26228 		/**
       
 26229 		 * Returns the contentWindow object of the iframe if it exists.
       
 26230 		 *
       
 26231 		 * @method getContentWindow
       
 26232 		 * @return {Window} window object or null.
       
 26233 		 */
       
 26234 		getContentWindow: function() {
       
 26235 			var ifr = this.getEl().getElementsByTagName('iframe')[0];
       
 26236 			return ifr ? ifr.contentWindow : null;
       
 26237 		}
       
 26238 	});
       
 26239 
       
 26240 	return Window;
       
 26241 });
       
 26242 
       
 26243 // Included from: js/tinymce/classes/ui/MessageBox.js
       
 26244 
       
 26245 /**
       
 26246  * MessageBox.js
       
 26247  *
       
 26248  * Copyright, Moxiecode Systems AB
       
 26249  * Released under LGPL License.
       
 26250  *
       
 26251  * License: http://www.tinymce.com/license
       
 26252  * Contributing: http://www.tinymce.com/contributing
       
 26253  */
       
 26254 
       
 26255 /**
       
 26256  * This class is used to create MessageBoxes like alerts/confirms etc.
       
 26257  *
       
 26258  * @class tinymce.ui.MessageBox
       
 26259  * @extends tinymce.ui.Window
       
 26260  */
       
 26261 define("tinymce/ui/MessageBox", [
       
 26262 	"tinymce/ui/Window"
       
 26263 ], function(Window) {
       
 26264 	"use strict";
       
 26265 
       
 26266 	var MessageBox = Window.extend({
       
 26267 		/**
       
 26268 		 * Constructs a instance with the specified settings.
       
 26269 		 *
       
 26270 		 * @constructor
       
 26271 		 * @param {Object} settings Name/value object with settings.
       
 26272 		 */
       
 26273 		init: function(settings) {
       
 26274 			settings = {
       
 26275 				border: 1,
       
 26276 				padding: 20,
       
 26277 				layout: 'flex',
       
 26278 				pack: "center",
       
 26279 				align: "center",
       
 26280 				containerCls: 'panel',
       
 26281 				autoScroll: true,
       
 26282 				buttons: {type: "button", text: "Ok", action: "ok"},
       
 26283 				items: {
       
 26284 					type: "label",
       
 26285 					multiline: true,
       
 26286 					maxWidth: 500,
       
 26287 					maxHeight: 200
       
 26288 				}
       
 26289 			};
       
 26290 
       
 26291 			this._super(settings);
       
 26292 		},
       
 26293 
       
 26294 		Statics: {
       
 26295 			/**
       
 26296 			 * Ok buttons constant.
       
 26297 			 *
       
 26298 			 * @static
       
 26299 			 * @final
       
 26300 			 * @field {Number} OK
       
 26301 			 */
       
 26302 			OK: 1,
       
 26303 
       
 26304 			/**
       
 26305 			 * Ok/cancel buttons constant.
       
 26306 			 *
       
 26307 			 * @static
       
 26308 			 * @final
       
 26309 			 * @field {Number} OK_CANCEL
       
 26310 			 */
       
 26311 			OK_CANCEL: 2,
       
 26312 
       
 26313 			/**
       
 26314 			 * yes/no buttons constant.
       
 26315 			 *
       
 26316 			 * @static
       
 26317 			 * @final
       
 26318 			 * @field {Number} YES_NO
       
 26319 			 */
       
 26320 			YES_NO: 3,
       
 26321 
       
 26322 			/**
       
 26323 			 * yes/no/cancel buttons constant.
       
 26324 			 *
       
 26325 			 * @static
       
 26326 			 * @final
       
 26327 			 * @field {Number} YES_NO_CANCEL
       
 26328 			 */
       
 26329 			YES_NO_CANCEL: 4,
       
 26330 
       
 26331 			/**
       
 26332 			 * Constructs a new message box and renders it to the body element.
       
 26333 			 *
       
 26334 			 * @static
       
 26335 			 * @method msgBox
       
 26336 			 * @param {Object} settings Name/value object with settings.
       
 26337 			 */
       
 26338 			msgBox: function(settings) {
       
 26339 				var buttons, callback = settings.callback || function() {};
       
 26340 
       
 26341 				function createButton(text, status, primary) {
       
 26342 					return {
       
 26343 						type: "button",
       
 26344 						text: text,
       
 26345 						subtype: primary ? 'primary' : '',
       
 26346 						onClick: function(e) {
       
 26347 							e.control.parents()[1].close();
       
 26348 							callback(status);
       
 26349 						}
       
 26350 					};
       
 26351 				}
       
 26352 
       
 26353 				switch (settings.buttons) {
       
 26354 					case MessageBox.OK_CANCEL:
       
 26355 						buttons = [
       
 26356 							createButton('Ok', true, true),
       
 26357 							createButton('Cancel', false)
       
 26358 						];
       
 26359 						break;
       
 26360 
       
 26361 					case MessageBox.YES_NO:
       
 26362 					case MessageBox.YES_NO_CANCEL:
       
 26363 						buttons = [
       
 26364 							createButton('Yes', 1, true),
       
 26365 							createButton('No', 0)
       
 26366 						];
       
 26367 
       
 26368 						if (settings.buttons == MessageBox.YES_NO_CANCEL) {
       
 26369 							buttons.push(createButton('Cancel', -1));
       
 26370 						}
       
 26371 						break;
       
 26372 
       
 26373 					default:
       
 26374 						buttons = [
       
 26375 							createButton('Ok', true, true)
       
 26376 						];
       
 26377 						break;
       
 26378 				}
       
 26379 
       
 26380 				return new Window({
       
 26381 					padding: 20,
       
 26382 					x: settings.x,
       
 26383 					y: settings.y,
       
 26384 					minWidth: 300,
       
 26385 					minHeight: 100,
       
 26386 					layout: "flex",
       
 26387 					pack: "center",
       
 26388 					align: "center",
       
 26389 					buttons: buttons,
       
 26390 					title: settings.title,
       
 26391 					role: 'alertdialog',
       
 26392 					items: {
       
 26393 						type: "label",
       
 26394 						multiline: true,
       
 26395 						maxWidth: 500,
       
 26396 						maxHeight: 200,
       
 26397 						text: settings.text
       
 26398 					},
       
 26399 					onPostRender: function() {
       
 26400 						this.aria('describedby', this.items()[0]._id);
       
 26401 					},
       
 26402 					onClose: settings.onClose,
       
 26403 					onCancel: function() {
       
 26404 						callback(false);
       
 26405 					}
       
 26406 				}).renderTo(document.body).reflow();
       
 26407 			},
       
 26408 
       
 26409 			/**
       
 26410 			 * Creates a new alert dialog.
       
 26411 			 *
       
 26412 			 * @method alert
       
 26413 			 * @param {Object} settings Settings for the alert dialog.
       
 26414 			 * @param {function} [callback] Callback to execute when the user makes a choice.
       
 26415 			 */
       
 26416 			alert: function(settings, callback) {
       
 26417 				if (typeof settings == "string") {
       
 26418 					settings = {text: settings};
       
 26419 				}
       
 26420 
       
 26421 				settings.callback = callback;
       
 26422 				return MessageBox.msgBox(settings);
       
 26423 			},
       
 26424 
       
 26425 			/**
       
 26426 			 * Creates a new confirm dialog.
       
 26427 			 *
       
 26428 			 * @method confirm
       
 26429 			 * @param {Object} settings Settings for the confirm dialog.
       
 26430 			 * @param {function} [callback] Callback to execute when the user makes a choice.
       
 26431 			 */
       
 26432 			confirm: function(settings, callback) {
       
 26433 				if (typeof settings == "string") {
       
 26434 					settings = {text: settings};
       
 26435 				}
       
 26436 
       
 26437 				settings.callback = callback;
       
 26438 				settings.buttons = MessageBox.OK_CANCEL;
       
 26439 
       
 26440 				return MessageBox.msgBox(settings);
       
 26441 			}
       
 26442 		}
       
 26443 	});
       
 26444 
       
 26445 	return MessageBox;
       
 26446 });
       
 26447 
       
 26448 // Included from: js/tinymce/classes/WindowManager.js
       
 26449 
       
 26450 /**
       
 26451  * WindowManager.js
       
 26452  *
       
 26453  * Copyright, Moxiecode Systems AB
       
 26454  * Released under LGPL License.
       
 26455  *
       
 26456  * License: http://www.tinymce.com/license
       
 26457  * Contributing: http://www.tinymce.com/contributing
       
 26458  */
       
 26459 
       
 26460 /**
       
 26461  * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs.
       
 26462  *
       
 26463  * @class tinymce.WindowManager
       
 26464  * @example
       
 26465  * // Opens a new dialog with the file.htm file and the size 320x240
       
 26466  * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
       
 26467  * tinymce.activeEditor.windowManager.open({
       
 26468  *    url: 'file.htm',
       
 26469  *    width: 320,
       
 26470  *    height: 240
       
 26471  * }, {
       
 26472  *    custom_param: 1
       
 26473  * });
       
 26474  *
       
 26475  * // Displays an alert box using the active editors window manager instance
       
 26476  * tinymce.activeEditor.windowManager.alert('Hello world!');
       
 26477  *
       
 26478  * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
       
 26479  * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
       
 26480  *    if (s)
       
 26481  *       tinymce.activeEditor.windowManager.alert("Ok");
       
 26482  *    else
       
 26483  *       tinymce.activeEditor.windowManager.alert("Cancel");
       
 26484  * });
       
 26485  */
       
 26486 define("tinymce/WindowManager", [
       
 26487 	"tinymce/ui/Window",
       
 26488 	"tinymce/ui/MessageBox"
       
 26489 ], function(Window, MessageBox) {
       
 26490 	return function(editor) {
       
 26491 		var self = this, windows = [];
       
 26492 
       
 26493 		function getTopMostWindow() {
       
 26494 			if (windows.length) {
       
 26495 				return windows[windows.length - 1];
       
 26496 			}
       
 26497 		}
       
 26498 
       
 26499 		self.windows = windows;
       
 26500 
       
 26501 		editor.on('remove', function() {
       
 26502 			var i = windows.length;
       
 26503 
       
 26504 			while (i--) {
       
 26505 				windows[i].close();
       
 26506 			}
       
 26507 		});
       
 26508 
       
 26509 		/**
       
 26510 		 * Opens a new window.
       
 26511 		 *
       
 26512 		 * @method open
       
 26513 		 * @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
       
 26514 		 * @option {String} title Window title.
       
 26515 		 * @option {String} file URL of the file to open in the window.
       
 26516 		 * @option {Number} width Width in pixels.
       
 26517 		 * @option {Number} height Height in pixels.
       
 26518 		 * @option {Boolean} autoScroll Specifies whether the popup window can have scrollbars if required (i.e. content
       
 26519 		 * larger than the popup size specified).
       
 26520 		 */
       
 26521 		self.open = function(args, params) {
       
 26522 			var win;
       
 26523 
       
 26524 			editor.editorManager.setActive(editor);
       
 26525 
       
 26526 			args.title = args.title || ' ';
       
 26527 
       
 26528 			// Handle URL
       
 26529 			args.url = args.url || args.file; // Legacy
       
 26530 			if (args.url) {
       
 26531 				args.width = parseInt(args.width || 320, 10);
       
 26532 				args.height = parseInt(args.height || 240, 10);
       
 26533 			}
       
 26534 
       
 26535 			// Handle body
       
 26536 			if (args.body) {
       
 26537 				args.items = {
       
 26538 					defaults: args.defaults,
       
 26539 					type: args.bodyType || 'form',
       
 26540 					items: args.body
       
 26541 				};
       
 26542 			}
       
 26543 
       
 26544 			if (!args.url && !args.buttons) {
       
 26545 				args.buttons = [
       
 26546 					{text: 'Ok', subtype: 'primary', onclick: function() {
       
 26547 						win.find('form')[0].submit();
       
 26548 					}},
       
 26549 
       
 26550 					{text: 'Cancel', onclick: function() {
       
 26551 						win.close();
       
 26552 					}}
       
 26553 				];
       
 26554 			}
       
 26555 
       
 26556 			win = new Window(args);
       
 26557 			windows.push(win);
       
 26558 
       
 26559 			win.on('close', function() {
       
 26560 				var i = windows.length;
       
 26561 
       
 26562 				while (i--) {
       
 26563 					if (windows[i] === win) {
       
 26564 						windows.splice(i, 1);
       
 26565 					}
       
 26566 				}
       
 26567 
       
 26568 				if (!windows.length) {
       
 26569 					editor.focus();
       
 26570 				}
       
 26571 			});
       
 26572 
       
 26573 			// Handle data
       
 26574 			if (args.data) {
       
 26575 				win.on('postRender', function() {
       
 26576 					this.find('*').each(function(ctrl) {
       
 26577 						var name = ctrl.name();
       
 26578 
       
 26579 						if (name in args.data) {
       
 26580 							ctrl.value(args.data[name]);
       
 26581 						}
       
 26582 					});
       
 26583 				});
       
 26584 			}
       
 26585 
       
 26586 			// store args and parameters
       
 26587 			win.features = args || {};
       
 26588 			win.params = params || {};
       
 26589 
       
 26590 			// Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
       
 26591 			if (windows.length === 1) {
       
 26592 				editor.nodeChanged();
       
 26593 			}
       
 26594 
       
 26595 			return win.renderTo().reflow();
       
 26596 		};
       
 26597 
       
 26598 		/**
       
 26599 		 * Creates a alert dialog. Please don't use the blocking behavior of this
       
 26600 		 * native version use the callback method instead then it can be extended.
       
 26601 		 *
       
 26602 		 * @method alert
       
 26603 		 * @param {String} message Text to display in the new alert dialog.
       
 26604 		 * @param {function} callback Callback function to be executed after the user has selected ok.
       
 26605 		 * @param {Object} scope Optional scope to execute the callback in.
       
 26606 		 * @example
       
 26607 		 * // Displays an alert box using the active editors window manager instance
       
 26608 		 * tinymce.activeEditor.windowManager.alert('Hello world!');
       
 26609 		 */
       
 26610 		self.alert = function(message, callback, scope) {
       
 26611 			MessageBox.alert(message, function() {
       
 26612 				if (callback) {
       
 26613 					callback.call(scope || this);
       
 26614 				} else {
       
 26615 					editor.focus();
       
 26616 				}
       
 26617 			});
       
 26618 		};
       
 26619 
       
 26620 		/**
       
 26621 		 * Creates a confirm dialog. Please don't use the blocking behavior of this
       
 26622 		 * native version use the callback method instead then it can be extended.
       
 26623 		 *
       
 26624 		 * @method confirm
       
 26625 		 * @param {String} messageText to display in the new confirm dialog.
       
 26626 		 * @param {function} callback Callback function to be executed after the user has selected ok or cancel.
       
 26627 		 * @param {Object} scope Optional scope to execute the callback in.
       
 26628 		 * @example
       
 26629 		 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
       
 26630 		 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
       
 26631 		 *    if (s)
       
 26632 		 *       tinymce.activeEditor.windowManager.alert("Ok");
       
 26633 		 *    else
       
 26634 		 *       tinymce.activeEditor.windowManager.alert("Cancel");
       
 26635 		 * });
       
 26636 		 */
       
 26637 		self.confirm = function(message, callback, scope) {
       
 26638 			MessageBox.confirm(message, function(state) {
       
 26639 				callback.call(scope || this, state);
       
 26640 			});
       
 26641 		};
       
 26642 
       
 26643 		/**
       
 26644 		 * Closes the top most window.
       
 26645 		 *
       
 26646 		 * @method close
       
 26647 		 */
       
 26648 		self.close = function() {
       
 26649 			if (getTopMostWindow()) {
       
 26650 				getTopMostWindow().close();
       
 26651 			}
       
 26652 		};
       
 26653 
       
 26654 		/**
       
 26655 		 * Returns the params of the last window open call. This can be used in iframe based
       
 26656 		 * dialog to get params passed from the tinymce plugin.
       
 26657 		 *
       
 26658 		 * @example
       
 26659 		 * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams();
       
 26660 		 *
       
 26661 		 * @method getParams
       
 26662 		 * @return {Object} Name/value object with parameters passed from windowManager.open call.
       
 26663 		 */
       
 26664 		self.getParams = function() {
       
 26665 			return getTopMostWindow() ? getTopMostWindow().params : null;
       
 26666 		};
       
 26667 
       
 26668 		/**
       
 26669 		 * Sets the params of the last opened window.
       
 26670 		 *
       
 26671 		 * @method setParams
       
 26672 		 * @param {Object} params Params object to set for the last opened window.
       
 26673 		 */
       
 26674 		self.setParams = function(params) {
       
 26675 			if (getTopMostWindow()) {
       
 26676 				getTopMostWindow().params = params;
       
 26677 			}
       
 26678 		};
       
 26679 
       
 26680 		/**
       
 26681 		 * Returns the currently opened window objects.
       
 26682 		 *
       
 26683 		 * @method getWindows
       
 26684 		 * @return {Array} Array of the currently opened windows.
       
 26685 		 */
       
 26686 		self.getWindows = function() {
       
 26687 			return windows;
       
 26688 		};
       
 26689 	};
       
 26690 });
       
 26691 
       
 26692 // Included from: js/tinymce/classes/util/Quirks.js
       
 26693 
       
 26694 /**
       
 26695  * Quirks.js
       
 26696  *
       
 26697  * Copyright, Moxiecode Systems AB
       
 26698  * Released under LGPL License.
       
 26699  *
       
 26700  * License: http://www.tinymce.com/license
       
 26701  * Contributing: http://www.tinymce.com/contributing
       
 26702  *
       
 26703  * @ignore-file
       
 26704  */
       
 26705 
       
 26706 /**
       
 26707  * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
       
 26708  *
       
 26709  * @class tinymce.util.Quirks
       
 26710  */
       
 26711 define("tinymce/util/Quirks", [
       
 26712 	"tinymce/util/VK",
       
 26713 	"tinymce/dom/RangeUtils",
       
 26714 	"tinymce/dom/TreeWalker",
       
 26715 	"tinymce/html/Node",
       
 26716 	"tinymce/html/Entities",
       
 26717 	"tinymce/Env",
       
 26718 	"tinymce/util/Tools"
       
 26719 ], function(VK, RangeUtils, TreeWalker, Node, Entities, Env, Tools) {
       
 26720 	return function(editor) {
       
 26721 		var each = Tools.each, $ = editor.$;
       
 26722 		var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
       
 26723 			settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
       
 26724 		var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
       
 26725 		var mceInternalUrlPrefix = 'data:text/mce-internal,';
       
 26726 		var mceInternalDataType = isIE ? 'Text' : 'URL';
       
 26727 
       
 26728 		/**
       
 26729 		 * Executes a command with a specific state this can be to enable/disable browser editing features.
       
 26730 		 */
       
 26731 		function setEditorCommandState(cmd, state) {
       
 26732 			try {
       
 26733 				editor.getDoc().execCommand(cmd, false, state);
       
 26734 			} catch (ex) {
       
 26735 				// Ignore
       
 26736 			}
       
 26737 		}
       
 26738 
       
 26739 		/**
       
 26740 		 * Returns current IE document mode.
       
 26741 		 */
       
 26742 		function getDocumentMode() {
       
 26743 			var documentMode = editor.getDoc().documentMode;
       
 26744 
       
 26745 			return documentMode ? documentMode : 6;
       
 26746 		}
       
 26747 
       
 26748 		/**
       
 26749 		 * Returns true/false if the event is prevented or not.
       
 26750 		 *
       
 26751 		 * @private
       
 26752 		 * @param {Event} e Event object.
       
 26753 		 * @return {Boolean} true/false if the event is prevented or not.
       
 26754 		 */
       
 26755 		function isDefaultPrevented(e) {
       
 26756 			return e.isDefaultPrevented();
       
 26757 		}
       
 26758 
       
 26759 		/**
       
 26760 		 * Sets Text/URL data on the event's dataTransfer object to a special data:text/mce-internal url.
       
 26761 		 * This is to workaround the inability to set custom contentType on IE and Safari.
       
 26762 		 * The editor's selected content is encoded into this url so drag and drop between editors will work.
       
 26763 		 *
       
 26764 		 * @private
       
 26765 		 * @param {DragEvent} e Event object
       
 26766 		 */
       
 26767 		function setMceInteralContent(e) {
       
 26768 			var selectionHtml;
       
 26769 
       
 26770 			if (e.dataTransfer) {
       
 26771 				if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') {
       
 26772 					selection.select(e.target);
       
 26773 				}
       
 26774 
       
 26775 				selectionHtml = editor.selection.getContent();
       
 26776 
       
 26777 				// Safari/IE doesn't support custom dataTransfer items so we can only use URL and Text
       
 26778 				if (selectionHtml.length > 0) {
       
 26779 					e.dataTransfer.setData(mceInternalDataType, mceInternalUrlPrefix + escape(selectionHtml));
       
 26780 				}
       
 26781 			}
       
 26782 		}
       
 26783 
       
 26784 		/**
       
 26785 		 * Gets content of special data:text/mce-internal url on the event's dataTransfer object.
       
 26786 		 * This is to workaround the inability to set custom contentType on IE and Safari.
       
 26787 		 * The editor's selected content is encoded into this url so drag and drop between editors will work.
       
 26788 		 *
       
 26789 		 * @private
       
 26790 		 * @param {DragEvent} e Event object
       
 26791 		 * @returns {String} mce-internal content
       
 26792 		 */
       
 26793 		function getMceInternalContent(e) {
       
 26794 			var internalContent, content;
       
 26795 
       
 26796 			if (e.dataTransfer) {
       
 26797 				internalContent = e.dataTransfer.getData(mceInternalDataType);
       
 26798 
       
 26799 				if (internalContent && internalContent.indexOf(mceInternalUrlPrefix) >= 0) {
       
 26800 					content = unescape(internalContent.substr(mceInternalUrlPrefix.length));
       
 26801 				}
       
 26802 			}
       
 26803 
       
 26804 			return content;
       
 26805 		}
       
 26806 
       
 26807 		/**
       
 26808 		 * Inserts contents using the paste clipboard command if it's available if it isn't it will fallback
       
 26809 		 * to the core command.
       
 26810 		 *
       
 26811 		 * @private
       
 26812 		 * @param {String} content Content to insert at selection.
       
 26813 		 */
       
 26814 		function insertClipboardContents(content) {
       
 26815 			if (editor.queryCommandSupported('mceInsertClipboardContent')) {
       
 26816 				editor.execCommand('mceInsertClipboardContent', false, {content: content});
       
 26817 			} else {
       
 26818 				editor.execCommand('mceInsertContent', false, content);
       
 26819 			}
       
 26820 		}
       
 26821 
       
 26822 		/**
       
 26823 		 * Fixes a WebKit bug when deleting contents using backspace or delete key.
       
 26824 		 * WebKit will produce a span element if you delete across two block elements.
       
 26825 		 *
       
 26826 		 * Example:
       
 26827 		 * <h1>a</h1><p>|b</p>
       
 26828 		 *
       
 26829 		 * Will produce this on backspace:
       
 26830 		 * <h1>a<span style="<all runtime styles>">b</span></p>
       
 26831 		 *
       
 26832 		 * This fixes the backspace to produce:
       
 26833 		 * <h1>a|b</p>
       
 26834 		 *
       
 26835 		 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
       
 26836 		 *
       
 26837 		 * This fixes the following delete scenarios:
       
 26838 		 *  1. Delete by pressing backspace key.
       
 26839 		 *  2. Delete by pressing delete key.
       
 26840 		 *  3. Delete by pressing backspace key with ctrl/cmd (Word delete).
       
 26841 		 *  4. Delete by pressing delete key with ctrl/cmd (Word delete).
       
 26842 		 *  5. Delete by drag/dropping contents inside the editor.
       
 26843 		 *  6. Delete by using Cut Ctrl+X/Cmd+X.
       
 26844 		 *  7. Delete by selecting contents and writing a character.
       
 26845 		 *
       
 26846 		 * This code is a ugly hack since writing full custom delete logic for just this bug
       
 26847 		 * fix seemed like a huge task. I hope we can remove this before the year 2030.
       
 26848 		 */
       
 26849 		function cleanupStylesWhenDeleting() {
       
 26850 			var doc = editor.getDoc(), dom = editor.dom, selection = editor.selection;
       
 26851 			var MutationObserver = window.MutationObserver, olderWebKit, dragStartRng;
       
 26852 
       
 26853 			// Add mini polyfill for older WebKits
       
 26854 			// TODO: Remove this when old Safari versions gets updated
       
 26855 			if (!MutationObserver) {
       
 26856 				olderWebKit = true;
       
 26857 
       
 26858 				MutationObserver = function() {
       
 26859 					var records = [], target;
       
 26860 
       
 26861 					function nodeInsert(e) {
       
 26862 						var target = e.relatedNode || e.target;
       
 26863 						records.push({target: target, addedNodes: [target]});
       
 26864 					}
       
 26865 
       
 26866 					function attrModified(e) {
       
 26867 						var target = e.relatedNode || e.target;
       
 26868 						records.push({target: target, attributeName: e.attrName});
       
 26869 					}
       
 26870 
       
 26871 					this.observe = function(node) {
       
 26872 						target = node;
       
 26873 						target.addEventListener('DOMSubtreeModified', nodeInsert, false);
       
 26874 						target.addEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
       
 26875 						target.addEventListener('DOMNodeInserted', nodeInsert, false);
       
 26876 						target.addEventListener('DOMAttrModified', attrModified, false);
       
 26877 					};
       
 26878 
       
 26879 					this.disconnect = function() {
       
 26880 						target.removeEventListener('DOMSubtreeModified', nodeInsert, false);
       
 26881 						target.removeEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
       
 26882 						target.removeEventListener('DOMNodeInserted', nodeInsert, false);
       
 26883 						target.removeEventListener('DOMAttrModified', attrModified, false);
       
 26884 					};
       
 26885 
       
 26886 					this.takeRecords = function() {
       
 26887 						return records;
       
 26888 					};
       
 26889 				};
       
 26890 			}
       
 26891 
       
 26892 			function isTrailingBr(node) {
       
 26893 				var blockElements = dom.schema.getBlockElements(), rootNode = editor.getBody();
       
 26894 
       
 26895 				if (node.nodeName != 'BR') {
       
 26896 					return false;
       
 26897 				}
       
 26898 
       
 26899 				for (node = node; node != rootNode && !blockElements[node.nodeName]; node = node.parentNode) {
       
 26900 					if (node.nextSibling) {
       
 26901 						return false;
       
 26902 					}
       
 26903 				}
       
 26904 
       
 26905 				return true;
       
 26906 			}
       
 26907 
       
 26908 			function isSiblingsIgnoreWhiteSpace(node1, node2) {
       
 26909 				var node;
       
 26910 
       
 26911 				for (node = node1.nextSibling; node && node != node2; node = node.nextSibling) {
       
 26912 					if (node.nodeType == 3 && $.trim(node.data).length === 0) {
       
 26913 						continue;
       
 26914 					}
       
 26915 
       
 26916 					if (node !== node2) {
       
 26917 						return false;
       
 26918 					}
       
 26919 				}
       
 26920 
       
 26921 				return node === node2;
       
 26922 			}
       
 26923 
       
 26924 			function findCaretNode(node, forward, startNode) {
       
 26925 				var walker, current, nonEmptyElements;
       
 26926 
       
 26927 				nonEmptyElements = dom.schema.getNonEmptyElements();
       
 26928 
       
 26929 				walker = new TreeWalker(startNode || node, node);
       
 26930 
       
 26931 				while ((current = walker[forward ? 'next' : 'prev']())) {
       
 26932 					if (nonEmptyElements[current.nodeName] && !isTrailingBr(current)) {
       
 26933 						return current;
       
 26934 					}
       
 26935 
       
 26936 					if (current.nodeType == 3 && current.data.length > 0) {
       
 26937 						return current;
       
 26938 					}
       
 26939 				}
       
 26940 			}
       
 26941 
       
 26942 			function deleteRangeBetweenTextBlocks(rng) {
       
 26943 				var startBlock, endBlock, caretNodeBefore, caretNodeAfter, textBlockElements;
       
 26944 
       
 26945 				if (rng.collapsed) {
       
 26946 					return;
       
 26947 				}
       
 26948 
       
 26949 				startBlock = dom.getParent(RangeUtils.getNode(rng.startContainer, rng.startOffset), dom.isBlock);
       
 26950 				endBlock = dom.getParent(RangeUtils.getNode(rng.endContainer, rng.endOffset), dom.isBlock);
       
 26951 				textBlockElements = editor.schema.getTextBlockElements();
       
 26952 
       
 26953 				if (startBlock == endBlock) {
       
 26954 					return;
       
 26955 				}
       
 26956 
       
 26957 				if (!textBlockElements[startBlock.nodeName] || !textBlockElements[endBlock.nodeName]) {
       
 26958 					return;
       
 26959 				}
       
 26960 
       
 26961 				if (dom.getContentEditable(startBlock) === "false" || dom.getContentEditable(endBlock) === "false") {
       
 26962 					return;
       
 26963 				}
       
 26964 
       
 26965 				rng.deleteContents();
       
 26966 
       
 26967 				caretNodeBefore = findCaretNode(startBlock, false);
       
 26968 				caretNodeAfter = findCaretNode(endBlock, true);
       
 26969 
       
 26970 				if (!dom.isEmpty(endBlock)) {
       
 26971 					$(startBlock).append(endBlock.childNodes);
       
 26972 				}
       
 26973 
       
 26974 				$(endBlock).remove();
       
 26975 
       
 26976 				if (caretNodeBefore) {
       
 26977 					if (caretNodeBefore.nodeType == 1) {
       
 26978 						if (caretNodeBefore.nodeName == "BR") {
       
 26979 							rng.setStartBefore(caretNodeBefore);
       
 26980 							rng.setEndBefore(caretNodeBefore);
       
 26981 						} else {
       
 26982 							rng.setStartAfter(caretNodeBefore);
       
 26983 							rng.setEndAfter(caretNodeBefore);
       
 26984 						}
       
 26985 					} else {
       
 26986 						rng.setStart(caretNodeBefore, caretNodeBefore.data.length);
       
 26987 						rng.setEnd(caretNodeBefore, caretNodeBefore.data.length);
       
 26988 					}
       
 26989 				} else if (caretNodeAfter) {
       
 26990 					if (caretNodeAfter.nodeType == 1) {
       
 26991 						rng.setStartBefore(caretNodeAfter);
       
 26992 						rng.setEndBefore(caretNodeAfter);
       
 26993 					} else {
       
 26994 						rng.setStart(caretNodeAfter, 0);
       
 26995 						rng.setEnd(caretNodeAfter, 0);
       
 26996 					}
       
 26997 				}
       
 26998 
       
 26999 				selection.setRng(rng);
       
 27000 
       
 27001 				return true;
       
 27002 			}
       
 27003 
       
 27004 			function expandBetweenBlocks(rng, isForward) {
       
 27005 				var caretNode, targetCaretNode, textBlock, targetTextBlock, container, offset;
       
 27006 
       
 27007 				if (!rng.collapsed) {
       
 27008 					return rng;
       
 27009 				}
       
 27010 
       
 27011 				container = rng.startContainer;
       
 27012 				offset = rng.startOffset;
       
 27013 
       
 27014 				if (container.nodeType == 3) {
       
 27015 					if (isForward) {
       
 27016 						if (offset < container.data.length) {
       
 27017 							return rng;
       
 27018 						}
       
 27019 					} else {
       
 27020 						if (offset > 0) {
       
 27021 							return rng;
       
 27022 						}
       
 27023 					}
       
 27024 				}
       
 27025 
       
 27026 				caretNode = RangeUtils.getNode(rng.startContainer, rng.startOffset);
       
 27027 				textBlock = dom.getParent(caretNode, dom.isBlock);
       
 27028 				targetCaretNode = findCaretNode(editor.getBody(), isForward, caretNode);
       
 27029 				targetTextBlock = dom.getParent(targetCaretNode, dom.isBlock);
       
 27030 
       
 27031 				if (!caretNode || !targetCaretNode) {
       
 27032 					return rng;
       
 27033 				}
       
 27034 
       
 27035 				if (targetTextBlock && textBlock != targetTextBlock) {
       
 27036 					if (!isForward) {
       
 27037 						if (!isSiblingsIgnoreWhiteSpace(targetTextBlock, textBlock)) {
       
 27038 							return rng;
       
 27039 						}
       
 27040 
       
 27041 						if (targetCaretNode.nodeType == 1) {
       
 27042 							if (targetCaretNode.nodeName == "BR") {
       
 27043 								rng.setStartBefore(targetCaretNode);
       
 27044 							} else {
       
 27045 								rng.setStartAfter(targetCaretNode);
       
 27046 							}
       
 27047 						} else {
       
 27048 							rng.setStart(targetCaretNode, targetCaretNode.data.length);
       
 27049 						}
       
 27050 
       
 27051 						if (caretNode.nodeType == 1) {
       
 27052 							rng.setEnd(caretNode, 0);
       
 27053 						} else {
       
 27054 							rng.setEndBefore(caretNode);
       
 27055 						}
       
 27056 					} else {
       
 27057 						if (!isSiblingsIgnoreWhiteSpace(textBlock, targetTextBlock)) {
       
 27058 							return rng;
       
 27059 						}
       
 27060 
       
 27061 						if (caretNode.nodeType == 1) {
       
 27062 							if (caretNode.nodeName == "BR") {
       
 27063 								rng.setStartBefore(caretNode);
       
 27064 							} else {
       
 27065 								rng.setStartAfter(caretNode);
       
 27066 							}
       
 27067 						} else {
       
 27068 							rng.setStart(caretNode, caretNode.data.length);
       
 27069 						}
       
 27070 
       
 27071 						if (targetCaretNode.nodeType == 1) {
       
 27072 							rng.setEnd(targetCaretNode, 0);
       
 27073 						} else {
       
 27074 							rng.setEndBefore(targetCaretNode);
       
 27075 						}
       
 27076 					}
       
 27077 				}
       
 27078 
       
 27079 				return rng;
       
 27080 			}
       
 27081 
       
 27082 			function handleTextBlockMergeDelete(isForward) {
       
 27083 				var rng = selection.getRng();
       
 27084 
       
 27085 				rng = expandBetweenBlocks(rng, isForward);
       
 27086 
       
 27087 				if (deleteRangeBetweenTextBlocks(rng)) {
       
 27088 					return true;
       
 27089 				}
       
 27090 			}
       
 27091 
       
 27092 			function customDelete(isForward) {
       
 27093 				var mutationObserver, rng, caretElement;
       
 27094 
       
 27095 				if (handleTextBlockMergeDelete(isForward)) {
       
 27096 					return;
       
 27097 				}
       
 27098 
       
 27099 				Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
       
 27100 					// Mark existing spans
       
 27101 					if (elm.tagName == 'SPAN') {
       
 27102 						elm.setAttribute('mce-data-marked', 1);
       
 27103 					}
       
 27104 
       
 27105 					// Make sure all elements has a data-mce-style attribute
       
 27106 					if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
       
 27107 						editor.dom.setAttrib(elm, 'style', editor.dom.getAttrib(elm, 'style'));
       
 27108 					}
       
 27109 				});
       
 27110 
       
 27111 				// Observe added nodes and style attribute changes
       
 27112 				mutationObserver = new MutationObserver(function() {});
       
 27113 				mutationObserver.observe(editor.getDoc(), {
       
 27114 					childList: true,
       
 27115 					attributes: true,
       
 27116 					subtree: true,
       
 27117 					attributeFilter: ['style']
       
 27118 				});
       
 27119 
       
 27120 				editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
       
 27121 
       
 27122 				rng = editor.selection.getRng();
       
 27123 				caretElement = rng.startContainer.parentNode;
       
 27124 
       
 27125 				Tools.each(mutationObserver.takeRecords(), function(record) {
       
 27126 					if (!dom.isChildOf(record.target, editor.getBody())) {
       
 27127 						return;
       
 27128 					}
       
 27129 
       
 27130 					// Restore style attribute to previous value
       
 27131 					if (record.attributeName == "style") {
       
 27132 						var oldValue = record.target.getAttribute('data-mce-style');
       
 27133 
       
 27134 						if (oldValue) {
       
 27135 							record.target.setAttribute("style", oldValue);
       
 27136 						} else {
       
 27137 							record.target.removeAttribute("style");
       
 27138 						}
       
 27139 					}
       
 27140 
       
 27141 					// Remove all spans that isn't maked and retain selection
       
 27142 					Tools.each(record.addedNodes, function(node) {
       
 27143 						if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
       
 27144 							var offset, container;
       
 27145 
       
 27146 							if (node == caretElement) {
       
 27147 								offset = rng.startOffset;
       
 27148 								container = node.firstChild;
       
 27149 							}
       
 27150 
       
 27151 							dom.remove(node, true);
       
 27152 
       
 27153 							if (container) {
       
 27154 								rng.setStart(container, offset);
       
 27155 								rng.setEnd(container, offset);
       
 27156 								editor.selection.setRng(rng);
       
 27157 							}
       
 27158 						}
       
 27159 					});
       
 27160 				});
       
 27161 
       
 27162 				mutationObserver.disconnect();
       
 27163 
       
 27164 				// Remove any left over marks
       
 27165 				Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
       
 27166 					span.removeAttribute('mce-data-marked');
       
 27167 				});
       
 27168 			}
       
 27169 
       
 27170 			editor.on('keydown', function(e) {
       
 27171 				var isForward = e.keyCode == DELETE, isMetaOrCtrl = e.ctrlKey || e.metaKey;
       
 27172 
       
 27173 				if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
       
 27174 					var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
       
 27175 
       
 27176 					// Ignore non meta delete in the where there is text before/after the caret
       
 27177 					if (!isMetaOrCtrl && rng.collapsed && container.nodeType == 3) {
       
 27178 						if (isForward ? offset < container.data.length : offset > 0) {
       
 27179 							return;
       
 27180 						}
       
 27181 					}
       
 27182 
       
 27183 					e.preventDefault();
       
 27184 
       
 27185 					if (isMetaOrCtrl) {
       
 27186 						editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", e.metaKey ? "lineboundary" : "word");
       
 27187 					}
       
 27188 
       
 27189 					customDelete(isForward);
       
 27190 				}
       
 27191 			});
       
 27192 
       
 27193 			// Handle case where text is deleted by typing over
       
 27194 			editor.on('keypress', function(e) {
       
 27195 				if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode && !VK.metaKeyPressed(e)) {
       
 27196 					var rng, currentFormatNodes, fragmentNode, blockParent, caretNode, charText;
       
 27197 
       
 27198 					rng = editor.selection.getRng();
       
 27199 					charText = String.fromCharCode(e.charCode);
       
 27200 					e.preventDefault();
       
 27201 
       
 27202 					// Keep track of current format nodes
       
 27203 					currentFormatNodes = $(rng.startContainer).parents().filter(function(idx, node) {
       
 27204 						return !!editor.schema.getTextInlineElements()[node.nodeName];
       
 27205 					});
       
 27206 
       
 27207 					customDelete(true);
       
 27208 
       
 27209 					// Check if the browser removed them
       
 27210 					currentFormatNodes = currentFormatNodes.filter(function(idx, node) {
       
 27211 						return !$.contains(editor.getBody(), node);
       
 27212 					});
       
 27213 
       
 27214 					// Then re-add them
       
 27215 					if (currentFormatNodes.length) {
       
 27216 						fragmentNode = dom.createFragment();
       
 27217 
       
 27218 						currentFormatNodes.each(function(idx, formatNode) {
       
 27219 							formatNode = formatNode.cloneNode(false);
       
 27220 
       
 27221 							if (fragmentNode.hasChildNodes()) {
       
 27222 								formatNode.appendChild(fragmentNode.firstChild);
       
 27223 								fragmentNode.appendChild(formatNode);
       
 27224 							} else {
       
 27225 								caretNode = formatNode;
       
 27226 								fragmentNode.appendChild(formatNode);
       
 27227 							}
       
 27228 
       
 27229 							fragmentNode.appendChild(formatNode);
       
 27230 						});
       
 27231 
       
 27232 						caretNode.appendChild(editor.getDoc().createTextNode(charText));
       
 27233 
       
 27234 						// Prevent edge case where older WebKit would add an extra BR element
       
 27235 						blockParent = dom.getParent(rng.startContainer, dom.isBlock);
       
 27236 						if (dom.isEmpty(blockParent)) {
       
 27237 							$(blockParent).empty().append(fragmentNode);
       
 27238 						} else {
       
 27239 							rng.insertNode(fragmentNode);
       
 27240 						}
       
 27241 
       
 27242 						rng.setStart(caretNode.firstChild, 1);
       
 27243 						rng.setEnd(caretNode.firstChild, 1);
       
 27244 						editor.selection.setRng(rng);
       
 27245 					} else {
       
 27246 						editor.selection.setContent(charText);
       
 27247 					}
       
 27248 				}
       
 27249 			});
       
 27250 
       
 27251 			editor.addCommand('Delete', function() {
       
 27252 				customDelete();
       
 27253 			});
       
 27254 
       
 27255 			editor.addCommand('ForwardDelete', function() {
       
 27256 				customDelete(true);
       
 27257 			});
       
 27258 
       
 27259 			// Older WebKits doesn't properly handle the clipboard so we can't add the rest
       
 27260 			if (olderWebKit) {
       
 27261 				return;
       
 27262 			}
       
 27263 
       
 27264 			editor.on('dragstart', function(e) {
       
 27265 				dragStartRng = selection.getRng();
       
 27266 				setMceInteralContent(e);
       
 27267 			});
       
 27268 
       
 27269 			editor.on('drop', function(e) {
       
 27270 				if (!isDefaultPrevented(e)) {
       
 27271 					var internalContent = getMceInternalContent(e);
       
 27272 					if (internalContent) {
       
 27273 						e.preventDefault();
       
 27274 
       
 27275 						// Safari has a weird issue where drag/dropping images sometimes
       
 27276 						// produces a green plus icon. When this happens the caretRangeFromPoint
       
 27277 						// will return "null" even though the x, y coordinate is correct.
       
 27278 						// But if we detach the insert from the drop event we will get a proper range
       
 27279 						window.setTimeout(function() {
       
 27280 							var pointRng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, doc);
       
 27281 
       
 27282 							if (dragStartRng) {
       
 27283 								selection.setRng(dragStartRng);
       
 27284 								dragStartRng = null;
       
 27285 							}
       
 27286 
       
 27287 							customDelete();
       
 27288 							selection.setRng(pointRng);
       
 27289 							insertClipboardContents(internalContent);
       
 27290 						}, 0);
       
 27291 					}
       
 27292 				}
       
 27293 			});
       
 27294 
       
 27295 			editor.on('cut', function(e) {
       
 27296 				if (!isDefaultPrevented(e) && e.clipboardData) {
       
 27297 					e.preventDefault();
       
 27298 					e.clipboardData.clearData();
       
 27299 					e.clipboardData.setData('text/html', editor.selection.getContent());
       
 27300 					e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
       
 27301 					customDelete(true);
       
 27302 				}
       
 27303 			});
       
 27304 		}
       
 27305 
       
 27306 		/**
       
 27307 		 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
       
 27308 		 *
       
 27309 		 * For example:
       
 27310 		 * <p><b>|</b></p>
       
 27311 		 *
       
 27312 		 * Or:
       
 27313 		 * <h1>|</h1>
       
 27314 		 *
       
 27315 		 * Or:
       
 27316 		 * [<h1></h1>]
       
 27317 		 */
       
 27318 		function emptyEditorWhenDeleting() {
       
 27319 			function serializeRng(rng) {
       
 27320 				var body = dom.create("body");
       
 27321 				var contents = rng.cloneContents();
       
 27322 				body.appendChild(contents);
       
 27323 				return selection.serializer.serialize(body, {format: 'html'});
       
 27324 			}
       
 27325 
       
 27326 			function allContentsSelected(rng) {
       
 27327 				if (!rng.setStart) {
       
 27328 					if (rng.item) {
       
 27329 						return false;
       
 27330 					}
       
 27331 
       
 27332 					var bodyRng = rng.duplicate();
       
 27333 					bodyRng.moveToElementText(editor.getBody());
       
 27334 					return RangeUtils.compareRanges(rng, bodyRng);
       
 27335 				}
       
 27336 
       
 27337 				var selection = serializeRng(rng);
       
 27338 
       
 27339 				var allRng = dom.createRng();
       
 27340 				allRng.selectNode(editor.getBody());
       
 27341 
       
 27342 				var allSelection = serializeRng(allRng);
       
 27343 				return selection === allSelection;
       
 27344 			}
       
 27345 
       
 27346 			editor.on('keydown', function(e) {
       
 27347 				var keyCode = e.keyCode, isCollapsed, body;
       
 27348 
       
 27349 				// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
       
 27350 				if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
       
 27351 					isCollapsed = editor.selection.isCollapsed();
       
 27352 					body = editor.getBody();
       
 27353 
       
 27354 					// Selection is collapsed but the editor isn't empty
       
 27355 					if (isCollapsed && !dom.isEmpty(body)) {
       
 27356 						return;
       
 27357 					}
       
 27358 
       
 27359 					// Selection isn't collapsed but not all the contents is selected
       
 27360 					if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
       
 27361 						return;
       
 27362 					}
       
 27363 
       
 27364 					// Manually empty the editor
       
 27365 					e.preventDefault();
       
 27366 					editor.setContent('');
       
 27367 
       
 27368 					if (body.firstChild && dom.isBlock(body.firstChild)) {
       
 27369 						editor.selection.setCursorLocation(body.firstChild, 0);
       
 27370 					} else {
       
 27371 						editor.selection.setCursorLocation(body, 0);
       
 27372 					}
       
 27373 
       
 27374 					editor.nodeChanged();
       
 27375 				}
       
 27376 			});
       
 27377 		}
       
 27378 
       
 27379 		/**
       
 27380 		 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
       
 27381 		 * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
       
 27382 		 * This selects the whole body so that backspace/delete logic will delete everything
       
 27383 		 */
       
 27384 		function selectAll() {
       
 27385 			editor.shortcuts.add('meta+a', null, 'SelectAll');
       
 27386 		}
       
 27387 
       
 27388 		/**
       
 27389 		 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
       
 27390 		 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
       
 27391 		 *
       
 27392 		 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
       
 27393 		 * you enter a character into the editor.
       
 27394 		 *
       
 27395 		 * It also happens when the first focus in made to the body.
       
 27396 		 *
       
 27397 		 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
       
 27398 		 */
       
 27399 		function inputMethodFocus() {
       
 27400 			if (!editor.settings.content_editable) {
       
 27401 				// Case 1 IME doesn't initialize if you focus the document
       
 27402 				dom.bind(editor.getDoc(), 'focusin', function() {
       
 27403 					selection.setRng(selection.getRng());
       
 27404 				});
       
 27405 
       
 27406 				// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
       
 27407 				// Needs to be both down/up due to weird rendering bug on Chrome Windows
       
 27408 				dom.bind(editor.getDoc(), 'mousedown mouseup', function(e) {
       
 27409 					if (e.target == editor.getDoc().documentElement) {
       
 27410 						editor.getBody().focus();
       
 27411 
       
 27412 						if (e.type == 'mousedown') {
       
 27413 							// Edge case for mousedown, drag select and mousedown again within selection on Chrome Windows to render caret
       
 27414 							selection.placeCaretAt(e.clientX, e.clientY);
       
 27415 						} else {
       
 27416 							selection.setRng(selection.getRng());
       
 27417 						}
       
 27418 					}
       
 27419 				});
       
 27420 			}
       
 27421 		}
       
 27422 
       
 27423 		/**
       
 27424 		 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
       
 27425 		 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
       
 27426 		 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
       
 27427 		 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
       
 27428 		 * browsers.
       
 27429 		 *
       
 27430 		 * It also fixes a bug on Firefox where it's impossible to delete HR elements.
       
 27431 		 */
       
 27432 		function removeHrOnBackspace() {
       
 27433 			editor.on('keydown', function(e) {
       
 27434 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
       
 27435 					// Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow
       
 27436 					if (!editor.getBody().getElementsByTagName('hr').length) {
       
 27437 						return;
       
 27438 					}
       
 27439 
       
 27440 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
       
 27441 						var node = selection.getNode();
       
 27442 						var previousSibling = node.previousSibling;
       
 27443 
       
 27444 						if (node.nodeName == 'HR') {
       
 27445 							dom.remove(node);
       
 27446 							e.preventDefault();
       
 27447 							return;
       
 27448 						}
       
 27449 
       
 27450 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
       
 27451 							dom.remove(previousSibling);
       
 27452 							e.preventDefault();
       
 27453 						}
       
 27454 					}
       
 27455 				}
       
 27456 			});
       
 27457 		}
       
 27458 
       
 27459 		/**
       
 27460 		 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
       
 27461 		 * side it's rectangle.
       
 27462 		 */
       
 27463 		function focusBody() {
       
 27464 			// Fix for a focus bug in FF 3.x where the body element
       
 27465 			// wouldn't get proper focus if the user clicked on the HTML element
       
 27466 			if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
       
 27467 				editor.on('mousedown', function(e) {
       
 27468 					if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
       
 27469 						var body = editor.getBody();
       
 27470 
       
 27471 						// Blur the body it's focused but not correctly focused
       
 27472 						body.blur();
       
 27473 
       
 27474 						// Refocus the body after a little while
       
 27475 						setTimeout(function() {
       
 27476 							body.focus();
       
 27477 						}, 0);
       
 27478 					}
       
 27479 				});
       
 27480 			}
       
 27481 		}
       
 27482 
       
 27483 		/**
       
 27484 		 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
       
 27485 		 * by clicking on them so we need to fake that.
       
 27486 		 */
       
 27487 		function selectControlElements() {
       
 27488 			editor.on('click', function(e) {
       
 27489 				var target = e.target;
       
 27490 
       
 27491 				// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
       
 27492 				// WebKit can't even do simple things like selecting an image
       
 27493 				// Needs to be the setBaseAndExtend or it will fail to select floated images
       
 27494 				if (/^(IMG|HR)$/.test(target.nodeName)) {
       
 27495 					e.preventDefault();
       
 27496 					selection.getSel().setBaseAndExtent(target, 0, target, 1);
       
 27497 					editor.nodeChanged();
       
 27498 				}
       
 27499 
       
 27500 				if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) {
       
 27501 					e.preventDefault();
       
 27502 					selection.select(target);
       
 27503 				}
       
 27504 			});
       
 27505 		}
       
 27506 
       
 27507 		/**
       
 27508 		 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
       
 27509 		 *
       
 27510 		 * Fixes do backspace/delete on this:
       
 27511 		 * <p>bla[ck</p><p style="color:red">r]ed</p>
       
 27512 		 *
       
 27513 		 * Would become:
       
 27514 		 * <p>bla|ed</p>
       
 27515 		 *
       
 27516 		 * Instead of:
       
 27517 		 * <p style="color:red">bla|ed</p>
       
 27518 		 */
       
 27519 		function removeStylesWhenDeletingAcrossBlockElements() {
       
 27520 			function getAttributeApplyFunction() {
       
 27521 				var template = dom.getAttribs(selection.getStart().cloneNode(false));
       
 27522 
       
 27523 				return function() {
       
 27524 					var target = selection.getStart();
       
 27525 
       
 27526 					if (target !== editor.getBody()) {
       
 27527 						dom.setAttrib(target, "style", null);
       
 27528 
       
 27529 						each(template, function(attr) {
       
 27530 							target.setAttributeNode(attr.cloneNode(true));
       
 27531 						});
       
 27532 					}
       
 27533 				};
       
 27534 			}
       
 27535 
       
 27536 			function isSelectionAcrossElements() {
       
 27537 				return !selection.isCollapsed() &&
       
 27538 					dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
       
 27539 			}
       
 27540 
       
 27541 			editor.on('keypress', function(e) {
       
 27542 				var applyAttributes;
       
 27543 
       
 27544 				if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
       
 27545 					applyAttributes = getAttributeApplyFunction();
       
 27546 					editor.getDoc().execCommand('delete', false, null);
       
 27547 					applyAttributes();
       
 27548 					e.preventDefault();
       
 27549 					return false;
       
 27550 				}
       
 27551 			});
       
 27552 
       
 27553 			dom.bind(editor.getDoc(), 'cut', function(e) {
       
 27554 				var applyAttributes;
       
 27555 
       
 27556 				if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
       
 27557 					applyAttributes = getAttributeApplyFunction();
       
 27558 
       
 27559 					setTimeout(function() {
       
 27560 						applyAttributes();
       
 27561 					}, 0);
       
 27562 				}
       
 27563 			});
       
 27564 		}
       
 27565 
       
 27566 		/**
       
 27567 		 * Screen readers on IE needs to have the role application set on the body.
       
 27568 		 */
       
 27569 		function ensureBodyHasRoleApplication() {
       
 27570 			document.body.setAttribute("role", "application");
       
 27571 		}
       
 27572 
       
 27573 		/**
       
 27574 		 * Backspacing into a table behaves differently depending upon browser type.
       
 27575 		 * Therefore, disable Backspace when cursor immediately follows a table.
       
 27576 		 */
       
 27577 		function disableBackspaceIntoATable() {
       
 27578 			editor.on('keydown', function(e) {
       
 27579 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
       
 27580 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
       
 27581 						var previousSibling = selection.getNode().previousSibling;
       
 27582 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
       
 27583 							e.preventDefault();
       
 27584 							return false;
       
 27585 						}
       
 27586 					}
       
 27587 				}
       
 27588 			});
       
 27589 		}
       
 27590 
       
 27591 		/**
       
 27592 		 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
       
 27593 		 * logic adds a \n before the BR so that it will get rendered.
       
 27594 		 */
       
 27595 		function addNewLinesBeforeBrInPre() {
       
 27596 			// IE8+ rendering mode does the right thing with BR in PRE
       
 27597 			if (getDocumentMode() > 7) {
       
 27598 				return;
       
 27599 			}
       
 27600 
       
 27601 			// Enable display: none in area and add a specific class that hides all BR elements in PRE to
       
 27602 			// avoid the caret from getting stuck at the BR elements while pressing the right arrow key
       
 27603 			setEditorCommandState('RespectVisibilityInDesign', true);
       
 27604 			editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
       
 27605 			dom.addClass(editor.getBody(), 'mceHideBrInPre');
       
 27606 
       
 27607 			// Adds a \n before all BR elements in PRE to get them visual
       
 27608 			parser.addNodeFilter('pre', function(nodes) {
       
 27609 				var i = nodes.length, brNodes, j, brElm, sibling;
       
 27610 
       
 27611 				while (i--) {
       
 27612 					brNodes = nodes[i].getAll('br');
       
 27613 					j = brNodes.length;
       
 27614 					while (j--) {
       
 27615 						brElm = brNodes[j];
       
 27616 
       
 27617 						// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
       
 27618 						sibling = brElm.prev;
       
 27619 						if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
       
 27620 							sibling.value += '\n';
       
 27621 						} else {
       
 27622 							brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
       
 27623 						}
       
 27624 					}
       
 27625 				}
       
 27626 			});
       
 27627 
       
 27628 			// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
       
 27629 			serializer.addNodeFilter('pre', function(nodes) {
       
 27630 				var i = nodes.length, brNodes, j, brElm, sibling;
       
 27631 
       
 27632 				while (i--) {
       
 27633 					brNodes = nodes[i].getAll('br');
       
 27634 					j = brNodes.length;
       
 27635 					while (j--) {
       
 27636 						brElm = brNodes[j];
       
 27637 						sibling = brElm.prev;
       
 27638 						if (sibling && sibling.type == 3) {
       
 27639 							sibling.value = sibling.value.replace(/\r?\n$/, '');
       
 27640 						}
       
 27641 					}
       
 27642 				}
       
 27643 			});
       
 27644 		}
       
 27645 
       
 27646 		/**
       
 27647 		 * Moves style width/height to attribute width/height when the user resizes an image on IE.
       
 27648 		 */
       
 27649 		function removePreSerializedStylesWhenSelectingControls() {
       
 27650 			dom.bind(editor.getBody(), 'mouseup', function() {
       
 27651 				var value, node = selection.getNode();
       
 27652 
       
 27653 				// Moved styles to attributes on IMG eements
       
 27654 				if (node.nodeName == 'IMG') {
       
 27655 					// Convert style width to width attribute
       
 27656 					if ((value = dom.getStyle(node, 'width'))) {
       
 27657 						dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
       
 27658 						dom.setStyle(node, 'width', '');
       
 27659 					}
       
 27660 
       
 27661 					// Convert style height to height attribute
       
 27662 					if ((value = dom.getStyle(node, 'height'))) {
       
 27663 						dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
       
 27664 						dom.setStyle(node, 'height', '');
       
 27665 					}
       
 27666 				}
       
 27667 			});
       
 27668 		}
       
 27669 
       
 27670 		/**
       
 27671 		 * Removes a blockquote when backspace is pressed at the beginning of it.
       
 27672 		 *
       
 27673 		 * For example:
       
 27674 		 * <blockquote><p>|x</p></blockquote>
       
 27675 		 *
       
 27676 		 * Becomes:
       
 27677 		 * <p>|x</p>
       
 27678 		 */
       
 27679 		function removeBlockQuoteOnBackSpace() {
       
 27680 			// Add block quote deletion handler
       
 27681 			editor.on('keydown', function(e) {
       
 27682 				var rng, container, offset, root, parent;
       
 27683 
       
 27684 				if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
       
 27685 					return;
       
 27686 				}
       
 27687 
       
 27688 				rng = selection.getRng();
       
 27689 				container = rng.startContainer;
       
 27690 				offset = rng.startOffset;
       
 27691 				root = dom.getRoot();
       
 27692 				parent = container;
       
 27693 
       
 27694 				if (!rng.collapsed || offset !== 0) {
       
 27695 					return;
       
 27696 				}
       
 27697 
       
 27698 				while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
       
 27699 					parent = parent.parentNode;
       
 27700 				}
       
 27701 
       
 27702 				// Is the cursor at the beginning of a blockquote?
       
 27703 				if (parent.tagName === 'BLOCKQUOTE') {
       
 27704 					// Remove the blockquote
       
 27705 					editor.formatter.toggle('blockquote', null, parent);
       
 27706 
       
 27707 					// Move the caret to the beginning of container
       
 27708 					rng = dom.createRng();
       
 27709 					rng.setStart(container, 0);
       
 27710 					rng.setEnd(container, 0);
       
 27711 					selection.setRng(rng);
       
 27712 				}
       
 27713 			});
       
 27714 		}
       
 27715 
       
 27716 		/**
       
 27717 		 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
       
 27718 		 */
       
 27719 		function setGeckoEditingOptions() {
       
 27720 			function setOpts() {
       
 27721 				editor._refreshContentEditable();
       
 27722 
       
 27723 				setEditorCommandState("StyleWithCSS", false);
       
 27724 				setEditorCommandState("enableInlineTableEditing", false);
       
 27725 
       
 27726 				if (!settings.object_resizing) {
       
 27727 					setEditorCommandState("enableObjectResizing", false);
       
 27728 				}
       
 27729 			}
       
 27730 
       
 27731 			if (!settings.readonly) {
       
 27732 				editor.on('BeforeExecCommand MouseDown', setOpts);
       
 27733 			}
       
 27734 		}
       
 27735 
       
 27736 		/**
       
 27737 		 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
       
 27738 		 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
       
 27739 		 *
       
 27740 		 * For example this:
       
 27741 		 * <p><b><a href="#">x</a></b></p>
       
 27742 		 *
       
 27743 		 * Becomes this:
       
 27744 		 * <p><b><a href="#">x</a></b><br></p>
       
 27745 		 */
       
 27746 		function addBrAfterLastLinks() {
       
 27747 			function fixLinks() {
       
 27748 				each(dom.select('a'), function(node) {
       
 27749 					var parentNode = node.parentNode, root = dom.getRoot();
       
 27750 
       
 27751 					if (parentNode.lastChild === node) {
       
 27752 						while (parentNode && !dom.isBlock(parentNode)) {
       
 27753 							if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
       
 27754 								return;
       
 27755 							}
       
 27756 
       
 27757 							parentNode = parentNode.parentNode;
       
 27758 						}
       
 27759 
       
 27760 						dom.add(parentNode, 'br', {'data-mce-bogus': 1});
       
 27761 					}
       
 27762 				});
       
 27763 			}
       
 27764 
       
 27765 			editor.on('SetContent ExecCommand', function(e) {
       
 27766 				if (e.type == "setcontent" || e.command === 'mceInsertLink') {
       
 27767 					fixLinks();
       
 27768 				}
       
 27769 			});
       
 27770 		}
       
 27771 
       
 27772 		/**
       
 27773 		 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
       
 27774 		 * default we want to change that behavior.
       
 27775 		 */
       
 27776 		function setDefaultBlockType() {
       
 27777 			if (settings.forced_root_block) {
       
 27778 				editor.on('init', function() {
       
 27779 					setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
       
 27780 				});
       
 27781 			}
       
 27782 		}
       
 27783 
       
 27784 		/**
       
 27785 		 * Removes ghost selections from images/tables on Gecko.
       
 27786 		 */
       
 27787 		function removeGhostSelection() {
       
 27788 			editor.on('Undo Redo SetContent', function(e) {
       
 27789 				if (!e.initial) {
       
 27790 					editor.execCommand('mceRepaint');
       
 27791 				}
       
 27792 			});
       
 27793 		}
       
 27794 
       
 27795 		/**
       
 27796 		 * Deletes the selected image on IE instead of navigating to previous page.
       
 27797 		 */
       
 27798 		function deleteControlItemOnBackSpace() {
       
 27799 			editor.on('keydown', function(e) {
       
 27800 				var rng;
       
 27801 
       
 27802 				if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
       
 27803 					rng = editor.getDoc().selection.createRange();
       
 27804 					if (rng && rng.item) {
       
 27805 						e.preventDefault();
       
 27806 						editor.undoManager.beforeChange();
       
 27807 						dom.remove(rng.item(0));
       
 27808 						editor.undoManager.add();
       
 27809 					}
       
 27810 				}
       
 27811 			});
       
 27812 		}
       
 27813 
       
 27814 		/**
       
 27815 		 * IE10 doesn't properly render block elements with the right height until you add contents to them.
       
 27816 		 * This fixes that by adding a padding-right to all empty text block elements.
       
 27817 		 * See: https://connect.microsoft.com/IE/feedback/details/743881
       
 27818 		 */
       
 27819 		function renderEmptyBlocksFix() {
       
 27820 			var emptyBlocksCSS;
       
 27821 
       
 27822 			// IE10+
       
 27823 			if (getDocumentMode() >= 10) {
       
 27824 				emptyBlocksCSS = '';
       
 27825 				each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
       
 27826 					emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
       
 27827 				});
       
 27828 
       
 27829 				editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
       
 27830 			}
       
 27831 		}
       
 27832 
       
 27833 		/**
       
 27834 		 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
       
 27835 		 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
       
 27836 		 */
       
 27837 		function keepNoScriptContents() {
       
 27838 			if (getDocumentMode() < 9) {
       
 27839 				parser.addNodeFilter('noscript', function(nodes) {
       
 27840 					var i = nodes.length, node, textNode;
       
 27841 
       
 27842 					while (i--) {
       
 27843 						node = nodes[i];
       
 27844 						textNode = node.firstChild;
       
 27845 
       
 27846 						if (textNode) {
       
 27847 							node.attr('data-mce-innertext', textNode.value);
       
 27848 						}
       
 27849 					}
       
 27850 				});
       
 27851 
       
 27852 				serializer.addNodeFilter('noscript', function(nodes) {
       
 27853 					var i = nodes.length, node, textNode, value;
       
 27854 
       
 27855 					while (i--) {
       
 27856 						node = nodes[i];
       
 27857 						textNode = nodes[i].firstChild;
       
 27858 
       
 27859 						if (textNode) {
       
 27860 							textNode.value = Entities.decode(textNode.value);
       
 27861 						} else {
       
 27862 							// Old IE can't retain noscript value so an attribute is used to store it
       
 27863 							value = node.attributes.map['data-mce-innertext'];
       
 27864 							if (value) {
       
 27865 								node.attr('data-mce-innertext', null);
       
 27866 								textNode = new Node('#text', 3);
       
 27867 								textNode.value = value;
       
 27868 								textNode.raw = true;
       
 27869 								node.append(textNode);
       
 27870 							}
       
 27871 						}
       
 27872 					}
       
 27873 				});
       
 27874 			}
       
 27875 		}
       
 27876 
       
 27877 		/**
       
 27878 		 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
       
 27879 		 */
       
 27880 		function fixCaretSelectionOfDocumentElementOnIe() {
       
 27881 			var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
       
 27882 
       
 27883 			// Return range from point or null if it failed
       
 27884 			function rngFromPoint(x, y) {
       
 27885 				var rng = body.createTextRange();
       
 27886 
       
 27887 				try {
       
 27888 					rng.moveToPoint(x, y);
       
 27889 				} catch (ex) {
       
 27890 					// IE sometimes throws and exception, so lets just ignore it
       
 27891 					rng = null;
       
 27892 				}
       
 27893 
       
 27894 				return rng;
       
 27895 			}
       
 27896 
       
 27897 			// Fires while the selection is changing
       
 27898 			function selectionChange(e) {
       
 27899 				var pointRng;
       
 27900 
       
 27901 				// Check if the button is down or not
       
 27902 				if (e.button) {
       
 27903 					// Create range from mouse position
       
 27904 					pointRng = rngFromPoint(e.x, e.y);
       
 27905 
       
 27906 					if (pointRng) {
       
 27907 						// Check if pointRange is before/after selection then change the endPoint
       
 27908 						if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
       
 27909 							pointRng.setEndPoint('StartToStart', startRng);
       
 27910 						} else {
       
 27911 							pointRng.setEndPoint('EndToEnd', startRng);
       
 27912 						}
       
 27913 
       
 27914 						pointRng.select();
       
 27915 					}
       
 27916 				} else {
       
 27917 					endSelection();
       
 27918 				}
       
 27919 			}
       
 27920 
       
 27921 			// Removes listeners
       
 27922 			function endSelection() {
       
 27923 				var rng = doc.selection.createRange();
       
 27924 
       
 27925 				// If the range is collapsed then use the last start range
       
 27926 				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
       
 27927 					startRng.select();
       
 27928 				}
       
 27929 
       
 27930 				dom.unbind(doc, 'mouseup', endSelection);
       
 27931 				dom.unbind(doc, 'mousemove', selectionChange);
       
 27932 				startRng = started = 0;
       
 27933 			}
       
 27934 
       
 27935 			// Make HTML element unselectable since we are going to handle selection by hand
       
 27936 			doc.documentElement.unselectable = true;
       
 27937 
       
 27938 			// Detect when user selects outside BODY
       
 27939 			dom.bind(doc, 'mousedown contextmenu', function(e) {
       
 27940 				if (e.target.nodeName === 'HTML') {
       
 27941 					if (started) {
       
 27942 						endSelection();
       
 27943 					}
       
 27944 
       
 27945 					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
       
 27946 					htmlElm = doc.documentElement;
       
 27947 					if (htmlElm.scrollHeight > htmlElm.clientHeight) {
       
 27948 						return;
       
 27949 					}
       
 27950 
       
 27951 					started = 1;
       
 27952 					// Setup start position
       
 27953 					startRng = rngFromPoint(e.x, e.y);
       
 27954 					if (startRng) {
       
 27955 						// Listen for selection change events
       
 27956 						dom.bind(doc, 'mouseup', endSelection);
       
 27957 						dom.bind(doc, 'mousemove', selectionChange);
       
 27958 
       
 27959 						dom.getRoot().focus();
       
 27960 						startRng.select();
       
 27961 					}
       
 27962 				}
       
 27963 			});
       
 27964 		}
       
 27965 
       
 27966 		/**
       
 27967 		 * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
       
 27968 		 * this fix will lean the caret right into the closest inline element.
       
 27969 		 */
       
 27970 		function normalizeSelection() {
       
 27971 			// Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
       
 27972 			editor.on('keyup focusin mouseup', function(e) {
       
 27973 				if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
       
 27974 					selection.normalize();
       
 27975 				}
       
 27976 			}, true);
       
 27977 		}
       
 27978 
       
 27979 		/**
       
 27980 		 * Forces Gecko to render a broken image icon if it fails to load an image.
       
 27981 		 */
       
 27982 		function showBrokenImageIcon() {
       
 27983 			editor.contentStyles.push(
       
 27984 				'img:-moz-broken {' +
       
 27985 					'-moz-force-broken-image-icon:1;' +
       
 27986 					'min-width:24px;' +
       
 27987 					'min-height:24px' +
       
 27988 				'}'
       
 27989 			);
       
 27990 		}
       
 27991 
       
 27992 		/**
       
 27993 		 * iOS has a bug where it's impossible to type if the document has a touchstart event
       
 27994 		 * bound and the user touches the document while having the on screen keyboard visible.
       
 27995 		 *
       
 27996 		 * The touch event moves the focus to the parent document while having the caret inside the iframe
       
 27997 		 * this fix moves the focus back into the iframe document.
       
 27998 		 */
       
 27999 		function restoreFocusOnKeyDown() {
       
 28000 			if (!editor.inline) {
       
 28001 				editor.on('keydown', function() {
       
 28002 					if (document.activeElement == document.body) {
       
 28003 						editor.getWin().focus();
       
 28004 					}
       
 28005 				});
       
 28006 			}
       
 28007 		}
       
 28008 
       
 28009 		/**
       
 28010 		 * IE 11 has an annoying issue where you can't move focus into the editor
       
 28011 		 * by clicking on the white area HTML element. We used to be able to to fix this with
       
 28012 		 * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
       
 28013 		 * object it's not possible anymore. So we need to hack in a ungly CSS to force the
       
 28014 		 * body to be at least 150px. If the user clicks the HTML element out side this 150px region
       
 28015 		 * we simply move the focus into the first paragraph. Not ideal since you loose the
       
 28016 		 * positioning of the caret but goot enough for most cases.
       
 28017 		 */
       
 28018 		function bodyHeight() {
       
 28019 			if (!editor.inline) {
       
 28020 				editor.contentStyles.push('body {min-height: 150px}');
       
 28021 				editor.on('click', function(e) {
       
 28022 					if (e.target.nodeName == 'HTML') {
       
 28023 						var rng;
       
 28024 
       
 28025 						// Need to store away non collapsed ranges since the focus call will mess that up see #7382
       
 28026 						rng = editor.selection.getRng();
       
 28027 						editor.getBody().focus();
       
 28028 						editor.selection.setRng(rng);
       
 28029 						editor.selection.normalize();
       
 28030 						editor.nodeChanged();
       
 28031 					}
       
 28032 				});
       
 28033 			}
       
 28034 		}
       
 28035 
       
 28036 		/**
       
 28037 		 * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
       
 28038 		 * You might then loose all your work so we need to block that behavior and replace it with our own.
       
 28039 		 */
       
 28040 		function blockCmdArrowNavigation() {
       
 28041 			if (Env.mac) {
       
 28042 				editor.on('keydown', function(e) {
       
 28043 					if (VK.metaKeyPressed(e) && (e.keyCode == 37 || e.keyCode == 39)) {
       
 28044 						e.preventDefault();
       
 28045 						editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'lineboundary');
       
 28046 					}
       
 28047 				});
       
 28048 			}
       
 28049 		}
       
 28050 
       
 28051 		/**
       
 28052 		 * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin.
       
 28053 		 */
       
 28054 		function disableAutoUrlDetect() {
       
 28055 			setEditorCommandState("AutoUrlDetect", false);
       
 28056 		}
       
 28057 
       
 28058 		/**
       
 28059 		 * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
       
 28060 		 * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
       
 28061 		 * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
       
 28062 		 * but not as the lastChild of the body. However is we add a BR element to the body then remove it
       
 28063 		 * it doesn't seem to add these BR elements makes sence right?!
       
 28064 		 *
       
 28065 		 * Example of what happens: <body>text</body> becomes <body>text<br><br></body>
       
 28066 		 */
       
 28067 		function doubleTrailingBrElements() {
       
 28068 			if (!editor.inline) {
       
 28069 				editor.on('focus blur beforegetcontent', function() {
       
 28070 					var br = editor.dom.create('br');
       
 28071 					editor.getBody().appendChild(br);
       
 28072 					br.parentNode.removeChild(br);
       
 28073 				}, true);
       
 28074 			}
       
 28075 		}
       
 28076 
       
 28077 		/**
       
 28078 		 * iOS 7.1 introduced two new bugs:
       
 28079 		 * 1) It's possible to open links within a contentEditable area by clicking on them.
       
 28080 		 * 2) If you hold down the finger it will display the link/image touch callout menu.
       
 28081 		 */
       
 28082 		function tapLinksAndImages() {
       
 28083 			editor.on('click', function(e) {
       
 28084 				var elm = e.target;
       
 28085 
       
 28086 				do {
       
 28087 					if (elm.tagName === 'A') {
       
 28088 						e.preventDefault();
       
 28089 						return;
       
 28090 					}
       
 28091 				} while ((elm = elm.parentNode));
       
 28092 			});
       
 28093 
       
 28094 			editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
       
 28095 		}
       
 28096 
       
 28097 		/**
       
 28098 		 * iOS Safari and possible other browsers have a bug where it won't fire
       
 28099 		 * a click event when a contentEditable is focused. This function fakes click events
       
 28100 		 * by using touchstart/touchend and measuring the time and distance travelled.
       
 28101 		 */
       
 28102 		function touchClickEvent() {
       
 28103 			editor.on('touchstart', function(e) {
       
 28104 				var elm, time, startTouch, changedTouches;
       
 28105 
       
 28106 				elm = e.target;
       
 28107 				time = new Date().getTime();
       
 28108 				changedTouches = e.changedTouches;
       
 28109 
       
 28110 				if (!changedTouches || changedTouches.length > 1) {
       
 28111 					return;
       
 28112 				}
       
 28113 
       
 28114 				startTouch = changedTouches[0];
       
 28115 
       
 28116 				editor.once('touchend', function(e) {
       
 28117 					var endTouch = e.changedTouches[0], args;
       
 28118 
       
 28119 					if (new Date().getTime() - time > 500) {
       
 28120 						return;
       
 28121 					}
       
 28122 
       
 28123 					if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) {
       
 28124 						return;
       
 28125 					}
       
 28126 
       
 28127 					if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) {
       
 28128 						return;
       
 28129 					}
       
 28130 
       
 28131 					args = {
       
 28132 						target: elm
       
 28133 					};
       
 28134 
       
 28135 					each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) {
       
 28136 						args[key] = endTouch[key];
       
 28137 					});
       
 28138 
       
 28139 					args = editor.fire('click', args);
       
 28140 
       
 28141 					if (!args.isDefaultPrevented()) {
       
 28142 						// iOS WebKit can't place the caret properly once
       
 28143 						// you bind touch events so we need to do this manually
       
 28144 						// TODO: Expand to the closest word? Touble tap still works.
       
 28145 						editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY);
       
 28146 						editor.nodeChanged();
       
 28147 					}
       
 28148 				});
       
 28149 			});
       
 28150 		}
       
 28151 
       
 28152 		/**
       
 28153 		 * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
       
 28154 		 * For example this: <form><button></form>
       
 28155 		 */
       
 28156 		function blockFormSubmitInsideEditor() {
       
 28157 			editor.on('init', function() {
       
 28158 				editor.dom.bind(editor.getBody(), 'submit', function(e) {
       
 28159 					e.preventDefault();
       
 28160 				});
       
 28161 			});
       
 28162 		}
       
 28163 
       
 28164 		/**
       
 28165 		 * Sometimes WebKit/Blink generates BR elements with the Apple-interchange-newline class.
       
 28166 		 *
       
 28167 		 * Scenario:
       
 28168 		 *  1) Create a table 2x2.
       
 28169 		 *  2) Select and copy cells A2-B2.
       
 28170 		 *  3) Paste and it will add BR element to table cell.
       
 28171 		 */
       
 28172 		function removeAppleInterchangeBrs() {
       
 28173 			parser.addNodeFilter('br', function(nodes) {
       
 28174 				var i = nodes.length;
       
 28175 
       
 28176 				while (i--) {
       
 28177 					if (nodes[i].attr('class') == 'Apple-interchange-newline') {
       
 28178 						nodes[i].remove();
       
 28179 					}
       
 28180 				}
       
 28181 			});
       
 28182 		}
       
 28183 
       
 28184 		/**
       
 28185 		 * IE cannot set custom contentType's on drag events, and also does not properly drag/drop between
       
 28186 		 * editors. This uses a special data:text/mce-internal URL to pass data when drag/drop between editors.
       
 28187 		 */
       
 28188 		function ieInternalDragAndDrop() {
       
 28189 			editor.on('dragstart', function(e) {
       
 28190 				setMceInteralContent(e);
       
 28191 			});
       
 28192 
       
 28193 			editor.on('drop', function(e) {
       
 28194 				if (!isDefaultPrevented(e)) {
       
 28195 					var internalContent = getMceInternalContent(e);
       
 28196 					if (internalContent) {
       
 28197 						e.preventDefault();
       
 28198 
       
 28199 						var rng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, editor.getDoc());
       
 28200 						selection.setRng(rng);
       
 28201 						insertClipboardContents(internalContent);
       
 28202 					}
       
 28203 				}
       
 28204 			});
       
 28205 		}
       
 28206 
       
 28207 		// All browsers
       
 28208 		removeBlockQuoteOnBackSpace();
       
 28209 		emptyEditorWhenDeleting();
       
 28210 		normalizeSelection();
       
 28211 
       
 28212 		// WebKit
       
 28213 		if (isWebKit) {
       
 28214 			cleanupStylesWhenDeleting();
       
 28215 			inputMethodFocus();
       
 28216 			selectControlElements();
       
 28217 			setDefaultBlockType();
       
 28218 			blockFormSubmitInsideEditor();
       
 28219 			disableBackspaceIntoATable();
       
 28220 			removeAppleInterchangeBrs();
       
 28221 			touchClickEvent();
       
 28222 
       
 28223 			// iOS
       
 28224 			if (Env.iOS) {
       
 28225 				restoreFocusOnKeyDown();
       
 28226 				bodyHeight();
       
 28227 				tapLinksAndImages();
       
 28228 			} else {
       
 28229 				selectAll();
       
 28230 			}
       
 28231 		}
       
 28232 
       
 28233 		// IE
       
 28234 		if (isIE && Env.ie < 11) {
       
 28235 			removeHrOnBackspace();
       
 28236 			ensureBodyHasRoleApplication();
       
 28237 			addNewLinesBeforeBrInPre();
       
 28238 			removePreSerializedStylesWhenSelectingControls();
       
 28239 			deleteControlItemOnBackSpace();
       
 28240 			renderEmptyBlocksFix();
       
 28241 			keepNoScriptContents();
       
 28242 			fixCaretSelectionOfDocumentElementOnIe();
       
 28243 		}
       
 28244 
       
 28245 		if (Env.ie >= 11) {
       
 28246 			bodyHeight();
       
 28247 			doubleTrailingBrElements();
       
 28248 			disableBackspaceIntoATable();
       
 28249 		}
       
 28250 
       
 28251 		if (Env.ie) {
       
 28252 			selectAll();
       
 28253 			disableAutoUrlDetect();
       
 28254 			ieInternalDragAndDrop();
       
 28255 		}
       
 28256 
       
 28257 		// Gecko
       
 28258 		if (isGecko) {
       
 28259 			removeHrOnBackspace();
       
 28260 			focusBody();
       
 28261 			removeStylesWhenDeletingAcrossBlockElements();
       
 28262 			setGeckoEditingOptions();
       
 28263 			addBrAfterLastLinks();
       
 28264 			removeGhostSelection();
       
 28265 			showBrokenImageIcon();
       
 28266 			blockCmdArrowNavigation();
       
 28267 			disableBackspaceIntoATable();
       
 28268 		}
       
 28269 	};
       
 28270 });
       
 28271 
       
 28272 // Included from: js/tinymce/classes/util/Observable.js
       
 28273 
       
 28274 /**
       
 28275  * Observable.js
       
 28276  *
       
 28277  * Copyright, Moxiecode Systems AB
       
 28278  * Released under LGPL License.
       
 28279  *
       
 28280  * License: http://www.tinymce.com/license
       
 28281  * Contributing: http://www.tinymce.com/contributing
       
 28282  */
       
 28283 
       
 28284 /**
       
 28285  * This mixin will add event binding logic to classes.
       
 28286  *
       
 28287  * @mixin tinymce.util.Observable
       
 28288  */
       
 28289 define("tinymce/util/Observable", [
       
 28290 	"tinymce/util/EventDispatcher"
       
 28291 ], function(EventDispatcher) {
       
 28292 	function getEventDispatcher(obj) {
       
 28293 		if (!obj._eventDispatcher) {
       
 28294 			obj._eventDispatcher = new EventDispatcher({
       
 28295 				scope: obj,
       
 28296 				toggleEvent: function(name, state) {
       
 28297 					if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {
       
 28298 						obj.toggleNativeEvent(name, state);
       
 28299 					}
       
 28300 				}
       
 28301 			});
       
 28302 		}
       
 28303 
       
 28304 		return obj._eventDispatcher;
       
 28305 	}
       
 28306 
       
 28307 	return {
       
 28308 		/**
       
 28309 		 * Fires the specified event by name.
       
 28310 		 *
       
 28311 		 * @method fire
       
 28312 		 * @param {String} name Name of the event to fire.
       
 28313 		 * @param {Object?} args Event arguments.
       
 28314 		 * @param {Boolean?} bubble True/false if the event is to be bubbled.
       
 28315 		 * @return {Object} Event args instance passed in.
       
 28316 		 * @example
       
 28317 		 * instance.fire('event', {...});
       
 28318 		 */
       
 28319 		fire: function(name, args, bubble) {
       
 28320 			var self = this;
       
 28321 
       
 28322 			// Prevent all events except the remove event after the instance has been removed
       
 28323 			if (self.removed && name !== "remove") {
       
 28324 				return args;
       
 28325 			}
       
 28326 
       
 28327 			args = getEventDispatcher(self).fire(name, args, bubble);
       
 28328 
       
 28329 			// Bubble event up to parents
       
 28330 			if (bubble !== false && self.parent) {
       
 28331 				var parent = self.parent();
       
 28332 				while (parent && !args.isPropagationStopped()) {
       
 28333 					parent.fire(name, args, false);
       
 28334 					parent = parent.parent();
       
 28335 				}
       
 28336 			}
       
 28337 
       
 28338 			return args;
       
 28339 		},
       
 28340 
       
 28341 		/**
       
 28342 		 * Binds an event listener to a specific event by name.
       
 28343 		 *
       
 28344 		 * @method on
       
 28345 		 * @param {String} name Event name or space separated list of events to bind.
       
 28346 		 * @param {callback} callback Callback to be executed when the event occurs.
       
 28347 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
       
 28348 		 * @return {Object} Current class instance.
       
 28349 		 * @example
       
 28350 		 * instance.on('event', function(e) {
       
 28351 		 *     // Callback logic
       
 28352 		 * });
       
 28353 		 */
       
 28354 		on: function(name, callback, prepend) {
       
 28355 			return getEventDispatcher(this).on(name, callback, prepend);
       
 28356 		},
       
 28357 
       
 28358 		/**
       
 28359 		 * Unbinds an event listener to a specific event by name.
       
 28360 		 *
       
 28361 		 * @method off
       
 28362 		 * @param {String?} name Name of the event to unbind.
       
 28363 		 * @param {callback?} callback Callback to unbind.
       
 28364 		 * @return {Object} Current class instance.
       
 28365 		 * @example
       
 28366 		 * // Unbind specific callback
       
 28367 		 * instance.off('event', handler);
       
 28368 		 *
       
 28369 		 * // Unbind all listeners by name
       
 28370 		 * instance.off('event');
       
 28371 		 *
       
 28372 		 * // Unbind all events
       
 28373 		 * instance.off();
       
 28374 		 */
       
 28375 		off: function(name, callback) {
       
 28376 			return getEventDispatcher(this).off(name, callback);
       
 28377 		},
       
 28378 
       
 28379 		/**
       
 28380 		 * Bind the event callback and once it fires the callback is removed.
       
 28381 		 *
       
 28382 		 * @method once
       
 28383 		 * @param {String} name Name of the event to bind.
       
 28384 		 * @param {callback} callback Callback to bind only once.
       
 28385 		 * @return {Object} Current class instance.
       
 28386 		 */
       
 28387 		once: function(name, callback) {
       
 28388 			return getEventDispatcher(this).once(name, callback);
       
 28389 		},
       
 28390 
       
 28391 		/**
       
 28392 		 * Returns true/false if the object has a event of the specified name.
       
 28393 		 *
       
 28394 		 * @method hasEventListeners
       
 28395 		 * @param {String} name Name of the event to check for.
       
 28396 		 * @return {Boolean} true/false if the event exists or not.
       
 28397 		 */
       
 28398 		hasEventListeners: function(name) {
       
 28399 			return getEventDispatcher(this).has(name);
       
 28400 		}
       
 28401 	};
       
 28402 });
       
 28403 
       
 28404 // Included from: js/tinymce/classes/EditorObservable.js
       
 28405 
       
 28406 /**
       
 28407  * EditorObservable.js
       
 28408  *
       
 28409  * Copyright, Moxiecode Systems AB
       
 28410  * Released under LGPL License.
       
 28411  *
       
 28412  * License: http://www.tinymce.com/license
       
 28413  * Contributing: http://www.tinymce.com/contributing
       
 28414  */
       
 28415 
       
 28416 /**
       
 28417  * This mixin contains the event logic for the tinymce.Editor class.
       
 28418  *
       
 28419  * @mixin tinymce.EditorObservable
       
 28420  * @extends tinymce.util.Observable
       
 28421  */
       
 28422 define("tinymce/EditorObservable", [
       
 28423 	"tinymce/util/Observable",
       
 28424 	"tinymce/dom/DOMUtils",
       
 28425 	"tinymce/util/Tools"
       
 28426 ], function(Observable, DOMUtils, Tools) {
       
 28427 	var DOM = DOMUtils.DOM, customEventRootDelegates;
       
 28428 
       
 28429 	/**
       
 28430 	 * Returns the event target so for the specified event. Some events fire
       
 28431 	 * only on document, some fire on documentElement etc. This also handles the
       
 28432 	 * custom event root setting where it returns that element instead of the body.
       
 28433 	 *
       
 28434 	 * @private
       
 28435 	 * @param {tinymce.Editor} editor Editor instance to get event target from.
       
 28436 	 * @param {String} eventName Name of the event for example "click".
       
 28437 	 * @return {Element/Document} HTML Element or document target to bind on.
       
 28438 	 */
       
 28439 	function getEventTarget(editor, eventName) {
       
 28440 		if (eventName == 'selectionchange') {
       
 28441 			return editor.getDoc();
       
 28442 		}
       
 28443 
       
 28444 		// Need to bind mousedown/mouseup etc to document not body in iframe mode
       
 28445 		// Since the user might click on the HTML element not the BODY
       
 28446 		if (!editor.inline && /^mouse|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
       
 28447 			return editor.getDoc().documentElement;
       
 28448 		}
       
 28449 
       
 28450 		// Bind to event root instead of body if it's defined
       
 28451 		if (editor.settings.event_root) {
       
 28452 			if (!editor.eventRoot) {
       
 28453 				editor.eventRoot = DOM.select(editor.settings.event_root)[0];
       
 28454 			}
       
 28455 
       
 28456 			return editor.eventRoot;
       
 28457 		}
       
 28458 
       
 28459 		return editor.getBody();
       
 28460 	}
       
 28461 
       
 28462 	/**
       
 28463 	 * Binds a event delegate for the specified name this delegate will fire
       
 28464 	 * the event to the editor dispatcher.
       
 28465 	 *
       
 28466 	 * @private
       
 28467 	 * @param {tinymce.Editor} editor Editor instance to get event target from.
       
 28468 	 * @param {String} eventName Name of the event for example "click".
       
 28469 	 */
       
 28470 	function bindEventDelegate(editor, eventName) {
       
 28471 		var eventRootElm = getEventTarget(editor, eventName), delegate;
       
 28472 
       
 28473 		if (!editor.delegates) {
       
 28474 			editor.delegates = {};
       
 28475 		}
       
 28476 
       
 28477 		if (editor.delegates[eventName]) {
       
 28478 			return;
       
 28479 		}
       
 28480 
       
 28481 		if (editor.settings.event_root) {
       
 28482 			if (!customEventRootDelegates) {
       
 28483 				customEventRootDelegates = {};
       
 28484 				editor.editorManager.on('removeEditor', function() {
       
 28485 					var name;
       
 28486 
       
 28487 					if (!editor.editorManager.activeEditor) {
       
 28488 						if (customEventRootDelegates) {
       
 28489 							for (name in customEventRootDelegates) {
       
 28490 								editor.dom.unbind(getEventTarget(editor, name));
       
 28491 							}
       
 28492 
       
 28493 							customEventRootDelegates = null;
       
 28494 						}
       
 28495 					}
       
 28496 				});
       
 28497 			}
       
 28498 
       
 28499 			if (customEventRootDelegates[eventName]) {
       
 28500 				return;
       
 28501 			}
       
 28502 
       
 28503 			delegate = function(e) {
       
 28504 				var target = e.target, editors = editor.editorManager.editors, i = editors.length;
       
 28505 
       
 28506 				while (i--) {
       
 28507 					var body = editors[i].getBody();
       
 28508 
       
 28509 					if (body === target || DOM.isChildOf(target, body)) {
       
 28510 						if (!editors[i].hidden) {
       
 28511 							editors[i].fire(eventName, e);
       
 28512 						}
       
 28513 					}
       
 28514 				}
       
 28515 			};
       
 28516 
       
 28517 			customEventRootDelegates[eventName] = delegate;
       
 28518 			DOM.bind(eventRootElm, eventName, delegate);
       
 28519 		} else {
       
 28520 			delegate = function(e) {
       
 28521 				if (!editor.hidden) {
       
 28522 					editor.fire(eventName, e);
       
 28523 				}
       
 28524 			};
       
 28525 
       
 28526 			DOM.bind(eventRootElm, eventName, delegate);
       
 28527 			editor.delegates[eventName] = delegate;
       
 28528 		}
       
 28529 	}
       
 28530 
       
 28531 	var EditorObservable = {
       
 28532 		/**
       
 28533 		 * Bind any pending event delegates. This gets executed after the target body/document is created.
       
 28534 		 *
       
 28535 		 * @private
       
 28536 		 */
       
 28537 		bindPendingEventDelegates: function() {
       
 28538 			var self = this;
       
 28539 
       
 28540 			Tools.each(self._pendingNativeEvents, function(name) {
       
 28541 				bindEventDelegate(self, name);
       
 28542 			});
       
 28543 		},
       
 28544 
       
 28545 		/**
       
 28546 		 * Toggles a native event on/off this is called by the EventDispatcher when
       
 28547 		 * the first native event handler is added and when the last native event handler is removed.
       
 28548 		 *
       
 28549 		 * @private
       
 28550 		 */
       
 28551 		toggleNativeEvent: function(name, state) {
       
 28552 			var self = this;
       
 28553 
       
 28554 			if (self.settings.readonly) {
       
 28555 				return;
       
 28556 			}
       
 28557 
       
 28558 			// Never bind focus/blur since the FocusManager fakes those
       
 28559 			if (name == "focus" || name == "blur") {
       
 28560 				return;
       
 28561 			}
       
 28562 
       
 28563 			if (state) {
       
 28564 				if (self.initialized) {
       
 28565 					bindEventDelegate(self, name);
       
 28566 				} else {
       
 28567 					if (!self._pendingNativeEvents) {
       
 28568 						self._pendingNativeEvents = [name];
       
 28569 					} else {
       
 28570 						self._pendingNativeEvents.push(name);
       
 28571 					}
       
 28572 				}
       
 28573 			} else if (self.initialized) {
       
 28574 				self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
       
 28575 				delete self.delegates[name];
       
 28576 			}
       
 28577 		},
       
 28578 
       
 28579 		/**
       
 28580 		 * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
       
 28581 		 *
       
 28582 		 * @private
       
 28583 		 */
       
 28584 		unbindAllNativeEvents: function() {
       
 28585 			var self = this, name;
       
 28586 
       
 28587 			if (self.delegates) {
       
 28588 				for (name in self.delegates) {
       
 28589 					self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
       
 28590 				}
       
 28591 
       
 28592 				delete self.delegates;
       
 28593 			}
       
 28594 
       
 28595 			if (!self.inline) {
       
 28596 				self.getBody().onload = null;
       
 28597 				self.dom.unbind(self.getWin());
       
 28598 				self.dom.unbind(self.getDoc());
       
 28599 			}
       
 28600 
       
 28601 			self.dom.unbind(self.getBody());
       
 28602 			self.dom.unbind(self.getContainer());
       
 28603 		}
       
 28604 	};
       
 28605 
       
 28606 	EditorObservable = Tools.extend({}, Observable, EditorObservable);
       
 28607 
       
 28608 	return EditorObservable;
       
 28609 });
       
 28610 
       
 28611 // Included from: js/tinymce/classes/Shortcuts.js
       
 28612 
       
 28613 /**
       
 28614  * Shortcuts.js
       
 28615  *
       
 28616  * Copyright, Moxiecode Systems AB
       
 28617  * Released under LGPL License.
       
 28618  *
       
 28619  * License: http://www.tinymce.com/license
       
 28620  * Contributing: http://www.tinymce.com/contributing
       
 28621  */
       
 28622 
       
 28623 /**
       
 28624  * Contains all logic for handling of keyboard shortcuts.
       
 28625  *
       
 28626  * @example
       
 28627  * editor.shortcuts.add('ctrl+a', function() {});
       
 28628  * editor.shortcuts.add('meta+a', function() {}); // "meta" maps to Command on Mac and Ctrl on PC
       
 28629  * editor.shortcuts.add('ctrl+alt+a', function() {});
       
 28630  * editor.shortcuts.add('access+a', function() {}); // "access" maps to ctrl+alt on Mac and shift+alt on PC
       
 28631  */
       
 28632 define("tinymce/Shortcuts", [
       
 28633 	"tinymce/util/Tools",
       
 28634 	"tinymce/Env"
       
 28635 ], function(Tools, Env) {
       
 28636 	var each = Tools.each, explode = Tools.explode;
       
 28637 
       
 28638 	var keyCodeLookup = {
       
 28639 		"f9": 120,
       
 28640 		"f10": 121,
       
 28641 		"f11": 122
       
 28642 	};
       
 28643 
       
 28644 	var modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access');
       
 28645 
       
 28646 	return function(editor) {
       
 28647 		var self = this, shortcuts = {};
       
 28648 
       
 28649 		function createShortcut(pattern, desc, cmdFunc, scope) {
       
 28650 			var id, key, shortcut;
       
 28651 
       
 28652 			shortcut = {
       
 28653 				func: cmdFunc,
       
 28654 				scope: scope || editor,
       
 28655 				desc: editor.translate(desc)
       
 28656 			};
       
 28657 
       
 28658 			// Parse modifiers and keys ctrl+alt+b for example
       
 28659 			each(explode(pattern, '+'), function(value) {
       
 28660 				if (value in modifierNames) {
       
 28661 					shortcut[value] = true;
       
 28662 				} else {
       
 28663 					// Allow numeric keycodes like ctrl+219 for ctrl+[
       
 28664 					if (/^[0-9]{2,}$/.test(value)) {
       
 28665 						shortcut.keyCode = parseInt(value, 10);
       
 28666 					} else {
       
 28667 						shortcut.charCode = value.charCodeAt(0);
       
 28668 						shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
       
 28669 					}
       
 28670 				}
       
 28671 			});
       
 28672 
       
 28673 			// Generate unique id for modifier combination and set default state for unused modifiers
       
 28674 			id = [shortcut.keyCode];
       
 28675 			for (key in modifierNames) {
       
 28676 				if (shortcut[key]) {
       
 28677 					id.push(key);
       
 28678 				} else {
       
 28679 					shortcut[key] = false;
       
 28680 				}
       
 28681 			}
       
 28682 			shortcut.id = id.join(',');
       
 28683 
       
 28684 			// Handle special access modifier differently depending on Mac/Win
       
 28685 			if (shortcut.access) {
       
 28686 				shortcut.alt = true;
       
 28687 
       
 28688 				if (Env.mac) {
       
 28689 					shortcut.ctrl = true;
       
 28690 				} else {
       
 28691 					shortcut.shift = true;
       
 28692 				}
       
 28693 			}
       
 28694 
       
 28695 			// Handle special meta modifier differently depending on Mac/Win
       
 28696 			if (shortcut.meta) {
       
 28697 				if (Env.mac) {
       
 28698 					shortcut.meta = true;
       
 28699 				} else {
       
 28700 					shortcut.ctrl = true;
       
 28701 					shortcut.meta = false;
       
 28702 				}
       
 28703 			}
       
 28704 
       
 28705 			return shortcut;
       
 28706 		}
       
 28707 
       
 28708 		editor.on('keyup keypress keydown', function(e) {
       
 28709 			if ((e.altKey || e.ctrlKey || e.metaKey) && !e.isDefaultPrevented()) {
       
 28710 				each(shortcuts, function(shortcut) {
       
 28711 					if (shortcut.ctrl != e.ctrlKey || shortcut.meta != e.metaKey) {
       
 28712 						return;
       
 28713 					}
       
 28714 
       
 28715 					if (shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
       
 28716 						return;
       
 28717 					}
       
 28718 
       
 28719 					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
       
 28720 						e.preventDefault();
       
 28721 
       
 28722 						if (e.type == "keydown") {
       
 28723 							shortcut.func.call(shortcut.scope);
       
 28724 						}
       
 28725 
       
 28726 						return true;
       
 28727 					}
       
 28728 				});
       
 28729 			}
       
 28730 		});
       
 28731 
       
 28732 		/**
       
 28733 		 * Adds a keyboard shortcut for some command or function.
       
 28734 		 *
       
 28735 		 * @method addShortcut
       
 28736 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
 28737 		 * @param {String} desc Text description for the command.
       
 28738 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
       
 28739 		 * @param {Object} sc Optional scope to execute the function in.
       
 28740 		 * @return {Boolean} true/false state if the shortcut was added or not.
       
 28741 		 */
       
 28742 		self.add = function(pattern, desc, cmdFunc, scope) {
       
 28743 			var cmd;
       
 28744 
       
 28745 			cmd = cmdFunc;
       
 28746 
       
 28747 			if (typeof cmdFunc === 'string') {
       
 28748 				cmdFunc = function() {
       
 28749 					editor.execCommand(cmd, false, null);
       
 28750 				};
       
 28751 			} else if (Tools.isArray(cmd)) {
       
 28752 				cmdFunc = function() {
       
 28753 					editor.execCommand(cmd[0], cmd[1], cmd[2]);
       
 28754 				};
       
 28755 			}
       
 28756 
       
 28757 			each(explode(pattern.toLowerCase()), function(pattern) {
       
 28758 				var shortcut = createShortcut(pattern, desc, cmdFunc, scope);
       
 28759 				shortcuts[shortcut.id] = shortcut;
       
 28760 			});
       
 28761 
       
 28762 			return true;
       
 28763 		};
       
 28764 
       
 28765 		/**
       
 28766 		 * Remove a keyboard shortcut by pattern.
       
 28767 		 *
       
 28768 		 * @method remove
       
 28769 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
 28770 		 * @return {Boolean} true/false state if the shortcut was removed or not.
       
 28771 		 */
       
 28772 		self.remove = function(pattern) {
       
 28773 			var shortcut = createShortcut(pattern);
       
 28774 
       
 28775 			if (shortcuts[shortcut.id]) {
       
 28776 				delete shortcuts[shortcut.id];
       
 28777 				return true;
       
 28778 			}
       
 28779 
       
 28780 			return false;
       
 28781 		};
       
 28782 	};
       
 28783 });
       
 28784 
       
 28785 // Included from: js/tinymce/classes/Editor.js
       
 28786 
       
 28787 /**
       
 28788  * Editor.js
       
 28789  *
       
 28790  * Copyright, Moxiecode Systems AB
       
 28791  * Released under LGPL License.
       
 28792  *
       
 28793  * License: http://www.tinymce.com/license
       
 28794  * Contributing: http://www.tinymce.com/contributing
       
 28795  */
       
 28796 
       
 28797 /*jshint scripturl:true */
       
 28798 
       
 28799 /**
       
 28800  * Include the base event class documentation.
       
 28801  *
       
 28802  * @include ../../../tools/docs/tinymce.Event.js
       
 28803  */
       
 28804 
       
 28805 /**
       
 28806  * This class contains the core logic for a TinyMCE editor.
       
 28807  *
       
 28808  * @class tinymce.Editor
       
 28809  * @mixes tinymce.util.Observable
       
 28810  * @example
       
 28811  * // Add a class to all paragraphs in the editor.
       
 28812  * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
 28813  *
       
 28814  * // Gets the current editors selection as text
       
 28815  * tinymce.activeEditor.selection.getContent({format: 'text'});
       
 28816  *
       
 28817  * // Creates a new editor instance
       
 28818  * var ed = new tinymce.Editor('textareaid', {
       
 28819  *     some_setting: 1
       
 28820  * }, tinymce.EditorManager);
       
 28821  *
       
 28822  * // Select each item the user clicks on
       
 28823  * ed.on('click', function(e) {
       
 28824  *     ed.selection.select(e.target);
       
 28825  * });
       
 28826  *
       
 28827  * ed.render();
       
 28828  */
       
 28829 define("tinymce/Editor", [
       
 28830 	"tinymce/dom/DOMUtils",
       
 28831 	"tinymce/dom/DomQuery",
       
 28832 	"tinymce/AddOnManager",
       
 28833 	"tinymce/NodeChange",
       
 28834 	"tinymce/html/Node",
       
 28835 	"tinymce/dom/Serializer",
       
 28836 	"tinymce/html/Serializer",
       
 28837 	"tinymce/dom/Selection",
       
 28838 	"tinymce/Formatter",
       
 28839 	"tinymce/UndoManager",
       
 28840 	"tinymce/EnterKey",
       
 28841 	"tinymce/ForceBlocks",
       
 28842 	"tinymce/EditorCommands",
       
 28843 	"tinymce/util/URI",
       
 28844 	"tinymce/dom/ScriptLoader",
       
 28845 	"tinymce/dom/EventUtils",
       
 28846 	"tinymce/WindowManager",
       
 28847 	"tinymce/html/Schema",
       
 28848 	"tinymce/html/DomParser",
       
 28849 	"tinymce/util/Quirks",
       
 28850 	"tinymce/Env",
       
 28851 	"tinymce/util/Tools",
       
 28852 	"tinymce/EditorObservable",
       
 28853 	"tinymce/Shortcuts"
       
 28854 ], function(
       
 28855 	DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
       
 28856 	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
       
 28857 	URI, ScriptLoader, EventUtils, WindowManager,
       
 28858 	Schema, DomParser, Quirks, Env, Tools, EditorObservable, Shortcuts
       
 28859 ) {
       
 28860 	// Shorten these names
       
 28861 	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
       
 28862 	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
       
 28863 	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
       
 28864 	var Event = EventUtils.Event;
       
 28865 	var isGecko = Env.gecko, ie = Env.ie;
       
 28866 
       
 28867 	/**
       
 28868 	 * Include documentation for all the events.
       
 28869 	 *
       
 28870 	 * @include ../../../tools/docs/tinymce.Editor.js
       
 28871 	 */
       
 28872 
       
 28873 	/**
       
 28874 	 * Constructs a editor instance by id.
       
 28875 	 *
       
 28876 	 * @constructor
       
 28877 	 * @method Editor
       
 28878 	 * @param {String} id Unique id for the editor.
       
 28879 	 * @param {Object} settings Settings for the editor.
       
 28880 	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
       
 28881 	 * @author Moxiecode
       
 28882 	 */
       
 28883 	function Editor(id, settings, editorManager) {
       
 28884 		var self = this, documentBaseUrl, baseUri;
       
 28885 
       
 28886 		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
       
 28887 		baseUri = editorManager.baseURI;
       
 28888 
       
 28889 		/**
       
 28890 		 * Name/value collection with editor settings.
       
 28891 		 *
       
 28892 		 * @property settings
       
 28893 		 * @type Object
       
 28894 		 * @example
       
 28895 		 * // Get the value of the theme setting
       
 28896 		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
       
 28897 		 */
       
 28898 		self.settings = settings = extend({
       
 28899 			id: id,
       
 28900 			theme: 'modern',
       
 28901 			delta_width: 0,
       
 28902 			delta_height: 0,
       
 28903 			popup_css: '',
       
 28904 			plugins: '',
       
 28905 			document_base_url: documentBaseUrl,
       
 28906 			add_form_submit_trigger: true,
       
 28907 			submit_patch: true,
       
 28908 			add_unload_trigger: true,
       
 28909 			convert_urls: true,
       
 28910 			relative_urls: true,
       
 28911 			remove_script_host: true,
       
 28912 			object_resizing: true,
       
 28913 			doctype: '<!DOCTYPE html>',
       
 28914 			visual: true,
       
 28915 			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
       
 28916 
       
 28917 			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
       
 28918 			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
       
 28919 			forced_root_block: 'p',
       
 28920 			hidden_input: true,
       
 28921 			padd_empty_editor: true,
       
 28922 			render_ui: true,
       
 28923 			indentation: '30px',
       
 28924 			inline_styles: true,
       
 28925 			convert_fonts_to_spans: true,
       
 28926 			indent: 'simple',
       
 28927 			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,' +
       
 28928 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
       
 28929 			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,' +
       
 28930 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
       
 28931 			validate: true,
       
 28932 			entity_encoding: 'named',
       
 28933 			url_converter: self.convertURL,
       
 28934 			url_converter_scope: self,
       
 28935 			ie7_compat: true
       
 28936 		}, settings);
       
 28937 
       
 28938 		AddOnManager.language = settings.language || 'en';
       
 28939 		AddOnManager.languageLoad = settings.language_load;
       
 28940 
       
 28941 		AddOnManager.baseURL = editorManager.baseURL;
       
 28942 
       
 28943 		/**
       
 28944 		 * Editor instance id, normally the same as the div/textarea that was replaced.
       
 28945 		 *
       
 28946 		 * @property id
       
 28947 		 * @type String
       
 28948 		 */
       
 28949 		self.id = settings.id = id;
       
 28950 
       
 28951 		/**
       
 28952 		 * State to force the editor to return false on a isDirty call.
       
 28953 		 *
       
 28954 		 * @property isNotDirty
       
 28955 		 * @type Boolean
       
 28956 		 * @example
       
 28957 		 * function ajaxSave() {
       
 28958 		 *     var ed = tinymce.get('elm1');
       
 28959 		 *
       
 28960 		 *     // Save contents using some XHR call
       
 28961 		 *     alert(ed.getContent());
       
 28962 		 *
       
 28963 		 *     ed.isNotDirty = true; // Force not dirty state
       
 28964 		 * }
       
 28965 		 */
       
 28966 		self.isNotDirty = true;
       
 28967 
       
 28968 		/**
       
 28969 		 * Name/Value object containting plugin instances.
       
 28970 		 *
       
 28971 		 * @property plugins
       
 28972 		 * @type Object
       
 28973 		 * @example
       
 28974 		 * // Execute a method inside a plugin directly
       
 28975 		 * tinymce.activeEditor.plugins.someplugin.someMethod();
       
 28976 		 */
       
 28977 		self.plugins = {};
       
 28978 
       
 28979 		/**
       
 28980 		 * URI object to document configured for the TinyMCE instance.
       
 28981 		 *
       
 28982 		 * @property documentBaseURI
       
 28983 		 * @type tinymce.util.URI
       
 28984 		 * @example
       
 28985 		 * // Get relative URL from the location of document_base_url
       
 28986 		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
       
 28987 		 *
       
 28988 		 * // Get absolute URL from the location of document_base_url
       
 28989 		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
       
 28990 		 */
       
 28991 		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
       
 28992 			base_uri: baseUri
       
 28993 		});
       
 28994 
       
 28995 		/**
       
 28996 		 * URI object to current document that holds the TinyMCE editor instance.
       
 28997 		 *
       
 28998 		 * @property baseURI
       
 28999 		 * @type tinymce.util.URI
       
 29000 		 * @example
       
 29001 		 * // Get relative URL from the location of the API
       
 29002 		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
       
 29003 		 *
       
 29004 		 * // Get absolute URL from the location of the API
       
 29005 		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
       
 29006 		 */
       
 29007 		self.baseURI = baseUri;
       
 29008 
       
 29009 		/**
       
 29010 		 * Array with CSS files to load into the iframe.
       
 29011 		 *
       
 29012 		 * @property contentCSS
       
 29013 		 * @type Array
       
 29014 		 */
       
 29015 		self.contentCSS = [];
       
 29016 
       
 29017 		/**
       
 29018 		 * Array of CSS styles to add to head of document when the editor loads.
       
 29019 		 *
       
 29020 		 * @property contentStyles
       
 29021 		 * @type Array
       
 29022 		 */
       
 29023 		self.contentStyles = [];
       
 29024 
       
 29025 		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
       
 29026 		self.shortcuts = new Shortcuts(self);
       
 29027 		self.loadedCSS = {};
       
 29028 		self.editorCommands = new EditorCommands(self);
       
 29029 
       
 29030 		if (settings.target) {
       
 29031 			self.targetElm = settings.target;
       
 29032 		}
       
 29033 
       
 29034 		self.suffix = editorManager.suffix;
       
 29035 		self.editorManager = editorManager;
       
 29036 		self.inline = settings.inline;
       
 29037 
       
 29038 		if (settings.cache_suffix) {
       
 29039 			Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, '');
       
 29040 		}
       
 29041 
       
 29042 		// Call setup
       
 29043 		editorManager.fire('SetupEditor', self);
       
 29044 		self.execCallback('setup', self);
       
 29045 
       
 29046 		/**
       
 29047 		 * Dom query instance with default scope to the editor document and default element is the body of the editor.
       
 29048 		 *
       
 29049 		 * @property $
       
 29050 		 * @type tinymce.dom.DomQuery
       
 29051 		 * @example
       
 29052 		 * tinymce.activeEditor.$('p').css('color', 'red');
       
 29053 		 * tinymce.activeEditor.$().append('<p>new</p>');
       
 29054 		 */
       
 29055 		self.$ = DomQuery.overrideDefaults(function() {
       
 29056 			return {
       
 29057 				context: self.inline ? self.getBody() : self.getDoc(),
       
 29058 				element: self.getBody()
       
 29059 			};
       
 29060 		});
       
 29061 	}
       
 29062 
       
 29063 	Editor.prototype = {
       
 29064 		/**
       
 29065 		 * Renderes the editor/adds it to the page.
       
 29066 		 *
       
 29067 		 * @method render
       
 29068 		 */
       
 29069 		render: function() {
       
 29070 			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
       
 29071 
       
 29072 			function readyHandler() {
       
 29073 				DOM.unbind(window, 'ready', readyHandler);
       
 29074 				self.render();
       
 29075 			}
       
 29076 
       
 29077 			// Page is not loaded yet, wait for it
       
 29078 			if (!Event.domLoaded) {
       
 29079 				DOM.bind(window, 'ready', readyHandler);
       
 29080 				return;
       
 29081 			}
       
 29082 
       
 29083 			// Element not found, then skip initialization
       
 29084 			if (!self.getElement()) {
       
 29085 				return;
       
 29086 			}
       
 29087 
       
 29088 			// No editable support old iOS versions etc
       
 29089 			if (!Env.contentEditable) {
       
 29090 				return;
       
 29091 			}
       
 29092 
       
 29093 			// Hide target element early to prevent content flashing
       
 29094 			if (!settings.inline) {
       
 29095 				self.orgVisibility = self.getElement().style.visibility;
       
 29096 				self.getElement().style.visibility = 'hidden';
       
 29097 			} else {
       
 29098 				self.inline = true;
       
 29099 			}
       
 29100 
       
 29101 			var form = self.getElement().form || DOM.getParent(id, 'form');
       
 29102 			if (form) {
       
 29103 				self.formElement = form;
       
 29104 
       
 29105 				// Add hidden input for non input elements inside form elements
       
 29106 				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
       
 29107 					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
       
 29108 					self.hasHiddenInput = true;
       
 29109 				}
       
 29110 
       
 29111 				// Pass submit/reset from form to editor instance
       
 29112 				self.formEventDelegate = function(e) {
       
 29113 					self.fire(e.type, e);
       
 29114 				};
       
 29115 
       
 29116 				DOM.bind(form, 'submit reset', self.formEventDelegate);
       
 29117 
       
 29118 				// Reset contents in editor when the form is reset
       
 29119 				self.on('reset', function() {
       
 29120 					self.setContent(self.startContent, {format: 'raw'});
       
 29121 				});
       
 29122 
       
 29123 				// Check page uses id="submit" or name="submit" for it's submit button
       
 29124 				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
       
 29125 					form._mceOldSubmit = form.submit;
       
 29126 					form.submit = function() {
       
 29127 						self.editorManager.triggerSave();
       
 29128 						self.isNotDirty = true;
       
 29129 
       
 29130 						return form._mceOldSubmit(form);
       
 29131 					};
       
 29132 				}
       
 29133 			}
       
 29134 
       
 29135 			/**
       
 29136 			 * Window manager reference, use this to open new windows and dialogs.
       
 29137 			 *
       
 29138 			 * @property windowManager
       
 29139 			 * @type tinymce.WindowManager
       
 29140 			 * @example
       
 29141 			 * // Shows an alert message
       
 29142 			 * tinymce.activeEditor.windowManager.alert('Hello world!');
       
 29143 			 *
       
 29144 			 * // Opens a new dialog with the file.htm file and the size 320x240
       
 29145 			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
       
 29146 			 * tinymce.activeEditor.windowManager.open({
       
 29147 			 *    url: 'file.htm',
       
 29148 			 *    width: 320,
       
 29149 			 *    height: 240
       
 29150 			 * }, {
       
 29151 			 *    custom_param: 1
       
 29152 			 * });
       
 29153 			 */
       
 29154 			self.windowManager = new WindowManager(self);
       
 29155 
       
 29156 			if (settings.encoding == 'xml') {
       
 29157 				self.on('GetContent', function(e) {
       
 29158 					if (e.save) {
       
 29159 						e.content = DOM.encode(e.content);
       
 29160 					}
       
 29161 				});
       
 29162 			}
       
 29163 
       
 29164 			if (settings.add_form_submit_trigger) {
       
 29165 				self.on('submit', function() {
       
 29166 					if (self.initialized) {
       
 29167 						self.save();
       
 29168 					}
       
 29169 				});
       
 29170 			}
       
 29171 
       
 29172 			if (settings.add_unload_trigger) {
       
 29173 				self._beforeUnload = function() {
       
 29174 					if (self.initialized && !self.destroyed && !self.isHidden()) {
       
 29175 						self.save({format: 'raw', no_events: true, set_dirty: false});
       
 29176 					}
       
 29177 				};
       
 29178 
       
 29179 				self.editorManager.on('BeforeUnload', self._beforeUnload);
       
 29180 			}
       
 29181 
       
 29182 			// Load scripts
       
 29183 			function loadScripts() {
       
 29184 				var scriptLoader = ScriptLoader.ScriptLoader;
       
 29185 
       
 29186 				if (settings.language && settings.language != 'en' && !settings.language_url) {
       
 29187 					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
       
 29188 				}
       
 29189 
       
 29190 				if (settings.language_url) {
       
 29191 					scriptLoader.add(settings.language_url);
       
 29192 				}
       
 29193 
       
 29194 				if (settings.theme && typeof settings.theme != "function" &&
       
 29195 					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
       
 29196 					var themeUrl = settings.theme_url;
       
 29197 
       
 29198 					if (themeUrl) {
       
 29199 						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
       
 29200 					} else {
       
 29201 						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
       
 29202 					}
       
 29203 
       
 29204 					ThemeManager.load(settings.theme, themeUrl);
       
 29205 				}
       
 29206 
       
 29207 				if (Tools.isArray(settings.plugins)) {
       
 29208 					settings.plugins = settings.plugins.join(' ');
       
 29209 				}
       
 29210 
       
 29211 				each(settings.external_plugins, function(url, name) {
       
 29212 					PluginManager.load(name, url);
       
 29213 					settings.plugins += ' ' + name;
       
 29214 				});
       
 29215 
       
 29216 				each(settings.plugins.split(/[ ,]/), function(plugin) {
       
 29217 					plugin = trim(plugin);
       
 29218 
       
 29219 					if (plugin && !PluginManager.urls[plugin]) {
       
 29220 						if (plugin.charAt(0) == '-') {
       
 29221 							plugin = plugin.substr(1, plugin.length);
       
 29222 
       
 29223 							var dependencies = PluginManager.dependencies(plugin);
       
 29224 
       
 29225 							each(dependencies, function(dep) {
       
 29226 								var defaultSettings = {
       
 29227 									prefix: 'plugins/',
       
 29228 									resource: dep,
       
 29229 									suffix: '/plugin' + suffix + '.js'
       
 29230 								};
       
 29231 
       
 29232 								dep = PluginManager.createUrl(defaultSettings, dep);
       
 29233 								PluginManager.load(dep.resource, dep);
       
 29234 							});
       
 29235 						} else {
       
 29236 							PluginManager.load(plugin, {
       
 29237 								prefix: 'plugins/',
       
 29238 								resource: plugin,
       
 29239 								suffix: '/plugin' + suffix + '.js'
       
 29240 							});
       
 29241 						}
       
 29242 					}
       
 29243 				});
       
 29244 
       
 29245 				scriptLoader.loadQueue(function() {
       
 29246 					if (!self.removed) {
       
 29247 						self.init();
       
 29248 					}
       
 29249 				});
       
 29250 			}
       
 29251 
       
 29252 			loadScripts();
       
 29253 		},
       
 29254 
       
 29255 		/**
       
 29256 		 * Initializes the editor this will be called automatically when
       
 29257 		 * all plugins/themes and language packs are loaded by the rendered method.
       
 29258 		 * This method will setup the iframe and create the theme and plugin instances.
       
 29259 		 *
       
 29260 		 * @method init
       
 29261 		 */
       
 29262 		init: function() {
       
 29263 			var self = this, settings = self.settings, elm = self.getElement();
       
 29264 			var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
       
 29265 
       
 29266 			this.editorManager.i18n.setCode(settings.language);
       
 29267 			self.rtl = this.editorManager.i18n.rtl;
       
 29268 			self.editorManager.add(self);
       
 29269 
       
 29270 			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
       
 29271 
       
 29272 			/**
       
 29273 			 * Reference to the theme instance that was used to generate the UI.
       
 29274 			 *
       
 29275 			 * @property theme
       
 29276 			 * @type tinymce.Theme
       
 29277 			 * @example
       
 29278 			 * // Executes a method on the theme directly
       
 29279 			 * tinymce.activeEditor.theme.someMethod();
       
 29280 			 */
       
 29281 			if (settings.theme) {
       
 29282 				if (typeof settings.theme != "function") {
       
 29283 					settings.theme = settings.theme.replace(/-/, '');
       
 29284 					Theme = ThemeManager.get(settings.theme);
       
 29285 					self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
       
 29286 
       
 29287 					if (self.theme.init) {
       
 29288 						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''), self.$);
       
 29289 					}
       
 29290 				} else {
       
 29291 					self.theme = settings.theme;
       
 29292 				}
       
 29293 			}
       
 29294 
       
 29295 			function initPlugin(plugin) {
       
 29296 				var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
       
 29297 
       
 29298 				pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
       
 29299 				plugin = trim(plugin);
       
 29300 				if (Plugin && inArray(initializedPlugins, plugin) === -1) {
       
 29301 					each(PluginManager.dependencies(plugin), function(dep) {
       
 29302 						initPlugin(dep);
       
 29303 					});
       
 29304 
       
 29305 					pluginInstance = new Plugin(self, pluginUrl, self.$);
       
 29306 
       
 29307 					self.plugins[plugin] = pluginInstance;
       
 29308 
       
 29309 					if (pluginInstance.init) {
       
 29310 						pluginInstance.init(self, pluginUrl);
       
 29311 						initializedPlugins.push(plugin);
       
 29312 					}
       
 29313 				}
       
 29314 			}
       
 29315 
       
 29316 			// Create all plugins
       
 29317 			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
       
 29318 
       
 29319 			// Measure box
       
 29320 			if (settings.render_ui && self.theme) {
       
 29321 				self.orgDisplay = elm.style.display;
       
 29322 
       
 29323 				if (typeof settings.theme != "function") {
       
 29324 					w = settings.width || elm.style.width || elm.offsetWidth;
       
 29325 					h = settings.height || elm.style.height || elm.offsetHeight;
       
 29326 					minHeight = settings.min_height || 100;
       
 29327 					re = /^[0-9\.]+(|px)$/i;
       
 29328 
       
 29329 					if (re.test('' + w)) {
       
 29330 						w = Math.max(parseInt(w, 10), 100);
       
 29331 					}
       
 29332 
       
 29333 					if (re.test('' + h)) {
       
 29334 						h = Math.max(parseInt(h, 10), minHeight);
       
 29335 					}
       
 29336 
       
 29337 					// Render UI
       
 29338 					o = self.theme.renderUI({
       
 29339 						targetNode: elm,
       
 29340 						width: w,
       
 29341 						height: h,
       
 29342 						deltaWidth: settings.delta_width,
       
 29343 						deltaHeight: settings.delta_height
       
 29344 					});
       
 29345 
       
 29346 					// Resize editor
       
 29347 					if (!settings.content_editable) {
       
 29348 						h = (o.iframeHeight || h) + (typeof h == 'number' ? (o.deltaHeight || 0) : '');
       
 29349 						if (h < minHeight) {
       
 29350 							h = minHeight;
       
 29351 						}
       
 29352 					}
       
 29353 				} else {
       
 29354 					o = settings.theme(self, elm);
       
 29355 
       
 29356 					// Convert element type to id:s
       
 29357 					if (o.editorContainer.nodeType) {
       
 29358 						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
       
 29359 					}
       
 29360 
       
 29361 					// Convert element type to id:s
       
 29362 					if (o.iframeContainer.nodeType) {
       
 29363 						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
       
 29364 					}
       
 29365 
       
 29366 					// Use specified iframe height or the targets offsetHeight
       
 29367 					h = o.iframeHeight || elm.offsetHeight;
       
 29368 				}
       
 29369 
       
 29370 				self.editorContainer = o.editorContainer;
       
 29371 			}
       
 29372 
       
 29373 			// Load specified content CSS last
       
 29374 			if (settings.content_css) {
       
 29375 				each(explode(settings.content_css), function(u) {
       
 29376 					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
       
 29377 				});
       
 29378 			}
       
 29379 
       
 29380 			// Load specified content CSS last
       
 29381 			if (settings.content_style) {
       
 29382 				self.contentStyles.push(settings.content_style);
       
 29383 			}
       
 29384 
       
 29385 			// Content editable mode ends here
       
 29386 			if (settings.content_editable) {
       
 29387 				elm = n = o = null; // Fix IE leak
       
 29388 				return self.initContentBody();
       
 29389 			}
       
 29390 
       
 29391 			self.iframeHTML = settings.doctype + '<html><head>';
       
 29392 
       
 29393 			// We only need to override paths if we have to
       
 29394 			// IE has a bug where it remove site absolute urls to relative ones if this is specified
       
 29395 			if (settings.document_base_url != self.documentBaseUrl) {
       
 29396 				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
       
 29397 			}
       
 29398 
       
 29399 			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
       
 29400 			if (!Env.caretAfter && settings.ie7_compat) {
       
 29401 				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
       
 29402 			}
       
 29403 
       
 29404 			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
       
 29405 
       
 29406 			// Load the CSS by injecting them into the HTML this will reduce "flicker"
       
 29407 			for (i = 0; i < self.contentCSS.length; i++) {
       
 29408 				var cssUrl = self.contentCSS[i];
       
 29409 				self.iframeHTML += (
       
 29410 					'<link type="text/css" ' +
       
 29411 						'rel="stylesheet" ' +
       
 29412 						'href="' + Tools._addCacheSuffix(cssUrl) + '" />'
       
 29413 				);
       
 29414 				self.loadedCSS[cssUrl] = true;
       
 29415 			}
       
 29416 
       
 29417 			bodyId = settings.body_id || 'tinymce';
       
 29418 			if (bodyId.indexOf('=') != -1) {
       
 29419 				bodyId = self.getParam('body_id', '', 'hash');
       
 29420 				bodyId = bodyId[self.id] || bodyId;
       
 29421 			}
       
 29422 
       
 29423 			bodyClass = settings.body_class || '';
       
 29424 			if (bodyClass.indexOf('=') != -1) {
       
 29425 				bodyClass = self.getParam('body_class', '', 'hash');
       
 29426 				bodyClass = bodyClass[self.id] || '';
       
 29427 			}
       
 29428 
       
 29429 			if (settings.content_security_policy) {
       
 29430 				self.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />';
       
 29431 			}
       
 29432 
       
 29433 			self.iframeHTML += '</head><body id="' + bodyId +
       
 29434 				'" class="mce-content-body ' + bodyClass +
       
 29435 				'" data-id="' + self.id + '"><br></body></html>';
       
 29436 
       
 29437 			/*eslint no-script-url:0 */
       
 29438 			var domainRelaxUrl = 'javascript:(function(){' +
       
 29439 				'document.open();document.domain="' + document.domain + '";' +
       
 29440 				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
       
 29441 				'document.close();ed.initContentBody(true);})()';
       
 29442 
       
 29443 			// Domain relaxing is required since the user has messed around with document.domain
       
 29444 			if (document.domain != location.hostname) {
       
 29445 				url = domainRelaxUrl;
       
 29446 			}
       
 29447 
       
 29448 			// Create iframe
       
 29449 			// TODO: ACC add the appropriate description on this.
       
 29450 			var ifr = DOM.create('iframe', {
       
 29451 				id: self.id + "_ifr",
       
 29452 				//src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
       
 29453 				frameBorder: '0',
       
 29454 				allowTransparency: "true",
       
 29455 				title: self.editorManager.translate(
       
 29456 						"Rich Text Area. Press ALT-F9 for menu. " +
       
 29457 						"Press ALT-F10 for toolbar. Press ALT-0 for help"
       
 29458 				),
       
 29459 				style: {
       
 29460 					width: '100%',
       
 29461 					height: h,
       
 29462 					display: 'block' // Important for Gecko to render the iframe correctly
       
 29463 				}
       
 29464 			});
       
 29465 
       
 29466 			ifr.onload = function() {
       
 29467 				ifr.onload = null;
       
 29468 				self.fire("load");
       
 29469 			};
       
 29470 
       
 29471 			DOM.setAttrib(ifr, "src", url || 'javascript:""');
       
 29472 
       
 29473 			self.contentAreaContainer = o.iframeContainer;
       
 29474 			self.iframeElement = ifr;
       
 29475 
       
 29476 			n = DOM.add(o.iframeContainer, ifr);
       
 29477 
       
 29478 			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
       
 29479 			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
       
 29480 			if (ie) {
       
 29481 				try {
       
 29482 					self.getDoc();
       
 29483 				} catch (e) {
       
 29484 					n.src = url = domainRelaxUrl;
       
 29485 				}
       
 29486 			}
       
 29487 
       
 29488 			if (o.editorContainer) {
       
 29489 				DOM.get(o.editorContainer).style.display = self.orgDisplay;
       
 29490 				self.hidden = DOM.isHidden(o.editorContainer);
       
 29491 			}
       
 29492 
       
 29493 			self.getElement().style.display = 'none';
       
 29494 			DOM.setAttrib(self.id, 'aria-hidden', true);
       
 29495 
       
 29496 			if (!url) {
       
 29497 				self.initContentBody();
       
 29498 			}
       
 29499 
       
 29500 			elm = n = o = null; // Cleanup
       
 29501 		},
       
 29502 
       
 29503 		/**
       
 29504 		 * This method get called by the init method ones the iframe is loaded.
       
 29505 		 * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
       
 29506 		 *
       
 29507 		 * @method initContentBody
       
 29508 		 * @private
       
 29509 		 */
       
 29510 		initContentBody: function(skipWrite) {
       
 29511 			var self = this, settings = self.settings, targetElm = self.getElement(), doc = self.getDoc(), body, contentCssText;
       
 29512 
       
 29513 			// Restore visibility on target element
       
 29514 			if (!settings.inline) {
       
 29515 				self.getElement().style.visibility = self.orgVisibility;
       
 29516 			}
       
 29517 
       
 29518 			// Setup iframe body
       
 29519 			if (!skipWrite && !settings.content_editable) {
       
 29520 				doc.open();
       
 29521 				doc.write(self.iframeHTML);
       
 29522 				doc.close();
       
 29523 			}
       
 29524 
       
 29525 			if (settings.content_editable) {
       
 29526 				self.on('remove', function() {
       
 29527 					var bodyEl = this.getBody();
       
 29528 
       
 29529 					DOM.removeClass(bodyEl, 'mce-content-body');
       
 29530 					DOM.removeClass(bodyEl, 'mce-edit-focus');
       
 29531 					DOM.setAttrib(bodyEl, 'contentEditable', null);
       
 29532 				});
       
 29533 
       
 29534 				DOM.addClass(targetElm, 'mce-content-body');
       
 29535 				self.contentDocument = doc = settings.content_document || document;
       
 29536 				self.contentWindow = settings.content_window || window;
       
 29537 				self.bodyElement = targetElm;
       
 29538 
       
 29539 				// Prevent leak in IE
       
 29540 				settings.content_document = settings.content_window = null;
       
 29541 
       
 29542 				// TODO: Fix this
       
 29543 				settings.root_name = targetElm.nodeName.toLowerCase();
       
 29544 			}
       
 29545 
       
 29546 			// It will not steal focus while setting contentEditable
       
 29547 			body = self.getBody();
       
 29548 			body.disabled = true;
       
 29549 
       
 29550 			if (!settings.readonly) {
       
 29551 				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
       
 29552 					body.style.position = 'relative';
       
 29553 				}
       
 29554 
       
 29555 				body.contentEditable = self.getParam('content_editable_state', true);
       
 29556 			}
       
 29557 
       
 29558 			body.disabled = false;
       
 29559 
       
 29560 			/**
       
 29561 			 * Schema instance, enables you to validate elements and it's children.
       
 29562 			 *
       
 29563 			 * @property schema
       
 29564 			 * @type tinymce.html.Schema
       
 29565 			 */
       
 29566 			self.schema = new Schema(settings);
       
 29567 
       
 29568 			/**
       
 29569 			 * DOM instance for the editor.
       
 29570 			 *
       
 29571 			 * @property dom
       
 29572 			 * @type tinymce.dom.DOMUtils
       
 29573 			 * @example
       
 29574 			 * // Adds a class to all paragraphs within the editor
       
 29575 			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
 29576 			 */
       
 29577 			self.dom = new DOMUtils(doc, {
       
 29578 				keep_values: true,
       
 29579 				url_converter: self.convertURL,
       
 29580 				url_converter_scope: self,
       
 29581 				hex_colors: settings.force_hex_style_colors,
       
 29582 				class_filter: settings.class_filter,
       
 29583 				update_styles: true,
       
 29584 				root_element: self.inline ? self.getBody() : null,
       
 29585 				collect: settings.content_editable,
       
 29586 				schema: self.schema,
       
 29587 				onSetAttrib: function(e) {
       
 29588 					self.fire('SetAttrib', e);
       
 29589 				}
       
 29590 			});
       
 29591 
       
 29592 			/**
       
 29593 			 * HTML parser will be used when contents is inserted into the editor.
       
 29594 			 *
       
 29595 			 * @property parser
       
 29596 			 * @type tinymce.html.DomParser
       
 29597 			 */
       
 29598 			self.parser = new DomParser(settings, self.schema);
       
 29599 
       
 29600 			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
       
 29601 			self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
       
 29602 				var i = nodes.length, node, dom = self.dom, value, internalName;
       
 29603 
       
 29604 				while (i--) {
       
 29605 					node = nodes[i];
       
 29606 					value = node.attr(name);
       
 29607 					internalName = 'data-mce-' + name;
       
 29608 
       
 29609 					// Add internal attribute if we need to we don't on a refresh of the document
       
 29610 					if (!node.attributes.map[internalName]) {
       
 29611 						if (name === "style") {
       
 29612 							value = dom.serializeStyle(dom.parseStyle(value), node.name);
       
 29613 
       
 29614 							if (!value.length) {
       
 29615 								value = null;
       
 29616 							}
       
 29617 
       
 29618 							node.attr(internalName, value);
       
 29619 							node.attr(name, value);
       
 29620 						} else if (name === "tabindex") {
       
 29621 							node.attr(internalName, value);
       
 29622 							node.attr(name, null);
       
 29623 						} else {
       
 29624 							node.attr(internalName, self.convertURL(value, name, node.name));
       
 29625 						}
       
 29626 					}
       
 29627 				}
       
 29628 			});
       
 29629 
       
 29630 			// Keep scripts from executing
       
 29631 			self.parser.addNodeFilter('script', function(nodes) {
       
 29632 				var i = nodes.length, node;
       
 29633 
       
 29634 				while (i--) {
       
 29635 					node = nodes[i];
       
 29636 					node.attr('type', 'mce-' + (node.attr('type') || 'no/type'));
       
 29637 				}
       
 29638 			});
       
 29639 
       
 29640 			self.parser.addNodeFilter('#cdata', function(nodes) {
       
 29641 				var i = nodes.length, node;
       
 29642 
       
 29643 				while (i--) {
       
 29644 					node = nodes[i];
       
 29645 					node.type = 8;
       
 29646 					node.name = '#comment';
       
 29647 					node.value = '[CDATA[' + node.value + ']]';
       
 29648 				}
       
 29649 			});
       
 29650 
       
 29651 			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
       
 29652 				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
       
 29653 
       
 29654 				while (i--) {
       
 29655 					node = nodes[i];
       
 29656 
       
 29657 					if (node.isEmpty(nonEmptyElements)) {
       
 29658 						node.append(new Node('br', 1)).shortEnded = true;
       
 29659 					}
       
 29660 				}
       
 29661 			});
       
 29662 
       
 29663 			/**
       
 29664 			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
       
 29665 			 *
       
 29666 			 * @property serializer
       
 29667 			 * @type tinymce.dom.Serializer
       
 29668 			 * @example
       
 29669 			 * // Serializes the first paragraph in the editor into a string
       
 29670 			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
       
 29671 			 */
       
 29672 			self.serializer = new DomSerializer(settings, self);
       
 29673 
       
 29674 			/**
       
 29675 			 * Selection instance for the editor.
       
 29676 			 *
       
 29677 			 * @property selection
       
 29678 			 * @type tinymce.dom.Selection
       
 29679 			 * @example
       
 29680 			 * // Sets some contents to the current selection in the editor
       
 29681 			 * tinymce.activeEditor.selection.setContent('Some contents');
       
 29682 			 *
       
 29683 			 * // Gets the current selection
       
 29684 			 * alert(tinymce.activeEditor.selection.getContent());
       
 29685 			 *
       
 29686 			 * // Selects the first paragraph found
       
 29687 			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
       
 29688 			 */
       
 29689 			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
       
 29690 
       
 29691 			/**
       
 29692 			 * Formatter instance.
       
 29693 			 *
       
 29694 			 * @property formatter
       
 29695 			 * @type tinymce.Formatter
       
 29696 			 */
       
 29697 			self.formatter = new Formatter(self);
       
 29698 
       
 29699 			/**
       
 29700 			 * Undo manager instance, responsible for handling undo levels.
       
 29701 			 *
       
 29702 			 * @property undoManager
       
 29703 			 * @type tinymce.UndoManager
       
 29704 			 * @example
       
 29705 			 * // Undoes the last modification to the editor
       
 29706 			 * tinymce.activeEditor.undoManager.undo();
       
 29707 			 */
       
 29708 			self.undoManager = new UndoManager(self);
       
 29709 
       
 29710 			self.forceBlocks = new ForceBlocks(self);
       
 29711 			self.enterKey = new EnterKey(self);
       
 29712 			self._nodeChangeDispatcher = new NodeChange(self);
       
 29713 
       
 29714 			self.fire('PreInit');
       
 29715 
       
 29716 			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
       
 29717 				doc.body.spellcheck = false; // Gecko
       
 29718 				DOM.setAttrib(body, "spellcheck", "false");
       
 29719 			}
       
 29720 
       
 29721 			self.fire('PostRender');
       
 29722 
       
 29723 			self.quirks = new Quirks(self);
       
 29724 
       
 29725 			if (settings.directionality) {
       
 29726 				body.dir = settings.directionality;
       
 29727 			}
       
 29728 
       
 29729 			if (settings.nowrap) {
       
 29730 				body.style.whiteSpace = "nowrap";
       
 29731 			}
       
 29732 
       
 29733 			if (settings.protect) {
       
 29734 				self.on('BeforeSetContent', function(e) {
       
 29735 					each(settings.protect, function(pattern) {
       
 29736 						e.content = e.content.replace(pattern, function(str) {
       
 29737 							return '<!--mce:protected ' + escape(str) + '-->';
       
 29738 						});
       
 29739 					});
       
 29740 				});
       
 29741 			}
       
 29742 
       
 29743 			self.on('SetContent', function() {
       
 29744 				self.addVisual(self.getBody());
       
 29745 			});
       
 29746 
       
 29747 			// Remove empty contents
       
 29748 			if (settings.padd_empty_editor) {
       
 29749 				self.on('PostProcess', function(e) {
       
 29750 					e.content = e.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
       
 29751 				});
       
 29752 			}
       
 29753 
       
 29754 			self.load({initial: true, format: 'html'});
       
 29755 			self.startContent = self.getContent({format: 'raw'});
       
 29756 
       
 29757 			/**
       
 29758 			 * Is set to true after the editor instance has been initialized
       
 29759 			 *
       
 29760 			 * @property initialized
       
 29761 			 * @type Boolean
       
 29762 			 * @example
       
 29763 			 * function isEditorInitialized(editor) {
       
 29764 			 *     return editor && editor.initialized;
       
 29765 			 * }
       
 29766 			 */
       
 29767 			self.initialized = true;
       
 29768 			self.bindPendingEventDelegates();
       
 29769 
       
 29770 			self.fire('init');
       
 29771 			self.focus(true);
       
 29772 			self.nodeChanged({initial: true});
       
 29773 			self.execCallback('init_instance_callback', self);
       
 29774 
       
 29775 			// Add editor specific CSS styles
       
 29776 			if (self.contentStyles.length > 0) {
       
 29777 				contentCssText = '';
       
 29778 
       
 29779 				each(self.contentStyles, function(style) {
       
 29780 					contentCssText += style + "\r\n";
       
 29781 				});
       
 29782 
       
 29783 				self.dom.addStyle(contentCssText);
       
 29784 			}
       
 29785 
       
 29786 			// Load specified content CSS last
       
 29787 			each(self.contentCSS, function(cssUrl) {
       
 29788 				if (!self.loadedCSS[cssUrl]) {
       
 29789 					self.dom.loadCSS(cssUrl);
       
 29790 					self.loadedCSS[cssUrl] = true;
       
 29791 				}
       
 29792 			});
       
 29793 
       
 29794 			// Handle auto focus
       
 29795 			if (settings.auto_focus) {
       
 29796 				setTimeout(function() {
       
 29797 					var editor;
       
 29798 
       
 29799 					if (settings.auto_focus === true) {
       
 29800 						editor = self;
       
 29801 					} else {
       
 29802 						editor = self.editorManager.get(settings.auto_focus);
       
 29803 					}
       
 29804 
       
 29805 					if (!editor.destroyed) {
       
 29806 						editor.focus();
       
 29807 					}
       
 29808 				}, 100);
       
 29809 			}
       
 29810 
       
 29811 			// Clean up references for IE
       
 29812 			targetElm = doc = body = null;
       
 29813 		},
       
 29814 
       
 29815 		/**
       
 29816 		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
       
 29817 		 * it will also place DOM focus inside the editor.
       
 29818 		 *
       
 29819 		 * @method focus
       
 29820 		 * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor.
       
 29821 		 */
       
 29822 		focus: function(skipFocus) {
       
 29823 			var self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
       
 29824 			var controlElm, doc = self.getDoc(), body;
       
 29825 
       
 29826 			if (!skipFocus) {
       
 29827 				// Get selected control element
       
 29828 				rng = selection.getRng();
       
 29829 				if (rng.item) {
       
 29830 					controlElm = rng.item(0);
       
 29831 				}
       
 29832 
       
 29833 				self._refreshContentEditable();
       
 29834 
       
 29835 				// Focus the window iframe
       
 29836 				if (!contentEditable) {
       
 29837 					// WebKit needs this call to fire focusin event properly see #5948
       
 29838 					// But Opera pre Blink engine will produce an empty selection so skip Opera
       
 29839 					if (!Env.opera) {
       
 29840 						self.getBody().focus();
       
 29841 					}
       
 29842 
       
 29843 					self.getWin().focus();
       
 29844 				}
       
 29845 
       
 29846 				// Focus the body as well since it's contentEditable
       
 29847 				if (isGecko || contentEditable) {
       
 29848 					body = self.getBody();
       
 29849 
       
 29850 					// Check for setActive since it doesn't scroll to the element
       
 29851 					if (body.setActive) {
       
 29852 						// IE 11 sometimes throws "Invalid function" then fallback to focus
       
 29853 						try {
       
 29854 							body.setActive();
       
 29855 						} catch (ex) {
       
 29856 							body.focus();
       
 29857 						}
       
 29858 					} else {
       
 29859 						body.focus();
       
 29860 					}
       
 29861 
       
 29862 					if (contentEditable) {
       
 29863 						selection.normalize();
       
 29864 					}
       
 29865 				}
       
 29866 
       
 29867 				// Restore selected control element
       
 29868 				// This is needed when for example an image is selected within a
       
 29869 				// layer a call to focus will then remove the control selection
       
 29870 				if (controlElm && controlElm.ownerDocument == doc) {
       
 29871 					rng = doc.body.createControlRange();
       
 29872 					rng.addElement(controlElm);
       
 29873 					rng.select();
       
 29874 				}
       
 29875 			}
       
 29876 
       
 29877 			self.editorManager.setActive(self);
       
 29878 		},
       
 29879 
       
 29880 		/**
       
 29881 		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
       
 29882 		 * There new event model is a better way to add callback so this method might be removed in the future.
       
 29883 		 *
       
 29884 		 * @method execCallback
       
 29885 		 * @param {String} name Name of the callback to execute.
       
 29886 		 * @return {Object} Return value passed from callback function.
       
 29887 		 */
       
 29888 		execCallback: function(name) {
       
 29889 			var self = this, callback = self.settings[name], scope;
       
 29890 
       
 29891 			if (!callback) {
       
 29892 				return;
       
 29893 			}
       
 29894 
       
 29895 			// Look through lookup
       
 29896 			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
       
 29897 				callback = scope.func;
       
 29898 				scope = scope.scope;
       
 29899 			}
       
 29900 
       
 29901 			if (typeof callback === 'string') {
       
 29902 				scope = callback.replace(/\.\w+$/, '');
       
 29903 				scope = scope ? resolve(scope) : 0;
       
 29904 				callback = resolve(callback);
       
 29905 				self.callbackLookup = self.callbackLookup || {};
       
 29906 				self.callbackLookup[name] = {func: callback, scope: scope};
       
 29907 			}
       
 29908 
       
 29909 			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
       
 29910 		},
       
 29911 
       
 29912 		/**
       
 29913 		 * Translates the specified string by replacing variables with language pack items it will also check if there is
       
 29914 		 * a key mathcin the input.
       
 29915 		 *
       
 29916 		 * @method translate
       
 29917 		 * @param {String} text String to translate by the language pack data.
       
 29918 		 * @return {String} Translated string.
       
 29919 		 */
       
 29920 		translate: function(text) {
       
 29921 			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
       
 29922 
       
 29923 			if (!text) {
       
 29924 				return '';
       
 29925 			}
       
 29926 
       
 29927 			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
       
 29928 				return i18n.data[lang + '.' + b] || '{#' + b + '}';
       
 29929 			});
       
 29930 		},
       
 29931 
       
 29932 		/**
       
 29933 		 * Returns a language pack item by name/key.
       
 29934 		 *
       
 29935 		 * @method getLang
       
 29936 		 * @param {String} name Name/key to get from the language pack.
       
 29937 		 * @param {String} defaultVal Optional default value to retrive.
       
 29938 		 */
       
 29939 		getLang: function(name, defaultVal) {
       
 29940 			return (
       
 29941 				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
       
 29942 				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
       
 29943 			);
       
 29944 		},
       
 29945 
       
 29946 		/**
       
 29947 		 * Returns a configuration parameter by name.
       
 29948 		 *
       
 29949 		 * @method getParam
       
 29950 		 * @param {String} name Configruation parameter to retrive.
       
 29951 		 * @param {String} defaultVal Optional default value to return.
       
 29952 		 * @param {String} type Optional type parameter.
       
 29953 		 * @return {String} Configuration parameter value or default value.
       
 29954 		 * @example
       
 29955 		 * // Returns a specific config value from the currently active editor
       
 29956 		 * var someval = tinymce.activeEditor.getParam('myvalue');
       
 29957 		 *
       
 29958 		 * // Returns a specific config value from a specific editor instance by id
       
 29959 		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
       
 29960 		 */
       
 29961 		getParam: function(name, defaultVal, type) {
       
 29962 			var value = name in this.settings ? this.settings[name] : defaultVal, output;
       
 29963 
       
 29964 			if (type === 'hash') {
       
 29965 				output = {};
       
 29966 
       
 29967 				if (typeof value === 'string') {
       
 29968 					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
       
 29969 						value = value.split('=');
       
 29970 
       
 29971 						if (value.length > 1) {
       
 29972 							output[trim(value[0])] = trim(value[1]);
       
 29973 						} else {
       
 29974 							output[trim(value[0])] = trim(value);
       
 29975 						}
       
 29976 					});
       
 29977 				} else {
       
 29978 					output = value;
       
 29979 				}
       
 29980 
       
 29981 				return output;
       
 29982 			}
       
 29983 
       
 29984 			return value;
       
 29985 		},
       
 29986 
       
 29987 		/**
       
 29988 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
       
 29989 		 * need to update the UI states or element path etc.
       
 29990 		 *
       
 29991 		 * @method nodeChanged
       
 29992 		 * @param {Object} args Optional args to pass to NodeChange event handlers.
       
 29993 		 */
       
 29994 		nodeChanged: function(args) {
       
 29995 			this._nodeChangeDispatcher.nodeChanged(args);
       
 29996 		},
       
 29997 
       
 29998 		/**
       
 29999 		 * Adds a button that later gets created by the theme in the editors toolbars.
       
 30000 		 *
       
 30001 		 * @method addButton
       
 30002 		 * @param {String} name Button name to add.
       
 30003 		 * @param {Object} settings Settings object with title, cmd etc.
       
 30004 		 * @example
       
 30005 		 * // Adds a custom button to the editor that inserts contents when clicked
       
 30006 		 * tinymce.init({
       
 30007 		 *    ...
       
 30008 		 *
       
 30009 		 *    toolbar: 'example'
       
 30010 		 *
       
 30011 		 *    setup: function(ed) {
       
 30012 		 *       ed.addButton('example', {
       
 30013 		 *          title: 'My title',
       
 30014 		 *          image: '../js/tinymce/plugins/example/img/example.gif',
       
 30015 		 *          onclick: function() {
       
 30016 		 *             ed.insertContent('Hello world!!');
       
 30017 		 *          }
       
 30018 		 *       });
       
 30019 		 *    }
       
 30020 		 * });
       
 30021 		 */
       
 30022 		addButton: function(name, settings) {
       
 30023 			var self = this;
       
 30024 
       
 30025 			if (settings.cmd) {
       
 30026 				settings.onclick = function() {
       
 30027 					self.execCommand(settings.cmd);
       
 30028 				};
       
 30029 			}
       
 30030 
       
 30031 			if (!settings.text && !settings.icon) {
       
 30032 				settings.icon = name;
       
 30033 			}
       
 30034 
       
 30035 			self.buttons = self.buttons || {};
       
 30036 			settings.tooltip = settings.tooltip || settings.title;
       
 30037 			self.buttons[name] = settings;
       
 30038 		},
       
 30039 
       
 30040 		/**
       
 30041 		 * Adds a menu item to be used in the menus of the theme. There might be multiple instances
       
 30042 		 * of this menu item for example it might be used in the main menus of the theme but also in
       
 30043 		 * the context menu so make sure that it's self contained and supports multiple instances.
       
 30044 		 *
       
 30045 		 * @method addMenuItem
       
 30046 		 * @param {String} name Menu item name to add.
       
 30047 		 * @param {Object} settings Settings object with title, cmd etc.
       
 30048 		 * @example
       
 30049 		 * // Adds a custom menu item to the editor that inserts contents when clicked
       
 30050 		 * // The context option allows you to add the menu item to an existing default menu
       
 30051 		 * tinymce.init({
       
 30052 		 *    ...
       
 30053 		 *
       
 30054 		 *    setup: function(ed) {
       
 30055 		 *       ed.addMenuItem('example', {
       
 30056 		 *          text: 'My menu item',
       
 30057 		 *          context: 'tools',
       
 30058 		 *          onclick: function() {
       
 30059 		 *             ed.insertContent('Hello world!!');
       
 30060 		 *          }
       
 30061 		 *       });
       
 30062 		 *    }
       
 30063 		 * });
       
 30064 		 */
       
 30065 		addMenuItem: function(name, settings) {
       
 30066 			var self = this;
       
 30067 
       
 30068 			if (settings.cmd) {
       
 30069 				settings.onclick = function() {
       
 30070 					self.execCommand(settings.cmd);
       
 30071 				};
       
 30072 			}
       
 30073 
       
 30074 			self.menuItems = self.menuItems || {};
       
 30075 			self.menuItems[name] = settings;
       
 30076 		},
       
 30077 
       
 30078 		/**
       
 30079 		 * Adds a custom command to the editor, you can also override existing commands with this method.
       
 30080 		 * The command that you add can be executed with execCommand.
       
 30081 		 *
       
 30082 		 * @method addCommand
       
 30083 		 * @param {String} name Command name to add/override.
       
 30084 		 * @param {addCommandCallback} callback Function to execute when the command occurs.
       
 30085 		 * @param {Object} scope Optional scope to execute the function in.
       
 30086 		 * @example
       
 30087 		 * // Adds a custom command that later can be executed using execCommand
       
 30088 		 * tinymce.init({
       
 30089 		 *    ...
       
 30090 		 *
       
 30091 		 *    setup: function(ed) {
       
 30092 		 *       // Register example command
       
 30093 		 *       ed.addCommand('mycommand', function(ui, v) {
       
 30094 		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
       
 30095 		 *       });
       
 30096 		 *    }
       
 30097 		 * });
       
 30098 		 */
       
 30099 		addCommand: function(name, callback, scope) {
       
 30100 			/**
       
 30101 			 * Callback function that gets called when a command is executed.
       
 30102 			 *
       
 30103 			 * @callback addCommandCallback
       
 30104 			 * @param {Boolean} ui Display UI state true/false.
       
 30105 			 * @param {Object} value Optional value for command.
       
 30106 			 * @return {Boolean} True/false state if the command was handled or not.
       
 30107 			 */
       
 30108 			this.editorCommands.addCommand(name, callback, scope);
       
 30109 		},
       
 30110 
       
 30111 		/**
       
 30112 		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
       
 30113 		 * The command that you add can be executed with queryCommandState function.
       
 30114 		 *
       
 30115 		 * @method addQueryStateHandler
       
 30116 		 * @param {String} name Command name to add/override.
       
 30117 		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
       
 30118 		 * @param {Object} scope Optional scope to execute the function in.
       
 30119 		 */
       
 30120 		addQueryStateHandler: function(name, callback, scope) {
       
 30121 			/**
       
 30122 			 * Callback function that gets called when a queryCommandState is executed.
       
 30123 			 *
       
 30124 			 * @callback addQueryStateHandlerCallback
       
 30125 			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
       
 30126 			 */
       
 30127 			this.editorCommands.addQueryStateHandler(name, callback, scope);
       
 30128 		},
       
 30129 
       
 30130 		/**
       
 30131 		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
       
 30132 		 * The command that you add can be executed with queryCommandValue function.
       
 30133 		 *
       
 30134 		 * @method addQueryValueHandler
       
 30135 		 * @param {String} name Command name to add/override.
       
 30136 		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
       
 30137 		 * @param {Object} scope Optional scope to execute the function in.
       
 30138 		 */
       
 30139 		addQueryValueHandler: function(name, callback, scope) {
       
 30140 			/**
       
 30141 			 * Callback function that gets called when a queryCommandValue is executed.
       
 30142 			 *
       
 30143 			 * @callback addQueryValueHandlerCallback
       
 30144 			 * @return {Object} Value of the command or undefined.
       
 30145 			 */
       
 30146 			this.editorCommands.addQueryValueHandler(name, callback, scope);
       
 30147 		},
       
 30148 
       
 30149 		/**
       
 30150 		 * Adds a keyboard shortcut for some command or function.
       
 30151 		 *
       
 30152 		 * @method addShortcut
       
 30153 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
 30154 		 * @param {String} desc Text description for the command.
       
 30155 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
       
 30156 		 * @param {Object} sc Optional scope to execute the function in.
       
 30157 		 * @return {Boolean} true/false state if the shortcut was added or not.
       
 30158 		 */
       
 30159 		addShortcut: function(pattern, desc, cmdFunc, scope) {
       
 30160 			this.shortcuts.add(pattern, desc, cmdFunc, scope);
       
 30161 		},
       
 30162 
       
 30163 		/**
       
 30164 		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
       
 30165 		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
       
 30166 		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
       
 30167 		 * return true it will handle the command as a internal browser command.
       
 30168 		 *
       
 30169 		 * @method execCommand
       
 30170 		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
       
 30171 		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
       
 30172 		 * @param {mixed} value Optional command value, this can be anything.
       
 30173 		 * @param {Object} args Optional arguments object.
       
 30174 		 */
       
 30175 		execCommand: function(cmd, ui, value, args) {
       
 30176 			return this.editorCommands.execCommand(cmd, ui, value, args);
       
 30177 		},
       
 30178 
       
 30179 		/**
       
 30180 		 * Returns a command specific state, for example if bold is enabled or not.
       
 30181 		 *
       
 30182 		 * @method queryCommandState
       
 30183 		 * @param {string} cmd Command to query state from.
       
 30184 		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
       
 30185 		 */
       
 30186 		queryCommandState: function(cmd) {
       
 30187 			return this.editorCommands.queryCommandState(cmd);
       
 30188 		},
       
 30189 
       
 30190 		/**
       
 30191 		 * Returns a command specific value, for example the current font size.
       
 30192 		 *
       
 30193 		 * @method queryCommandValue
       
 30194 		 * @param {string} cmd Command to query value from.
       
 30195 		 * @return {Object} Command specific value, for example the current font size.
       
 30196 		 */
       
 30197 		queryCommandValue: function(cmd) {
       
 30198 			return this.editorCommands.queryCommandValue(cmd);
       
 30199 		},
       
 30200 
       
 30201 		/**
       
 30202 		 * Returns true/false if the command is supported or not.
       
 30203 		 *
       
 30204 		 * @method queryCommandSupported
       
 30205 		 * @param {String} cmd Command that we check support for.
       
 30206 		 * @return {Boolean} true/false if the command is supported or not.
       
 30207 		 */
       
 30208 		queryCommandSupported: function(cmd) {
       
 30209 			return this.editorCommands.queryCommandSupported(cmd);
       
 30210 		},
       
 30211 
       
 30212 		/**
       
 30213 		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
       
 30214 		 *
       
 30215 		 * @method show
       
 30216 		 */
       
 30217 		show: function() {
       
 30218 			var self = this;
       
 30219 
       
 30220 			if (self.hidden) {
       
 30221 				self.hidden = false;
       
 30222 
       
 30223 				if (self.inline) {
       
 30224 					self.getBody().contentEditable = true;
       
 30225 				} else {
       
 30226 					DOM.show(self.getContainer());
       
 30227 					DOM.hide(self.id);
       
 30228 				}
       
 30229 
       
 30230 				self.load();
       
 30231 				self.fire('show');
       
 30232 			}
       
 30233 		},
       
 30234 
       
 30235 		/**
       
 30236 		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
       
 30237 		 *
       
 30238 		 * @method hide
       
 30239 		 */
       
 30240 		hide: function() {
       
 30241 			var self = this, doc = self.getDoc();
       
 30242 
       
 30243 			if (!self.hidden) {
       
 30244 				// Fixed bug where IE has a blinking cursor left from the editor
       
 30245 				if (ie && doc && !self.inline) {
       
 30246 					doc.execCommand('SelectAll');
       
 30247 				}
       
 30248 
       
 30249 				// We must save before we hide so Safari doesn't crash
       
 30250 				self.save();
       
 30251 
       
 30252 				if (self.inline) {
       
 30253 					self.getBody().contentEditable = false;
       
 30254 
       
 30255 					// Make sure the editor gets blurred
       
 30256 					if (self == self.editorManager.focusedEditor) {
       
 30257 						self.editorManager.focusedEditor = null;
       
 30258 					}
       
 30259 				} else {
       
 30260 					DOM.hide(self.getContainer());
       
 30261 					DOM.setStyle(self.id, 'display', self.orgDisplay);
       
 30262 				}
       
 30263 
       
 30264 				self.hidden = true;
       
 30265 				self.fire('hide');
       
 30266 			}
       
 30267 		},
       
 30268 
       
 30269 		/**
       
 30270 		 * Returns true/false if the editor is hidden or not.
       
 30271 		 *
       
 30272 		 * @method isHidden
       
 30273 		 * @return {Boolean} True/false if the editor is hidden or not.
       
 30274 		 */
       
 30275 		isHidden: function() {
       
 30276 			return !!this.hidden;
       
 30277 		},
       
 30278 
       
 30279 		/**
       
 30280 		 * Sets the progress state, this will display a throbber/progess for the editor.
       
 30281 		 * This is ideal for asycronous operations like an AJAX save call.
       
 30282 		 *
       
 30283 		 * @method setProgressState
       
 30284 		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
       
 30285 		 * @param {Number} time Optional time to wait before the progress gets shown.
       
 30286 		 * @return {Boolean} Same as the input state.
       
 30287 		 * @example
       
 30288 		 * // Show progress for the active editor
       
 30289 		 * tinymce.activeEditor.setProgressState(true);
       
 30290 		 *
       
 30291 		 * // Hide progress for the active editor
       
 30292 		 * tinymce.activeEditor.setProgressState(false);
       
 30293 		 *
       
 30294 		 * // Show progress after 3 seconds
       
 30295 		 * tinymce.activeEditor.setProgressState(true, 3000);
       
 30296 		 */
       
 30297 		setProgressState: function(state, time) {
       
 30298 			this.fire('ProgressState', {state: state, time: time});
       
 30299 		},
       
 30300 
       
 30301 		/**
       
 30302 		 * Loads contents from the textarea or div element that got converted into an editor instance.
       
 30303 		 * This method will move the contents from that textarea or div into the editor by using setContent
       
 30304 		 * so all events etc that method has will get dispatched as well.
       
 30305 		 *
       
 30306 		 * @method load
       
 30307 		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
       
 30308 		 * @return {String} HTML string that got set into the editor.
       
 30309 		 */
       
 30310 		load: function(args) {
       
 30311 			var self = this, elm = self.getElement(), html;
       
 30312 
       
 30313 			if (elm) {
       
 30314 				args = args || {};
       
 30315 				args.load = true;
       
 30316 
       
 30317 				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
       
 30318 				args.element = elm;
       
 30319 
       
 30320 				if (!args.no_events) {
       
 30321 					self.fire('LoadContent', args);
       
 30322 				}
       
 30323 
       
 30324 				args.element = elm = null;
       
 30325 
       
 30326 				return html;
       
 30327 			}
       
 30328 		},
       
 30329 
       
 30330 		/**
       
 30331 		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
       
 30332 		 * This method will move the HTML contents from the editor into that textarea or div by getContent
       
 30333 		 * so all events etc that method has will get dispatched as well.
       
 30334 		 *
       
 30335 		 * @method save
       
 30336 		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
       
 30337 		 * @return {String} HTML string that got set into the textarea/div.
       
 30338 		 */
       
 30339 		save: function(args) {
       
 30340 			var self = this, elm = self.getElement(), html, form;
       
 30341 
       
 30342 			if (!elm || !self.initialized) {
       
 30343 				return;
       
 30344 			}
       
 30345 
       
 30346 			args = args || {};
       
 30347 			args.save = true;
       
 30348 
       
 30349 			args.element = elm;
       
 30350 			html = args.content = self.getContent(args);
       
 30351 
       
 30352 			if (!args.no_events) {
       
 30353 				self.fire('SaveContent', args);
       
 30354 			}
       
 30355 
       
 30356 			html = args.content;
       
 30357 
       
 30358 			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
       
 30359 				// Update DIV element when not in inline mode
       
 30360 				if (!self.inline) {
       
 30361 					elm.innerHTML = html;
       
 30362 				}
       
 30363 
       
 30364 				// Update hidden form element
       
 30365 				if ((form = DOM.getParent(self.id, 'form'))) {
       
 30366 					each(form.elements, function(elm) {
       
 30367 						if (elm.name == self.id) {
       
 30368 							elm.value = html;
       
 30369 							return false;
       
 30370 						}
       
 30371 					});
       
 30372 				}
       
 30373 			} else {
       
 30374 				elm.value = html;
       
 30375 			}
       
 30376 
       
 30377 			args.element = elm = null;
       
 30378 
       
 30379 			if (args.set_dirty !== false) {
       
 30380 				self.isNotDirty = true;
       
 30381 			}
       
 30382 
       
 30383 			return html;
       
 30384 		},
       
 30385 
       
 30386 		/**
       
 30387 		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
       
 30388 		 * the different cleanup rules options.
       
 30389 		 *
       
 30390 		 * @method setContent
       
 30391 		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
       
 30392 		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
       
 30393 		 * @return {String} HTML string that got set into the editor.
       
 30394 		 * @example
       
 30395 		 * // Sets the HTML contents of the activeEditor editor
       
 30396 		 * tinymce.activeEditor.setContent('<span>some</span> html');
       
 30397 		 *
       
 30398 		 * // Sets the raw contents of the activeEditor editor
       
 30399 		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
       
 30400 		 *
       
 30401 		 * // Sets the content of a specific editor (my_editor in this example)
       
 30402 		 * tinymce.get('my_editor').setContent(data);
       
 30403 		 *
       
 30404 		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
       
 30405 		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
       
 30406 		 */
       
 30407 		setContent: function(content, args) {
       
 30408 			var self = this, body = self.getBody(), forcedRootBlockName;
       
 30409 
       
 30410 			// Setup args object
       
 30411 			args = args || {};
       
 30412 			args.format = args.format || 'html';
       
 30413 			args.set = true;
       
 30414 			args.content = content;
       
 30415 
       
 30416 			// Do preprocessing
       
 30417 			if (!args.no_events) {
       
 30418 				self.fire('BeforeSetContent', args);
       
 30419 			}
       
 30420 
       
 30421 			content = args.content;
       
 30422 
       
 30423 			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
       
 30424 			// It will also be impossible to place the caret in the editor unless there is a BR element present
       
 30425 			if (content.length === 0 || /^\s+$/.test(content)) {
       
 30426 				forcedRootBlockName = self.settings.forced_root_block;
       
 30427 
       
 30428 				// Check if forcedRootBlock is configured and that the block is a valid child of the body
       
 30429 				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
       
 30430 					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
       
 30431 					content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
       
 30432 					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
       
 30433 				} else if (!ie) {
       
 30434 					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
       
 30435 					content = '<br data-mce-bogus="1">';
       
 30436 				}
       
 30437 
       
 30438 				self.dom.setHTML(body, content);
       
 30439 
       
 30440 				self.fire('SetContent', args);
       
 30441 			} else {
       
 30442 				// Parse and serialize the html
       
 30443 				if (args.format !== 'raw') {
       
 30444 					content = new Serializer({}, self.schema).serialize(
       
 30445 						self.parser.parse(content, {isRootContent: true})
       
 30446 					);
       
 30447 				}
       
 30448 
       
 30449 				// Set the new cleaned contents to the editor
       
 30450 				args.content = trim(content);
       
 30451 				self.dom.setHTML(body, args.content);
       
 30452 
       
 30453 				// Do post processing
       
 30454 				if (!args.no_events) {
       
 30455 					self.fire('SetContent', args);
       
 30456 				}
       
 30457 
       
 30458 				// Don't normalize selection if the focused element isn't the body in
       
 30459 				// content editable mode since it will steal focus otherwise
       
 30460 				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
       
 30461 					self.selection.normalize();
       
 30462 				}*/
       
 30463 			}
       
 30464 
       
 30465 			return args.content;
       
 30466 		},
       
 30467 
       
 30468 		/**
       
 30469 		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
       
 30470 		 * the different cleanup rules options.
       
 30471 		 *
       
 30472 		 * @method getContent
       
 30473 		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
       
 30474 		 * @return {String} Cleaned content string, normally HTML contents.
       
 30475 		 * @example
       
 30476 		 * // Get the HTML contents of the currently active editor
       
 30477 		 * console.debug(tinymce.activeEditor.getContent());
       
 30478 		 *
       
 30479 		 * // Get the raw contents of the currently active editor
       
 30480 		 * tinymce.activeEditor.getContent({format: 'raw'});
       
 30481 		 *
       
 30482 		 * // Get content of a specific editor:
       
 30483 		 * tinymce.get('content id').getContent()
       
 30484 		 */
       
 30485 		getContent: function(args) {
       
 30486 			var self = this, content, body = self.getBody();
       
 30487 
       
 30488 			// Setup args object
       
 30489 			args = args || {};
       
 30490 			args.format = args.format || 'html';
       
 30491 			args.get = true;
       
 30492 			args.getInner = true;
       
 30493 
       
 30494 			// Do preprocessing
       
 30495 			if (!args.no_events) {
       
 30496 				self.fire('BeforeGetContent', args);
       
 30497 			}
       
 30498 
       
 30499 			// Get raw contents or by default the cleaned contents
       
 30500 			if (args.format == 'raw') {
       
 30501 				content = body.innerHTML;
       
 30502 			} else if (args.format == 'text') {
       
 30503 				content = body.innerText || body.textContent;
       
 30504 			} else {
       
 30505 				content = self.serializer.serialize(body, args);
       
 30506 			}
       
 30507 
       
 30508 			// Trim whitespace in beginning/end of HTML
       
 30509 			if (args.format != 'text') {
       
 30510 				args.content = trim(content);
       
 30511 			} else {
       
 30512 				args.content = content;
       
 30513 			}
       
 30514 
       
 30515 			// Do post processing
       
 30516 			if (!args.no_events) {
       
 30517 				self.fire('GetContent', args);
       
 30518 			}
       
 30519 
       
 30520 			return args.content;
       
 30521 		},
       
 30522 
       
 30523 		/**
       
 30524 		 * Inserts content at caret position.
       
 30525 		 *
       
 30526 		 * @method insertContent
       
 30527 		 * @param {String} content Content to insert.
       
 30528 		 * @param {Object} args Optional args to pass to insert call.
       
 30529 		 */
       
 30530 		insertContent: function(content, args) {
       
 30531 			if (args) {
       
 30532 				content = extend({content: content}, args);
       
 30533 			}
       
 30534 
       
 30535 			this.execCommand('mceInsertContent', false, content);
       
 30536 		},
       
 30537 
       
 30538 		/**
       
 30539 		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
       
 30540 		 *
       
 30541 		 * @method isDirty
       
 30542 		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
       
 30543 		 * @example
       
 30544 		 * if (tinymce.activeEditor.isDirty())
       
 30545 		 *     alert("You must save your contents.");
       
 30546 		 */
       
 30547 		isDirty: function() {
       
 30548 			return !this.isNotDirty;
       
 30549 		},
       
 30550 
       
 30551 		/**
       
 30552 		 * Returns the editors container element. The container element wrappes in
       
 30553 		 * all the elements added to the page for the editor. Such as UI, iframe etc.
       
 30554 		 *
       
 30555 		 * @method getContainer
       
 30556 		 * @return {Element} HTML DOM element for the editor container.
       
 30557 		 */
       
 30558 		getContainer: function() {
       
 30559 			var self = this;
       
 30560 
       
 30561 			if (!self.container) {
       
 30562 				self.container = DOM.get(self.editorContainer || self.id + '_parent');
       
 30563 			}
       
 30564 
       
 30565 			return self.container;
       
 30566 		},
       
 30567 
       
 30568 		/**
       
 30569 		 * Returns the editors content area container element. The this element is the one who
       
 30570 		 * holds the iframe or the editable element.
       
 30571 		 *
       
 30572 		 * @method getContentAreaContainer
       
 30573 		 * @return {Element} HTML DOM element for the editor area container.
       
 30574 		 */
       
 30575 		getContentAreaContainer: function() {
       
 30576 			return this.contentAreaContainer;
       
 30577 		},
       
 30578 
       
 30579 		/**
       
 30580 		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
       
 30581 		 *
       
 30582 		 * @method getElement
       
 30583 		 * @return {Element} HTML DOM element for the replaced element.
       
 30584 		 */
       
 30585 		getElement: function() {
       
 30586 			if (!this.targetElm) {
       
 30587 				this.targetElm = DOM.get(this.id);
       
 30588 			}
       
 30589 
       
 30590 			return this.targetElm;
       
 30591 		},
       
 30592 
       
 30593 		/**
       
 30594 		 * Returns the iframes window object.
       
 30595 		 *
       
 30596 		 * @method getWin
       
 30597 		 * @return {Window} Iframe DOM window object.
       
 30598 		 */
       
 30599 		getWin: function() {
       
 30600 			var self = this, elm;
       
 30601 
       
 30602 			if (!self.contentWindow) {
       
 30603 				elm = self.iframeElement;
       
 30604 
       
 30605 				if (elm) {
       
 30606 					self.contentWindow = elm.contentWindow;
       
 30607 				}
       
 30608 			}
       
 30609 
       
 30610 			return self.contentWindow;
       
 30611 		},
       
 30612 
       
 30613 		/**
       
 30614 		 * Returns the iframes document object.
       
 30615 		 *
       
 30616 		 * @method getDoc
       
 30617 		 * @return {Document} Iframe DOM document object.
       
 30618 		 */
       
 30619 		getDoc: function() {
       
 30620 			var self = this, win;
       
 30621 
       
 30622 			if (!self.contentDocument) {
       
 30623 				win = self.getWin();
       
 30624 
       
 30625 				if (win) {
       
 30626 					self.contentDocument = win.document;
       
 30627 				}
       
 30628 			}
       
 30629 
       
 30630 			return self.contentDocument;
       
 30631 		},
       
 30632 
       
 30633 		/**
       
 30634 		 * Returns the root element of the editable area.
       
 30635 		 * For a non-inline iframe-based editor, returns the iframe's body element.
       
 30636 		 *
       
 30637 		 * @method getBody
       
 30638 		 * @return {Element} The root element of the editable area.
       
 30639 		 */
       
 30640 		getBody: function() {
       
 30641 			return this.bodyElement || this.getDoc().body;
       
 30642 		},
       
 30643 
       
 30644 		/**
       
 30645 		 * URL converter function this gets executed each time a user adds an img, a or
       
 30646 		 * any other element that has a URL in it. This will be called both by the DOM and HTML
       
 30647 		 * manipulation functions.
       
 30648 		 *
       
 30649 		 * @method convertURL
       
 30650 		 * @param {string} url URL to convert.
       
 30651 		 * @param {string} name Attribute name src, href etc.
       
 30652 		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
       
 30653 		 * @return {string} Converted URL string.
       
 30654 		 */
       
 30655 		convertURL: function(url, name, elm) {
       
 30656 			var self = this, settings = self.settings;
       
 30657 
       
 30658 			// Use callback instead
       
 30659 			if (settings.urlconverter_callback) {
       
 30660 				return self.execCallback('urlconverter_callback', url, elm, true, name);
       
 30661 			}
       
 30662 
       
 30663 			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
       
 30664 			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
       
 30665 				return url;
       
 30666 			}
       
 30667 
       
 30668 			// Convert to relative
       
 30669 			if (settings.relative_urls) {
       
 30670 				return self.documentBaseURI.toRelative(url);
       
 30671 			}
       
 30672 
       
 30673 			// Convert to absolute
       
 30674 			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
       
 30675 
       
 30676 			return url;
       
 30677 		},
       
 30678 
       
 30679 		/**
       
 30680 		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
       
 30681 		 *
       
 30682 		 * @method addVisual
       
 30683 		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
       
 30684 		 */
       
 30685 		addVisual: function(elm) {
       
 30686 			var self = this, settings = self.settings, dom = self.dom, cls;
       
 30687 
       
 30688 			elm = elm || self.getBody();
       
 30689 
       
 30690 			if (self.hasVisual === undefined) {
       
 30691 				self.hasVisual = settings.visual;
       
 30692 			}
       
 30693 
       
 30694 			each(dom.select('table,a', elm), function(elm) {
       
 30695 				var value;
       
 30696 
       
 30697 				switch (elm.nodeName) {
       
 30698 					case 'TABLE':
       
 30699 						cls = settings.visual_table_class || 'mce-item-table';
       
 30700 						value = dom.getAttrib(elm, 'border');
       
 30701 
       
 30702 						if ((!value || value == '0') && self.hasVisual) {
       
 30703 							dom.addClass(elm, cls);
       
 30704 						} else {
       
 30705 							dom.removeClass(elm, cls);
       
 30706 						}
       
 30707 
       
 30708 						return;
       
 30709 
       
 30710 					case 'A':
       
 30711 						if (!dom.getAttrib(elm, 'href', false)) {
       
 30712 							value = dom.getAttrib(elm, 'name') || elm.id;
       
 30713 							cls = settings.visual_anchor_class || 'mce-item-anchor';
       
 30714 
       
 30715 							if (value && self.hasVisual) {
       
 30716 								dom.addClass(elm, cls);
       
 30717 							} else {
       
 30718 								dom.removeClass(elm, cls);
       
 30719 							}
       
 30720 						}
       
 30721 
       
 30722 						return;
       
 30723 				}
       
 30724 			});
       
 30725 
       
 30726 			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
       
 30727 		},
       
 30728 
       
 30729 		/**
       
 30730 		 * Removes the editor from the dom and tinymce collection.
       
 30731 		 *
       
 30732 		 * @method remove
       
 30733 		 */
       
 30734 		remove: function() {
       
 30735 			var self = this;
       
 30736 
       
 30737 			if (!self.removed) {
       
 30738 				self.save();
       
 30739 				self.removed = 1;
       
 30740 				self.unbindAllNativeEvents();
       
 30741 
       
 30742 				// Remove any hidden input
       
 30743 				if (self.hasHiddenInput) {
       
 30744 					DOM.remove(self.getElement().nextSibling);
       
 30745 				}
       
 30746 
       
 30747 				if (!self.inline) {
       
 30748 					// IE 9 has a bug where the selection stops working if you place the
       
 30749 					// caret inside the editor then remove the iframe
       
 30750 					if (ie && ie < 10) {
       
 30751 						self.getDoc().execCommand('SelectAll', false, null);
       
 30752 					}
       
 30753 
       
 30754 					DOM.setStyle(self.id, 'display', self.orgDisplay);
       
 30755 					self.getBody().onload = null; // Prevent #6816
       
 30756 				}
       
 30757 
       
 30758 				self.fire('remove');
       
 30759 
       
 30760 				self.editorManager.remove(self);
       
 30761 				DOM.remove(self.getContainer());
       
 30762 				self.destroy();
       
 30763 			}
       
 30764 		},
       
 30765 
       
 30766 		/**
       
 30767 		 * Destroys the editor instance by removing all events, element references or other resources
       
 30768 		 * that could leak memory. This method will be called automatically when the page is unloaded
       
 30769 		 * but you can also call it directly if you know what you are doing.
       
 30770 		 *
       
 30771 		 * @method destroy
       
 30772 		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
       
 30773 		 */
       
 30774 		destroy: function(automatic) {
       
 30775 			var self = this, form;
       
 30776 
       
 30777 			// One time is enough
       
 30778 			if (self.destroyed) {
       
 30779 				return;
       
 30780 			}
       
 30781 
       
 30782 			// If user manually calls destroy and not remove
       
 30783 			// Users seems to have logic that calls destroy instead of remove
       
 30784 			if (!automatic && !self.removed) {
       
 30785 				self.remove();
       
 30786 				return;
       
 30787 			}
       
 30788 
       
 30789 			if (!automatic) {
       
 30790 				self.editorManager.off('beforeunload', self._beforeUnload);
       
 30791 
       
 30792 				// Manual destroy
       
 30793 				if (self.theme && self.theme.destroy) {
       
 30794 					self.theme.destroy();
       
 30795 				}
       
 30796 
       
 30797 				// Destroy controls, selection and dom
       
 30798 				self.selection.destroy();
       
 30799 				self.dom.destroy();
       
 30800 			}
       
 30801 
       
 30802 			form = self.formElement;
       
 30803 			if (form) {
       
 30804 				if (form._mceOldSubmit) {
       
 30805 					form.submit = form._mceOldSubmit;
       
 30806 					form._mceOldSubmit = null;
       
 30807 				}
       
 30808 
       
 30809 				DOM.unbind(form, 'submit reset', self.formEventDelegate);
       
 30810 			}
       
 30811 
       
 30812 			self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null;
       
 30813 			self.bodyElement = self.contentDocument = self.contentWindow = null;
       
 30814 			self.iframeElement = self.targetElm = null;
       
 30815 
       
 30816 			if (self.selection) {
       
 30817 				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
       
 30818 			}
       
 30819 
       
 30820 			self.destroyed = 1;
       
 30821 		},
       
 30822 
       
 30823 		// Internal functions
       
 30824 
       
 30825 		_refreshContentEditable: function() {
       
 30826 			var self = this, body, parent;
       
 30827 
       
 30828 			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
       
 30829 			if (self._isHidden()) {
       
 30830 				body = self.getBody();
       
 30831 				parent = body.parentNode;
       
 30832 
       
 30833 				parent.removeChild(body);
       
 30834 				parent.appendChild(body);
       
 30835 
       
 30836 				body.focus();
       
 30837 			}
       
 30838 		},
       
 30839 
       
 30840 		_isHidden: function() {
       
 30841 			var sel;
       
 30842 
       
 30843 			if (!isGecko) {
       
 30844 				return 0;
       
 30845 			}
       
 30846 
       
 30847 			// Weird, wheres that cursor selection?
       
 30848 			sel = this.selection.getSel();
       
 30849 			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
       
 30850 		}
       
 30851 	};
       
 30852 
       
 30853 	extend(Editor.prototype, EditorObservable);
       
 30854 
       
 30855 	return Editor;
       
 30856 });
       
 30857 
       
 30858 // Included from: js/tinymce/classes/util/I18n.js
       
 30859 
       
 30860 /**
       
 30861  * I18n.js
       
 30862  *
       
 30863  * Copyright, Moxiecode Systems AB
       
 30864  * Released under LGPL License.
       
 30865  *
       
 30866  * License: http://www.tinymce.com/license
       
 30867  * Contributing: http://www.tinymce.com/contributing
       
 30868  */
       
 30869 
       
 30870 /**
       
 30871  * I18n class that handles translation of TinyMCE UI.
       
 30872  * Uses po style with csharp style parameters.
       
 30873  *
       
 30874  * @class tinymce.util.I18n
       
 30875  */
       
 30876 define("tinymce/util/I18n", [], function() {
       
 30877 	"use strict";
       
 30878 
       
 30879 	var data = {}, code = "en";
       
 30880 
       
 30881 	return {
       
 30882 		/**
       
 30883 		 * Sets the current language code.
       
 30884 		 *
       
 30885 		 * @method setCode
       
 30886 		 * @param {String} newCode Current language code.
       
 30887 		 */
       
 30888 		setCode: function(newCode) {
       
 30889 			if (newCode) {
       
 30890 				code = newCode;
       
 30891 				this.rtl = this.data[newCode] ? this.data[newCode]._dir === 'rtl' : false;
       
 30892 			}
       
 30893 		},
       
 30894 
       
 30895 		/**
       
 30896 		 * Returns the current language code.
       
 30897 		 *
       
 30898 		 * @return {String} Current language code.
       
 30899 		 */
       
 30900 		getCode: function() {
       
 30901 			return code;
       
 30902 		},
       
 30903 
       
 30904 		/**
       
 30905 		 * Property gets set to true if a RTL language pack was loaded.
       
 30906 		 *
       
 30907 		 * @property rtl
       
 30908 		 * @type Boolean
       
 30909 		 */
       
 30910 		rtl: false,
       
 30911 
       
 30912 		/**
       
 30913 		 * Adds translations for a specific language code.
       
 30914 		 *
       
 30915 		 * @method add
       
 30916 		 * @param {String} code Language code like sv_SE.
       
 30917 		 * @param {Array} items Name/value array with English en_US to sv_SE.
       
 30918 		 */
       
 30919 		add: function(code, items) {
       
 30920 			var langData = data[code];
       
 30921 
       
 30922 			if (!langData) {
       
 30923 				data[code] = langData = {};
       
 30924 			}
       
 30925 
       
 30926 			for (var name in items) {
       
 30927 				langData[name] = items[name];
       
 30928 			}
       
 30929 
       
 30930 			this.setCode(code);
       
 30931 		},
       
 30932 
       
 30933 		/**
       
 30934 		 * Translates the specified text.
       
 30935 		 *
       
 30936 		 * It has a few formats:
       
 30937 		 * I18n.translate("Text");
       
 30938 		 * I18n.translate(["Text {0}/{1}", 0, 1]);
       
 30939 		 * I18n.translate({raw: "Raw string"});
       
 30940 		 *
       
 30941 		 * @method translate
       
 30942 		 * @param {String/Object/Array} text Text to translate.
       
 30943 		 * @return {String} String that got translated.
       
 30944 		 */
       
 30945 		translate: function(text) {
       
 30946 			var langData;
       
 30947 
       
 30948 			langData = data[code];
       
 30949 			if (!langData) {
       
 30950 				langData = {};
       
 30951 			}
       
 30952 
       
 30953 			if (typeof text == "undefined") {
       
 30954 				return text;
       
 30955 			}
       
 30956 
       
 30957 			if (typeof text != "string" && text.raw) {
       
 30958 				return text.raw;
       
 30959 			}
       
 30960 
       
 30961 			if (text.push) {
       
 30962 				var values = text.slice(1);
       
 30963 
       
 30964 				text = (langData[text[0]] || text[0]).replace(/\{([0-9]+)\}/g, function(match1, match2) {
       
 30965 					return values[match2];
       
 30966 				});
       
 30967 			}
       
 30968 
       
 30969 			return (langData[text] || text).replace(/{context:\w+}$/, '');
       
 30970 		},
       
 30971 
       
 30972 		data: data
       
 30973 	};
       
 30974 });
       
 30975 
       
 30976 // Included from: js/tinymce/classes/FocusManager.js
       
 30977 
       
 30978 /**
       
 30979  * FocusManager.js
       
 30980  *
       
 30981  * Copyright, Moxiecode Systems AB
       
 30982  * Released under LGPL License.
       
 30983  *
       
 30984  * License: http://www.tinymce.com/license
       
 30985  * Contributing: http://www.tinymce.com/contributing
       
 30986  */
       
 30987 
       
 30988 /**
       
 30989  * This class manages the focus/blur state of the editor. This class is needed since some
       
 30990  * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
       
 30991  *
       
 30992  * This class will fire two events focus and blur on the editor instances that got affected.
       
 30993  * It will also handle the restore of selection when the focus is lost and returned.
       
 30994  *
       
 30995  * @class tinymce.FocusManager
       
 30996  */
       
 30997 define("tinymce/FocusManager", [
       
 30998 	"tinymce/dom/DOMUtils",
       
 30999 	"tinymce/Env"
       
 31000 ], function(DOMUtils, Env) {
       
 31001 	var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM;
       
 31002 
       
 31003 	/**
       
 31004 	 * Constructs a new focus manager instance.
       
 31005 	 *
       
 31006 	 * @constructor FocusManager
       
 31007 	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
       
 31008 	 */
       
 31009 	function FocusManager(editorManager) {
       
 31010 		function getActiveElement() {
       
 31011 			try {
       
 31012 				return document.activeElement;
       
 31013 			} catch (ex) {
       
 31014 				// IE sometimes fails to get the activeElement when resizing table
       
 31015 				// TODO: Investigate this
       
 31016 				return document.body;
       
 31017 			}
       
 31018 		}
       
 31019 
       
 31020 		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
       
 31021 		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
       
 31022 		function createBookmark(dom, rng) {
       
 31023 			if (rng && rng.startContainer) {
       
 31024 				// Verify that the range is within the root of the editor
       
 31025 				if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) {
       
 31026 					return;
       
 31027 				}
       
 31028 
       
 31029 				return {
       
 31030 					startContainer: rng.startContainer,
       
 31031 					startOffset: rng.startOffset,
       
 31032 					endContainer: rng.endContainer,
       
 31033 					endOffset: rng.endOffset
       
 31034 				};
       
 31035 			}
       
 31036 
       
 31037 			return rng;
       
 31038 		}
       
 31039 
       
 31040 		function bookmarkToRng(editor, bookmark) {
       
 31041 			var rng;
       
 31042 
       
 31043 			if (bookmark.startContainer) {
       
 31044 				rng = editor.getDoc().createRange();
       
 31045 				rng.setStart(bookmark.startContainer, bookmark.startOffset);
       
 31046 				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
       
 31047 			} else {
       
 31048 				rng = bookmark;
       
 31049 			}
       
 31050 
       
 31051 			return rng;
       
 31052 		}
       
 31053 
       
 31054 		function isUIElement(elm) {
       
 31055 			return !!DOM.getParent(elm, FocusManager.isEditorUIElement);
       
 31056 		}
       
 31057 
       
 31058 		function registerEvents(e) {
       
 31059 			var editor = e.editor;
       
 31060 
       
 31061 			editor.on('init', function() {
       
 31062 				// Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab
       
 31063 				if (editor.inline || Env.ie) {
       
 31064 					// Use the onbeforedeactivate event when available since it works better see #7023
       
 31065 					if ("onbeforedeactivate" in document && Env.ie < 9) {
       
 31066 						editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) {
       
 31067 							if (e.target != editor.getBody()) {
       
 31068 								return;
       
 31069 							}
       
 31070 
       
 31071 							try {
       
 31072 								editor.lastRng = editor.selection.getRng();
       
 31073 							} catch (ex) {
       
 31074 								// IE throws "Unexcpected call to method or property access" some times so lets ignore it
       
 31075 							}
       
 31076 						});
       
 31077 					} else {
       
 31078 						// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
       
 31079 						editor.on('nodechange mouseup keyup', function(e) {
       
 31080 							var node = getActiveElement();
       
 31081 
       
 31082 							// Only act on manual nodechanges
       
 31083 							if (e.type == 'nodechange' && e.selectionChange) {
       
 31084 								return;
       
 31085 							}
       
 31086 
       
 31087 							// IE 11 reports active element as iframe not body of iframe
       
 31088 							if (node && node.id == editor.id + '_ifr') {
       
 31089 								node = editor.getBody();
       
 31090 							}
       
 31091 
       
 31092 							if (editor.dom.isChildOf(node, editor.getBody())) {
       
 31093 								editor.lastRng = editor.selection.getRng();
       
 31094 							}
       
 31095 						});
       
 31096 					}
       
 31097 
       
 31098 					// Handles the issue with WebKit not retaining selection within inline document
       
 31099 					// If the user releases the mouse out side the body since a mouse up event wont occur on the body
       
 31100 					if (Env.webkit && !selectionChangeHandler) {
       
 31101 						selectionChangeHandler = function() {
       
 31102 							var activeEditor = editorManager.activeEditor;
       
 31103 
       
 31104 							if (activeEditor && activeEditor.selection) {
       
 31105 								var rng = activeEditor.selection.getRng();
       
 31106 
       
 31107 								// Store when it's non collapsed
       
 31108 								if (rng && !rng.collapsed) {
       
 31109 									editor.lastRng = rng;
       
 31110 								}
       
 31111 							}
       
 31112 						};
       
 31113 
       
 31114 						DOM.bind(document, 'selectionchange', selectionChangeHandler);
       
 31115 					}
       
 31116 				}
       
 31117 			});
       
 31118 
       
 31119 			editor.on('setcontent', function() {
       
 31120 				editor.lastRng = null;
       
 31121 			});
       
 31122 
       
 31123 			// Remove last selection bookmark on mousedown see #6305
       
 31124 			editor.on('mousedown', function() {
       
 31125 				editor.selection.lastFocusBookmark = null;
       
 31126 			});
       
 31127 
       
 31128 			editor.on('focusin', function() {
       
 31129 				var focusedEditor = editorManager.focusedEditor;
       
 31130 
       
 31131 				if (editor.selection.lastFocusBookmark) {
       
 31132 					editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
       
 31133 					editor.selection.lastFocusBookmark = null;
       
 31134 				}
       
 31135 
       
 31136 				if (focusedEditor != editor) {
       
 31137 					if (focusedEditor) {
       
 31138 						focusedEditor.fire('blur', {focusedEditor: editor});
       
 31139 					}
       
 31140 
       
 31141 					editorManager.setActive(editor);
       
 31142 					editorManager.focusedEditor = editor;
       
 31143 					editor.fire('focus', {blurredEditor: focusedEditor});
       
 31144 					editor.focus(true);
       
 31145 				}
       
 31146 
       
 31147 				editor.lastRng = null;
       
 31148 			});
       
 31149 
       
 31150 			editor.on('focusout', function() {
       
 31151 				window.setTimeout(function() {
       
 31152 					var focusedEditor = editorManager.focusedEditor;
       
 31153 
       
 31154 					// Still the same editor the the blur was outside any editor UI
       
 31155 					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
       
 31156 						editor.fire('blur', {focusedEditor: null});
       
 31157 						editorManager.focusedEditor = null;
       
 31158 
       
 31159 						// Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs
       
 31160 						if (editor.selection) {
       
 31161 							editor.selection.lastFocusBookmark = null;
       
 31162 						}
       
 31163 					}
       
 31164 				}, 0);
       
 31165 			});
       
 31166 
       
 31167 			// Check if focus is moved to an element outside the active editor by checking if the target node
       
 31168 			// isn't within the body of the activeEditor nor a UI element such as a dialog child control
       
 31169 			if (!documentFocusInHandler) {
       
 31170 				documentFocusInHandler = function(e) {
       
 31171 					var activeEditor = editorManager.activeEditor;
       
 31172 
       
 31173 					if (activeEditor && e.target.ownerDocument == document) {
       
 31174 						// Check to make sure we have a valid selection don't update the bookmark if it's
       
 31175 						// a focusin to the body of the editor see #7025
       
 31176 						if (activeEditor.selection && e.target != activeEditor.getBody()) {
       
 31177 							activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng);
       
 31178 						}
       
 31179 
       
 31180 						// Fire a blur event if the element isn't a UI element
       
 31181 						if (e.target != document.body && !isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
       
 31182 							activeEditor.fire('blur', {focusedEditor: null});
       
 31183 							editorManager.focusedEditor = null;
       
 31184 						}
       
 31185 					}
       
 31186 				};
       
 31187 
       
 31188 				DOM.bind(document, 'focusin', documentFocusInHandler);
       
 31189 			}
       
 31190 
       
 31191 			// Handle edge case when user starts the selection inside the editor and releases
       
 31192 			// the mouse outside the editor producing a new selection. This weird workaround is needed since
       
 31193 			// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
       
 31194 			if (editor.inline && !documentMouseUpHandler) {
       
 31195 				documentMouseUpHandler = function(e) {
       
 31196 					var activeEditor = editorManager.activeEditor;
       
 31197 
       
 31198 					if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
       
 31199 						var rng = activeEditor.selection.getRng();
       
 31200 
       
 31201 						if (!rng.collapsed) {
       
 31202 							activeEditor.lastRng = rng;
       
 31203 						}
       
 31204 					}
       
 31205 				};
       
 31206 
       
 31207 				DOM.bind(document, 'mouseup', documentMouseUpHandler);
       
 31208 			}
       
 31209 		}
       
 31210 
       
 31211 		function unregisterDocumentEvents(e) {
       
 31212 			if (editorManager.focusedEditor == e.editor) {
       
 31213 				editorManager.focusedEditor = null;
       
 31214 			}
       
 31215 
       
 31216 			if (!editorManager.activeEditor) {
       
 31217 				DOM.unbind(document, 'selectionchange', selectionChangeHandler);
       
 31218 				DOM.unbind(document, 'focusin', documentFocusInHandler);
       
 31219 				DOM.unbind(document, 'mouseup', documentMouseUpHandler);
       
 31220 				selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null;
       
 31221 			}
       
 31222 		}
       
 31223 
       
 31224 		editorManager.on('AddEditor', registerEvents);
       
 31225 		editorManager.on('RemoveEditor', unregisterDocumentEvents);
       
 31226 	}
       
 31227 
       
 31228 	/**
       
 31229 	 * Returns true if the specified element is part of the UI for example an button or text input.
       
 31230 	 *
       
 31231 	 * @method isEditorUIElement
       
 31232 	 * @param  {Element} elm Element to check if it's part of the UI or not.
       
 31233 	 * @return {Boolean} True/false state if the element is part of the UI or not.
       
 31234 	 */
       
 31235 	FocusManager.isEditorUIElement = function(elm) {
       
 31236 		// Needs to be converted to string since svg can have focus: #6776
       
 31237 		return elm.className.toString().indexOf('mce-') !== -1;
       
 31238 	};
       
 31239 
       
 31240 	return FocusManager;
       
 31241 });
       
 31242 
       
 31243 // Included from: js/tinymce/classes/EditorManager.js
       
 31244 
       
 31245 /**
       
 31246  * EditorManager.js
       
 31247  *
       
 31248  * Copyright, Moxiecode Systems AB
       
 31249  * Released under LGPL License.
       
 31250  *
       
 31251  * License: http://www.tinymce.com/license
       
 31252  * Contributing: http://www.tinymce.com/contributing
       
 31253  */
       
 31254 
       
 31255 /**
       
 31256  * This class used as a factory for manager for tinymce.Editor instances.
       
 31257  *
       
 31258  * @example
       
 31259  * tinymce.EditorManager.init({});
       
 31260  *
       
 31261  * @class tinymce.EditorManager
       
 31262  * @mixes tinymce.util.Observable
       
 31263  * @static
       
 31264  */
       
 31265 define("tinymce/EditorManager", [
       
 31266 	"tinymce/Editor",
       
 31267 	"tinymce/dom/DomQuery",
       
 31268 	"tinymce/dom/DOMUtils",
       
 31269 	"tinymce/util/URI",
       
 31270 	"tinymce/Env",
       
 31271 	"tinymce/util/Tools",
       
 31272 	"tinymce/util/Observable",
       
 31273 	"tinymce/util/I18n",
       
 31274 	"tinymce/FocusManager"
       
 31275 ], function(Editor, DomQuery, DOMUtils, URI, Env, Tools, Observable, I18n, FocusManager) {
       
 31276 	var DOM = DOMUtils.DOM;
       
 31277 	var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
       
 31278 	var instanceCounter = 0, beforeUnloadDelegate, EditorManager;
       
 31279 
       
 31280 	function removeEditorFromList(editor) {
       
 31281 		var editors = EditorManager.editors, removedFromList;
       
 31282 
       
 31283 		delete editors[editor.id];
       
 31284 
       
 31285 		for (var i = 0; i < editors.length; i++) {
       
 31286 			if (editors[i] == editor) {
       
 31287 				editors.splice(i, 1);
       
 31288 				removedFromList = true;
       
 31289 				break;
       
 31290 			}
       
 31291 		}
       
 31292 
       
 31293 		// Select another editor since the active one was removed
       
 31294 		if (EditorManager.activeEditor == editor) {
       
 31295 			EditorManager.activeEditor = editors[0];
       
 31296 		}
       
 31297 
       
 31298 		// Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor
       
 31299 		if (EditorManager.focusedEditor == editor) {
       
 31300 			EditorManager.focusedEditor = null;
       
 31301 		}
       
 31302 
       
 31303 		return removedFromList;
       
 31304 	}
       
 31305 
       
 31306 	function purgeDestroyedEditor(editor) {
       
 31307 		// User has manually destroyed the editor lets clean up the mess
       
 31308 		if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
       
 31309 			removeEditorFromList(editor);
       
 31310 			editor.unbindAllNativeEvents();
       
 31311 			editor.destroy(true);
       
 31312 			editor = null;
       
 31313 		}
       
 31314 
       
 31315 		return editor;
       
 31316 	}
       
 31317 
       
 31318 	EditorManager = {
       
 31319 		/**
       
 31320 		 * Dom query instance.
       
 31321 		 *
       
 31322 		 * @property $
       
 31323 		 * @type tinymce.dom.DomQuery
       
 31324 		 */
       
 31325 		$: DomQuery,
       
 31326 
       
 31327 		/**
       
 31328 		 * Major version of TinyMCE build.
       
 31329 		 *
       
 31330 		 * @property majorVersion
       
 31331 		 * @type String
       
 31332 		 */
       
 31333 		majorVersion: '4',
       
 31334 
       
 31335 		/**
       
 31336 		 * Minor version of TinyMCE build.
       
 31337 		 *
       
 31338 		 * @property minorVersion
       
 31339 		 * @type String
       
 31340 		 */
       
 31341 		minorVersion: '1.10',
       
 31342 
       
 31343 		/**
       
 31344 		 * Release date of TinyMCE build.
       
 31345 		 *
       
 31346 		 * @property releaseDate
       
 31347 		 * @type String
       
 31348 		 */
       
 31349 		releaseDate: '2015-05-05',
       
 31350 
       
 31351 		/**
       
 31352 		 * Collection of editor instances.
       
 31353 		 *
       
 31354 		 * @property editors
       
 31355 		 * @type Object
       
 31356 		 * @example
       
 31357 		 * for (edId in tinymce.editors)
       
 31358 		 *     tinymce.editors[edId].save();
       
 31359 		 */
       
 31360 		editors: [],
       
 31361 
       
 31362 		/**
       
 31363 		 * Collection of language pack data.
       
 31364 		 *
       
 31365 		 * @property i18n
       
 31366 		 * @type Object
       
 31367 		 */
       
 31368 		i18n: I18n,
       
 31369 
       
 31370 		/**
       
 31371 		 * Currently active editor instance.
       
 31372 		 *
       
 31373 		 * @property activeEditor
       
 31374 		 * @type tinymce.Editor
       
 31375 		 * @example
       
 31376 		 * tinyMCE.activeEditor.selection.getContent();
       
 31377 		 * tinymce.EditorManager.activeEditor.selection.getContent();
       
 31378 		 */
       
 31379 		activeEditor: null,
       
 31380 
       
 31381 		setup: function() {
       
 31382 			var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
       
 31383 
       
 31384 			// Get base URL for the current document
       
 31385 			documentBaseURL = document.location.href;
       
 31386 
       
 31387 			// Check if the URL is a document based format like: http://site/dir/file and file:///
       
 31388 			// leave other formats like applewebdata://... intact
       
 31389 			if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {
       
 31390 				documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
       
 31391 
       
 31392 				if (!/[\/\\]$/.test(documentBaseURL)) {
       
 31393 					documentBaseURL += '/';
       
 31394 				}
       
 31395 			}
       
 31396 
       
 31397 			// If tinymce is defined and has a base use that or use the old tinyMCEPreInit
       
 31398 			preInit = window.tinymce || window.tinyMCEPreInit;
       
 31399 			if (preInit) {
       
 31400 				baseURL = preInit.base || preInit.baseURL;
       
 31401 				suffix = preInit.suffix;
       
 31402 			} else {
       
 31403 				// Get base where the tinymce script is located
       
 31404 				var scripts = document.getElementsByTagName('script');
       
 31405 				for (var i = 0; i < scripts.length; i++) {
       
 31406 					src = scripts[i].src;
       
 31407 
       
 31408 					// Script types supported:
       
 31409 					// tinymce.js tinymce.min.js tinymce.dev.js
       
 31410 					// tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
       
 31411 					// tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
       
 31412 					if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
       
 31413 						if (src.indexOf('.min') != -1) {
       
 31414 							suffix = '.min';
       
 31415 						}
       
 31416 
       
 31417 						baseURL = src.substring(0, src.lastIndexOf('/'));
       
 31418 						break;
       
 31419 					}
       
 31420 				}
       
 31421 
       
 31422 				// We didn't find any baseURL by looking at the script elements
       
 31423 				// Try to use the document.currentScript as a fallback
       
 31424 				if (!baseURL && document.currentScript) {
       
 31425 					src = document.currentScript.src;
       
 31426 
       
 31427 					if (src.indexOf('.min') != -1) {
       
 31428 						suffix = '.min';
       
 31429 					}
       
 31430 
       
 31431 					baseURL = src.substring(0, src.lastIndexOf('/'));
       
 31432 				}
       
 31433 			}
       
 31434 
       
 31435 			/**
       
 31436 			 * Base URL where the root directory if TinyMCE is located.
       
 31437 			 *
       
 31438 			 * @property baseURL
       
 31439 			 * @type String
       
 31440 			 */
       
 31441 			self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
       
 31442 
       
 31443 			/**
       
 31444 			 * Document base URL where the current document is located.
       
 31445 			 *
       
 31446 			 * @property documentBaseURL
       
 31447 			 * @type String
       
 31448 			 */
       
 31449 			self.documentBaseURL = documentBaseURL;
       
 31450 
       
 31451 			/**
       
 31452 			 * Absolute baseURI for the installation path of TinyMCE.
       
 31453 			 *
       
 31454 			 * @property baseURI
       
 31455 			 * @type tinymce.util.URI
       
 31456 			 */
       
 31457 			self.baseURI = new URI(self.baseURL);
       
 31458 
       
 31459 			/**
       
 31460 			 * Current suffix to add to each plugin/theme that gets loaded for example ".min".
       
 31461 			 *
       
 31462 			 * @property suffix
       
 31463 			 * @type String
       
 31464 			 */
       
 31465 			self.suffix = suffix;
       
 31466 
       
 31467 			self.focusManager = new FocusManager(self);
       
 31468 		},
       
 31469 
       
 31470 		/**
       
 31471 		 * Initializes a set of editors. This method will create editors based on various settings.
       
 31472 		 *
       
 31473 		 * @method init
       
 31474 		 * @param {Object} settings Settings object to be passed to each editor instance.
       
 31475 		 * @example
       
 31476 		 * // Initializes a editor using the longer method
       
 31477 		 * tinymce.EditorManager.init({
       
 31478 		 *    some_settings : 'some value'
       
 31479 		 * });
       
 31480 		 *
       
 31481 		 * // Initializes a editor instance using the shorter version
       
 31482 		 * tinyMCE.init({
       
 31483 		 *    some_settings : 'some value'
       
 31484 		 * });
       
 31485 		 */
       
 31486 		init: function(settings) {
       
 31487 			var self = this, editors = [];
       
 31488 
       
 31489 			function createId(elm) {
       
 31490 				var id = elm.id;
       
 31491 
       
 31492 				// Use element id, or unique name or generate a unique id
       
 31493 				if (!id) {
       
 31494 					id = elm.name;
       
 31495 
       
 31496 					if (id && !DOM.get(id)) {
       
 31497 						id = elm.name;
       
 31498 					} else {
       
 31499 						// Generate unique name
       
 31500 						id = DOM.uniqueId();
       
 31501 					}
       
 31502 
       
 31503 					elm.setAttribute('id', id);
       
 31504 				}
       
 31505 
       
 31506 				return id;
       
 31507 			}
       
 31508 
       
 31509 			function createEditor(id, settings, targetElm) {
       
 31510 				if (!purgeDestroyedEditor(self.get(id))) {
       
 31511 					var editor = new Editor(id, settings, self);
       
 31512 
       
 31513 					editor.targetElm = editor.targetElm || targetElm;
       
 31514 					editors.push(editor);
       
 31515 					editor.render();
       
 31516 				}
       
 31517 			}
       
 31518 
       
 31519 			function execCallback(name) {
       
 31520 				var callback = settings[name];
       
 31521 
       
 31522 				if (!callback) {
       
 31523 					return;
       
 31524 				}
       
 31525 
       
 31526 				return callback.apply(self, Array.prototype.slice.call(arguments, 2));
       
 31527 			}
       
 31528 
       
 31529 			function hasClass(elm, className) {
       
 31530 				return className.constructor === RegExp ? className.test(elm.className) : DOM.hasClass(elm, className);
       
 31531 			}
       
 31532 
       
 31533 			function readyHandler() {
       
 31534 				var l, co;
       
 31535 
       
 31536 				DOM.unbind(window, 'ready', readyHandler);
       
 31537 
       
 31538 				execCallback('onpageload');
       
 31539 
       
 31540 				if (settings.types) {
       
 31541 					// Process type specific selector
       
 31542 					each(settings.types, function(type) {
       
 31543 						each(DOM.select(type.selector), function(elm) {
       
 31544 							createEditor(createId(elm), extend({}, settings, type), elm);
       
 31545 						});
       
 31546 					});
       
 31547 
       
 31548 					return;
       
 31549 				} else if (settings.selector) {
       
 31550 					// Process global selector
       
 31551 					each(DOM.select(settings.selector), function(elm) {
       
 31552 						createEditor(createId(elm), settings, elm);
       
 31553 					});
       
 31554 
       
 31555 					return;
       
 31556 				} else if (settings.target) {
       
 31557 					createEditor(createId(settings.target), settings);
       
 31558 				}
       
 31559 
       
 31560 				// Fallback to old setting
       
 31561 				switch (settings.mode) {
       
 31562 					case "exact":
       
 31563 						l = settings.elements || '';
       
 31564 
       
 31565 						if (l.length > 0) {
       
 31566 							each(explode(l), function(id) {
       
 31567 								var elm;
       
 31568 
       
 31569 								if ((elm = DOM.get(id))) {
       
 31570 									createEditor(id, settings, elm);
       
 31571 								} else {
       
 31572 									each(document.forms, function(f) {
       
 31573 										each(f.elements, function(e) {
       
 31574 											if (e.name === id) {
       
 31575 												id = 'mce_editor_' + instanceCounter++;
       
 31576 												DOM.setAttrib(e, 'id', id);
       
 31577 												createEditor(id, settings, e);
       
 31578 											}
       
 31579 										});
       
 31580 									});
       
 31581 								}
       
 31582 							});
       
 31583 						}
       
 31584 						break;
       
 31585 
       
 31586 					case "textareas":
       
 31587 					case "specific_textareas":
       
 31588 						each(DOM.select('textarea'), function(elm) {
       
 31589 							if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) {
       
 31590 								return;
       
 31591 							}
       
 31592 
       
 31593 							if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) {
       
 31594 								createEditor(createId(elm), settings, elm);
       
 31595 							}
       
 31596 						});
       
 31597 						break;
       
 31598 				}
       
 31599 
       
 31600 				// Call onInit when all editors are initialized
       
 31601 				if (settings.oninit) {
       
 31602 					l = co = 0;
       
 31603 
       
 31604 					each(editors, function(ed) {
       
 31605 						co++;
       
 31606 
       
 31607 						if (!ed.initialized) {
       
 31608 							// Wait for it
       
 31609 							ed.on('init', function() {
       
 31610 								l++;
       
 31611 
       
 31612 								// All done
       
 31613 								if (l == co) {
       
 31614 									execCallback('oninit');
       
 31615 								}
       
 31616 							});
       
 31617 						} else {
       
 31618 							l++;
       
 31619 						}
       
 31620 
       
 31621 						// All done
       
 31622 						if (l == co) {
       
 31623 							execCallback('oninit');
       
 31624 						}
       
 31625 					});
       
 31626 				}
       
 31627 			}
       
 31628 
       
 31629 			self.settings = settings;
       
 31630 
       
 31631 			DOM.bind(window, 'ready', readyHandler);
       
 31632 		},
       
 31633 
       
 31634 		/**
       
 31635 		 * Returns a editor instance by id.
       
 31636 		 *
       
 31637 		 * @method get
       
 31638 		 * @param {String/Number} id Editor instance id or index to return.
       
 31639 		 * @return {tinymce.Editor} Editor instance to return.
       
 31640 		 * @example
       
 31641 		 * // Adds an onclick event to an editor by id (shorter version)
       
 31642 		 * tinymce.get('mytextbox').on('click', function(e) {
       
 31643 		 *    ed.windowManager.alert('Hello world!');
       
 31644 		 * });
       
 31645 		 *
       
 31646 		 * // Adds an onclick event to an editor by id (longer version)
       
 31647 		 * tinymce.EditorManager.get('mytextbox').on('click', function(e) {
       
 31648 		 *    ed.windowManager.alert('Hello world!');
       
 31649 		 * });
       
 31650 		 */
       
 31651 		get: function(id) {
       
 31652 			if (!arguments.length) {
       
 31653 				return this.editors;
       
 31654 			}
       
 31655 
       
 31656 			return id in this.editors ? this.editors[id] : null;
       
 31657 		},
       
 31658 
       
 31659 		/**
       
 31660 		 * Adds an editor instance to the editor collection. This will also set it as the active editor.
       
 31661 		 *
       
 31662 		 * @method add
       
 31663 		 * @param {tinymce.Editor} editor Editor instance to add to the collection.
       
 31664 		 * @return {tinymce.Editor} The same instance that got passed in.
       
 31665 		 */
       
 31666 		add: function(editor) {
       
 31667 			var self = this, editors = self.editors;
       
 31668 
       
 31669 			// Add named and index editor instance
       
 31670 			editors[editor.id] = editor;
       
 31671 			editors.push(editor);
       
 31672 
       
 31673 			// Doesn't call setActive method since we don't want
       
 31674 			// to fire a bunch of activate/deactivate calls while initializing
       
 31675 			self.activeEditor = editor;
       
 31676 
       
 31677 			/**
       
 31678 			 * Fires when an editor is added to the EditorManager collection.
       
 31679 			 *
       
 31680 			 * @event AddEditor
       
 31681 			 * @param {Object} e Event arguments.
       
 31682 			 */
       
 31683 			self.fire('AddEditor', {editor: editor});
       
 31684 
       
 31685 			if (!beforeUnloadDelegate) {
       
 31686 				beforeUnloadDelegate = function() {
       
 31687 					self.fire('BeforeUnload');
       
 31688 				};
       
 31689 
       
 31690 				DOM.bind(window, 'beforeunload', beforeUnloadDelegate);
       
 31691 			}
       
 31692 
       
 31693 			return editor;
       
 31694 		},
       
 31695 
       
 31696 		/**
       
 31697 		 * Creates an editor instance and adds it to the EditorManager collection.
       
 31698 		 *
       
 31699 		 * @method createEditor
       
 31700 		 * @param {String} id Instance id to use for editor.
       
 31701 		 * @param {Object} settings Editor instance settings.
       
 31702 		 * @return {tinymce.Editor} Editor instance that got created.
       
 31703 		 */
       
 31704 		createEditor: function(id, settings) {
       
 31705 			return this.add(new Editor(id, settings, this));
       
 31706 		},
       
 31707 
       
 31708 		/**
       
 31709 		 * Removes a editor or editors form page.
       
 31710 		 *
       
 31711 		 * @example
       
 31712 		 * // Remove all editors bound to divs
       
 31713 		 * tinymce.remove('div');
       
 31714 		 *
       
 31715 		 * // Remove all editors bound to textareas
       
 31716 		 * tinymce.remove('textarea');
       
 31717 		 *
       
 31718 		 * // Remove all editors
       
 31719 		 * tinymce.remove();
       
 31720 		 *
       
 31721 		 * // Remove specific instance by id
       
 31722 		 * tinymce.remove('#id');
       
 31723 		 *
       
 31724 		 * @method remove
       
 31725 		 * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove.
       
 31726 		 * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
       
 31727 		 */
       
 31728 		remove: function(selector) {
       
 31729 			var self = this, i, editors = self.editors, editor;
       
 31730 
       
 31731 			// Remove all editors
       
 31732 			if (!selector) {
       
 31733 				for (i = editors.length - 1; i >= 0; i--) {
       
 31734 					self.remove(editors[i]);
       
 31735 				}
       
 31736 
       
 31737 				return;
       
 31738 			}
       
 31739 
       
 31740 			// Remove editors by selector
       
 31741 			if (typeof selector == "string") {
       
 31742 				selector = selector.selector || selector;
       
 31743 
       
 31744 				each(DOM.select(selector), function(elm) {
       
 31745 					editor = editors[elm.id];
       
 31746 
       
 31747 					if (editor) {
       
 31748 						self.remove(editor);
       
 31749 					}
       
 31750 				});
       
 31751 
       
 31752 				return;
       
 31753 			}
       
 31754 
       
 31755 			// Remove specific editor
       
 31756 			editor = selector;
       
 31757 
       
 31758 			// Not in the collection
       
 31759 			if (!editors[editor.id]) {
       
 31760 				return null;
       
 31761 			}
       
 31762 
       
 31763 			/**
       
 31764 			 * Fires when an editor is removed from EditorManager collection.
       
 31765 			 *
       
 31766 			 * @event RemoveEditor
       
 31767 			 * @param {Object} e Event arguments.
       
 31768 			 */
       
 31769 			if (removeEditorFromList(editor)) {
       
 31770 				self.fire('RemoveEditor', {editor: editor});
       
 31771 			}
       
 31772 
       
 31773 			if (!editors.length) {
       
 31774 				DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
       
 31775 			}
       
 31776 
       
 31777 			editor.remove();
       
 31778 
       
 31779 			return editor;
       
 31780 		},
       
 31781 
       
 31782 		/**
       
 31783 		 * Executes a specific command on the currently active editor.
       
 31784 		 *
       
 31785 		 * @method execCommand
       
 31786 		 * @param {String} c Command to perform for example Bold.
       
 31787 		 * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
       
 31788 		 * @param {String} v Optional value parameter like for example an URL to a link.
       
 31789 		 * @return {Boolean} true/false if the command was executed or not.
       
 31790 		 */
       
 31791 		execCommand: function(cmd, ui, value) {
       
 31792 			var self = this, editor = self.get(value);
       
 31793 
       
 31794 			// Manager commands
       
 31795 			switch (cmd) {
       
 31796 				case "mceAddEditor":
       
 31797 					if (!self.get(value)) {
       
 31798 						new Editor(value, self.settings, self).render();
       
 31799 					}
       
 31800 
       
 31801 					return true;
       
 31802 
       
 31803 				case "mceRemoveEditor":
       
 31804 					if (editor) {
       
 31805 						editor.remove();
       
 31806 					}
       
 31807 
       
 31808 					return true;
       
 31809 
       
 31810 				case 'mceToggleEditor':
       
 31811 					if (!editor) {
       
 31812 						self.execCommand('mceAddEditor', 0, value);
       
 31813 						return true;
       
 31814 					}
       
 31815 
       
 31816 					if (editor.isHidden()) {
       
 31817 						editor.show();
       
 31818 					} else {
       
 31819 						editor.hide();
       
 31820 					}
       
 31821 
       
 31822 					return true;
       
 31823 			}
       
 31824 
       
 31825 			// Run command on active editor
       
 31826 			if (self.activeEditor) {
       
 31827 				return self.activeEditor.execCommand(cmd, ui, value);
       
 31828 			}
       
 31829 
       
 31830 			return false;
       
 31831 		},
       
 31832 
       
 31833 		/**
       
 31834 		 * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
       
 31835 		 *
       
 31836 		 * @method triggerSave
       
 31837 		 * @example
       
 31838 		 * // Saves all contents
       
 31839 		 * tinyMCE.triggerSave();
       
 31840 		 */
       
 31841 		triggerSave: function() {
       
 31842 			each(this.editors, function(editor) {
       
 31843 				editor.save();
       
 31844 			});
       
 31845 		},
       
 31846 
       
 31847 		/**
       
 31848 		 * Adds a language pack, this gets called by the loaded language files like en.js.
       
 31849 		 *
       
 31850 		 * @method addI18n
       
 31851 		 * @param {String} code Optional language code.
       
 31852 		 * @param {Object} items Name/value object with translations.
       
 31853 		 */
       
 31854 		addI18n: function(code, items) {
       
 31855 			I18n.add(code, items);
       
 31856 		},
       
 31857 
       
 31858 		/**
       
 31859 		 * Translates the specified string using the language pack items.
       
 31860 		 *
       
 31861 		 * @method translate
       
 31862 		 * @param {String/Array/Object} text String to translate
       
 31863 		 * @return {String} Translated string.
       
 31864 		 */
       
 31865 		translate: function(text) {
       
 31866 			return I18n.translate(text);
       
 31867 		},
       
 31868 
       
 31869 		/**
       
 31870 		 * Sets the active editor instance and fires the deactivate/activate events.
       
 31871 		 *
       
 31872 		 * @method setActive
       
 31873 		 * @param {tinymce.Editor} editor Editor instance to set as the active instance.
       
 31874 		 */
       
 31875 		setActive: function(editor) {
       
 31876 			var activeEditor = this.activeEditor;
       
 31877 
       
 31878 			if (this.activeEditor != editor) {
       
 31879 				if (activeEditor) {
       
 31880 					activeEditor.fire('deactivate', {relatedTarget: editor});
       
 31881 				}
       
 31882 
       
 31883 				editor.fire('activate', {relatedTarget: activeEditor});
       
 31884 			}
       
 31885 
       
 31886 			this.activeEditor = editor;
       
 31887 		}
       
 31888 	};
       
 31889 
       
 31890 	extend(EditorManager, Observable);
       
 31891 
       
 31892 	EditorManager.setup();
       
 31893 
       
 31894 	// Export EditorManager as tinymce/tinymce in global namespace
       
 31895 	window.tinymce = window.tinyMCE = EditorManager;
       
 31896 
       
 31897 	return EditorManager;
       
 31898 });
       
 31899 
       
 31900 // Included from: js/tinymce/classes/LegacyInput.js
       
 31901 
       
 31902 /**
       
 31903  * LegacyInput.js
       
 31904  *
       
 31905  * Copyright, Moxiecode Systems AB
       
 31906  * Released under LGPL License.
       
 31907  *
       
 31908  * License: http://www.tinymce.com/license
       
 31909  * Contributing: http://www.tinymce.com/contributing
       
 31910  */
       
 31911 
       
 31912 define("tinymce/LegacyInput", [
       
 31913 	"tinymce/EditorManager",
       
 31914 	"tinymce/util/Tools"
       
 31915 ], function(EditorManager, Tools) {
       
 31916 	var each = Tools.each, explode = Tools.explode;
       
 31917 
       
 31918 	EditorManager.on('AddEditor', function(e) {
       
 31919 		var editor = e.editor;
       
 31920 
       
 31921 		editor.on('preInit', function() {
       
 31922 			var filters, fontSizes, dom, settings = editor.settings;
       
 31923 
       
 31924 			function replaceWithSpan(node, styles) {
       
 31925 				each(styles, function(value, name) {
       
 31926 					if (value) {
       
 31927 						dom.setStyle(node, name, value);
       
 31928 					}
       
 31929 				});
       
 31930 
       
 31931 				dom.rename(node, 'span');
       
 31932 			}
       
 31933 
       
 31934 			function convert(e) {
       
 31935 				dom = editor.dom;
       
 31936 
       
 31937 				if (settings.convert_fonts_to_spans) {
       
 31938 					each(dom.select('font,u,strike', e.node), function(node) {
       
 31939 						filters[node.nodeName.toLowerCase()](dom, node);
       
 31940 					});
       
 31941 				}
       
 31942 			}
       
 31943 
       
 31944 			if (settings.inline_styles) {
       
 31945 				fontSizes = explode(settings.font_size_legacy_values);
       
 31946 
       
 31947 				filters = {
       
 31948 					font: function(dom, node) {
       
 31949 						replaceWithSpan(node, {
       
 31950 							backgroundColor: node.style.backgroundColor,
       
 31951 							color: node.color,
       
 31952 							fontFamily: node.face,
       
 31953 							fontSize: fontSizes[parseInt(node.size, 10) - 1]
       
 31954 						});
       
 31955 					},
       
 31956 
       
 31957 					u: function(dom, node) {
       
 31958 						// HTML5 allows U element
       
 31959 						if (editor.settings.schema === "html4") {
       
 31960 							replaceWithSpan(node, {
       
 31961 								textDecoration: 'underline'
       
 31962 							});
       
 31963 						}
       
 31964 					},
       
 31965 
       
 31966 					strike: function(dom, node) {
       
 31967 						replaceWithSpan(node, {
       
 31968 							textDecoration: 'line-through'
       
 31969 						});
       
 31970 					}
       
 31971 				};
       
 31972 
       
 31973 				editor.on('PreProcess SetContent', convert);
       
 31974 			}
       
 31975 		});
       
 31976 	});
       
 31977 });
       
 31978 
       
 31979 // Included from: js/tinymce/classes/util/XHR.js
       
 31980 
       
 31981 /**
       
 31982  * XHR.js
       
 31983  *
       
 31984  * Copyright, Moxiecode Systems AB
       
 31985  * Released under LGPL License.
       
 31986  *
       
 31987  * License: http://www.tinymce.com/license
       
 31988  * Contributing: http://www.tinymce.com/contributing
       
 31989  */
       
 31990 
       
 31991 /**
       
 31992  * This class enables you to send XMLHTTPRequests cross browser.
       
 31993  * @class tinymce.util.XHR
       
 31994  * @mixes tinymce.util.Observable
       
 31995  * @static
       
 31996  * @example
       
 31997  * // Sends a low level Ajax request
       
 31998  * tinymce.util.XHR.send({
       
 31999  *    url: 'someurl',
       
 32000  *    success: function(text) {
       
 32001  *       console.debug(text);
       
 32002  *    }
       
 32003  * });
       
 32004  *
       
 32005  * // Add custom header to XHR request
       
 32006  * tinymce.util.XHR.on('beforeSend', function(e) {
       
 32007  *     e.xhr.setRequestHeader('X-Requested-With', 'Something');
       
 32008  * });
       
 32009  */
       
 32010 define("tinymce/util/XHR", [
       
 32011 	"tinymce/util/Observable",
       
 32012 	"tinymce/util/Tools"
       
 32013 ], function(Observable, Tools) {
       
 32014 	var XHR = {
       
 32015 		/**
       
 32016 		 * Sends a XMLHTTPRequest.
       
 32017 		 * Consult the Wiki for details on what settings this method takes.
       
 32018 		 *
       
 32019 		 * @method send
       
 32020 		 * @param {Object} settings Object will target URL, callbacks and other info needed to make the request.
       
 32021 		 */
       
 32022 		send: function(settings) {
       
 32023 			var xhr, count = 0;
       
 32024 
       
 32025 			function ready() {
       
 32026 				if (!settings.async || xhr.readyState == 4 || count++ > 10000) {
       
 32027 					if (settings.success && count < 10000 && xhr.status == 200) {
       
 32028 						settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings);
       
 32029 					} else if (settings.error) {
       
 32030 						settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings);
       
 32031 					}
       
 32032 
       
 32033 					xhr = null;
       
 32034 				} else {
       
 32035 					setTimeout(ready, 10);
       
 32036 				}
       
 32037 			}
       
 32038 
       
 32039 			// Default settings
       
 32040 			settings.scope = settings.scope || this;
       
 32041 			settings.success_scope = settings.success_scope || settings.scope;
       
 32042 			settings.error_scope = settings.error_scope || settings.scope;
       
 32043 			settings.async = settings.async === false ? false : true;
       
 32044 			settings.data = settings.data || '';
       
 32045 
       
 32046 			xhr = new XMLHttpRequest();
       
 32047 
       
 32048 			if (xhr) {
       
 32049 				if (xhr.overrideMimeType) {
       
 32050 					xhr.overrideMimeType(settings.content_type);
       
 32051 				}
       
 32052 
       
 32053 				xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
       
 32054 
       
 32055 				if (settings.crossDomain) {
       
 32056 					xhr.withCredentials = true;
       
 32057 				}
       
 32058 
       
 32059 				if (settings.content_type) {
       
 32060 					xhr.setRequestHeader('Content-Type', settings.content_type);
       
 32061 				}
       
 32062 
       
 32063 				xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
       
 32064 
       
 32065 				xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr;
       
 32066 				xhr.send(settings.data);
       
 32067 
       
 32068 				// Syncronous request
       
 32069 				if (!settings.async) {
       
 32070 					return ready();
       
 32071 				}
       
 32072 
       
 32073 				// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
       
 32074 				setTimeout(ready, 10);
       
 32075 			}
       
 32076 		}
       
 32077 	};
       
 32078 
       
 32079 	Tools.extend(XHR, Observable);
       
 32080 
       
 32081 	return XHR;
       
 32082 });
       
 32083 
       
 32084 // Included from: js/tinymce/classes/util/JSON.js
       
 32085 
       
 32086 /**
       
 32087  * JSON.js
       
 32088  *
       
 32089  * Copyright, Moxiecode Systems AB
       
 32090  * Released under LGPL License.
       
 32091  *
       
 32092  * License: http://www.tinymce.com/license
       
 32093  * Contributing: http://www.tinymce.com/contributing
       
 32094  */
       
 32095 
       
 32096 /**
       
 32097  * JSON parser and serializer class.
       
 32098  *
       
 32099  * @class tinymce.util.JSON
       
 32100  * @static
       
 32101  * @example
       
 32102  * // JSON parse a string into an object
       
 32103  * var obj = tinymce.util.JSON.parse(somestring);
       
 32104  *
       
 32105  * // JSON serialize a object into an string
       
 32106  * var str = tinymce.util.JSON.serialize(obj);
       
 32107  */
       
 32108 define("tinymce/util/JSON", [], function() {
       
 32109 	function serialize(o, quote) {
       
 32110 		var i, v, t, name;
       
 32111 
       
 32112 		quote = quote || '"';
       
 32113 
       
 32114 		if (o === null) {
       
 32115 			return 'null';
       
 32116 		}
       
 32117 
       
 32118 		t = typeof o;
       
 32119 
       
 32120 		if (t == 'string') {
       
 32121 			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
       
 32122 
       
 32123 			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
       
 32124 				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
       
 32125 				if (quote === '"' && a === "'") {
       
 32126 					return a;
       
 32127 				}
       
 32128 
       
 32129 				i = v.indexOf(b);
       
 32130 
       
 32131 				if (i + 1) {
       
 32132 					return '\\' + v.charAt(i + 1);
       
 32133 				}
       
 32134 
       
 32135 				a = b.charCodeAt().toString(16);
       
 32136 
       
 32137 				return '\\u' + '0000'.substring(a.length) + a;
       
 32138 			}) + quote;
       
 32139 		}
       
 32140 
       
 32141 		if (t == 'object') {
       
 32142 			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
       
 32143 				for (i = 0, v = '['; i < o.length; i++) {
       
 32144 					v += (i > 0 ? ',' : '') + serialize(o[i], quote);
       
 32145 				}
       
 32146 
       
 32147 				return v + ']';
       
 32148 			}
       
 32149 
       
 32150 			v = '{';
       
 32151 
       
 32152 			for (name in o) {
       
 32153 				if (o.hasOwnProperty(name)) {
       
 32154 					v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name +
       
 32155 						quote + ':' + serialize(o[name], quote) : '';
       
 32156 				}
       
 32157 			}
       
 32158 
       
 32159 			return v + '}';
       
 32160 		}
       
 32161 
       
 32162 		return '' + o;
       
 32163 	}
       
 32164 
       
 32165 	return {
       
 32166 		/**
       
 32167 		 * Serializes the specified object as a JSON string.
       
 32168 		 *
       
 32169 		 * @method serialize
       
 32170 		 * @param {Object} obj Object to serialize as a JSON string.
       
 32171 		 * @param {String} quote Optional quote string defaults to ".
       
 32172 		 * @return {string} JSON string serialized from input.
       
 32173 		 */
       
 32174 		serialize: serialize,
       
 32175 
       
 32176 		/**
       
 32177 		 * Unserializes/parses the specified JSON string into a object.
       
 32178 		 *
       
 32179 		 * @method parse
       
 32180 		 * @param {string} s JSON String to parse into a JavaScript object.
       
 32181 		 * @return {Object} Object from input JSON string or undefined if it failed.
       
 32182 		 */
       
 32183 		parse: function(text) {
       
 32184 			try {
       
 32185 				// Trick uglify JS
       
 32186 				return window[String.fromCharCode(101) + 'val']('(' + text + ')');
       
 32187 			} catch (ex) {
       
 32188 				// Ignore
       
 32189 			}
       
 32190 		}
       
 32191 
       
 32192 		/**#@-*/
       
 32193 	};
       
 32194 });
       
 32195 
       
 32196 // Included from: js/tinymce/classes/util/JSONRequest.js
       
 32197 
       
 32198 /**
       
 32199  * JSONRequest.js
       
 32200  *
       
 32201  * Copyright, Moxiecode Systems AB
       
 32202  * Released under LGPL License.
       
 32203  *
       
 32204  * License: http://www.tinymce.com/license
       
 32205  * Contributing: http://www.tinymce.com/contributing
       
 32206  */
       
 32207 
       
 32208 /**
       
 32209  * This class enables you to use JSON-RPC to call backend methods.
       
 32210  *
       
 32211  * @class tinymce.util.JSONRequest
       
 32212  * @example
       
 32213  * var json = new tinymce.util.JSONRequest({
       
 32214  *     url: 'somebackend.php'
       
 32215  * });
       
 32216  *
       
 32217  * // Send RPC call 1
       
 32218  * json.send({
       
 32219  *     method: 'someMethod1',
       
 32220  *     params: ['a', 'b'],
       
 32221  *     success: function(result) {
       
 32222  *         console.dir(result);
       
 32223  *     }
       
 32224  * });
       
 32225  *
       
 32226  * // Send RPC call 2
       
 32227  * json.send({
       
 32228  *     method: 'someMethod2',
       
 32229  *     params: ['a', 'b'],
       
 32230  *     success: function(result) {
       
 32231  *         console.dir(result);
       
 32232  *     }
       
 32233  * });
       
 32234  */
       
 32235 define("tinymce/util/JSONRequest", [
       
 32236 	"tinymce/util/JSON",
       
 32237 	"tinymce/util/XHR",
       
 32238 	"tinymce/util/Tools"
       
 32239 ], function(JSON, XHR, Tools) {
       
 32240 	var extend = Tools.extend;
       
 32241 
       
 32242 	function JSONRequest(settings) {
       
 32243 		this.settings = extend({}, settings);
       
 32244 		this.count = 0;
       
 32245 	}
       
 32246 
       
 32247 	/**
       
 32248 	 * Simple helper function to send a JSON-RPC request without the need to initialize an object.
       
 32249 	 * Consult the Wiki API documentation for more details on what you can pass to this function.
       
 32250 	 *
       
 32251 	 * @method sendRPC
       
 32252 	 * @static
       
 32253 	 * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc.
       
 32254 	 */
       
 32255 	JSONRequest.sendRPC = function(o) {
       
 32256 		return new JSONRequest().send(o);
       
 32257 	};
       
 32258 
       
 32259 	JSONRequest.prototype = {
       
 32260 		/**
       
 32261 		 * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function.
       
 32262 		 *
       
 32263 		 * @method send
       
 32264 		 * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc.
       
 32265 		 */
       
 32266 		send: function(args) {
       
 32267 			var ecb = args.error, scb = args.success;
       
 32268 
       
 32269 			args = extend(this.settings, args);
       
 32270 
       
 32271 			args.success = function(c, x) {
       
 32272 				c = JSON.parse(c);
       
 32273 
       
 32274 				if (typeof c == 'undefined') {
       
 32275 					c = {
       
 32276 						error: 'JSON Parse error.'
       
 32277 					};
       
 32278 				}
       
 32279 
       
 32280 				if (c.error) {
       
 32281 					ecb.call(args.error_scope || args.scope, c.error, x);
       
 32282 				} else {
       
 32283 					scb.call(args.success_scope || args.scope, c.result);
       
 32284 				}
       
 32285 			};
       
 32286 
       
 32287 			args.error = function(ty, x) {
       
 32288 				if (ecb) {
       
 32289 					ecb.call(args.error_scope || args.scope, ty, x);
       
 32290 				}
       
 32291 			};
       
 32292 
       
 32293 			args.data = JSON.serialize({
       
 32294 				id: args.id || 'c' + (this.count++),
       
 32295 				method: args.method,
       
 32296 				params: args.params
       
 32297 			});
       
 32298 
       
 32299 			// JSON content type for Ruby on rails. Bug: #1883287
       
 32300 			args.content_type = 'application/json';
       
 32301 
       
 32302 			XHR.send(args);
       
 32303 		}
       
 32304 	};
       
 32305 
       
 32306 	return JSONRequest;
       
 32307 });
       
 32308 
       
 32309 // Included from: js/tinymce/classes/util/JSONP.js
       
 32310 
       
 32311 /**
       
 32312  * JSONP.js
       
 32313  *
       
 32314  * Copyright, Moxiecode Systems AB
       
 32315  * Released under LGPL License.
       
 32316  *
       
 32317  * License: http://www.tinymce.com/license
       
 32318  * Contributing: http://www.tinymce.com/contributing
       
 32319  */
       
 32320 
       
 32321 define("tinymce/util/JSONP", [
       
 32322 	"tinymce/dom/DOMUtils"
       
 32323 ], function(DOMUtils) {
       
 32324 	return {
       
 32325 		callbacks: {},
       
 32326 		count: 0,
       
 32327 
       
 32328 		send: function(settings) {
       
 32329 			var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count;
       
 32330 			var id = 'tinymce_jsonp_' + count;
       
 32331 
       
 32332 			self.callbacks[count] = function(json) {
       
 32333 				dom.remove(id);
       
 32334 				delete self.callbacks[count];
       
 32335 
       
 32336 				settings.callback(json);
       
 32337 			};
       
 32338 
       
 32339 			dom.add(dom.doc.body, 'script', {
       
 32340 				id: id,
       
 32341 				src: settings.url,
       
 32342 				type: 'text/javascript'
       
 32343 			});
       
 32344 
       
 32345 			self.count++;
       
 32346 		}
       
 32347 	};
       
 32348 });
       
 32349 
       
 32350 // Included from: js/tinymce/classes/util/LocalStorage.js
       
 32351 
       
 32352 /**
       
 32353  * LocalStorage.js
       
 32354  *
       
 32355  * Copyright, Moxiecode Systems AB
       
 32356  * Released under LGPL License.
       
 32357  *
       
 32358  * License: http://www.tinymce.com/license
       
 32359  * Contributing: http://www.tinymce.com/contributing
       
 32360  */
       
 32361 
       
 32362 /**
       
 32363  * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
       
 32364  * Storage is done using userData on IE 7 and a special serialization format. The format is designed
       
 32365  * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
       
 32366  * makes it possible to store for example HTML data.
       
 32367  *
       
 32368  * Storage format for userData:
       
 32369  * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
       
 32370  *
       
 32371  * For example this data key1=value1,key2=value2 would be:
       
 32372  * 4,key1,6,value1,4,key2,6,value2
       
 32373  *
       
 32374  * @class tinymce.util.LocalStorage
       
 32375  * @static
       
 32376  * @version 4.0
       
 32377  * @example
       
 32378  * tinymce.util.LocalStorage.setItem('key', 'value');
       
 32379  * var value = tinymce.util.LocalStorage.getItem('key');
       
 32380  */
       
 32381 define("tinymce/util/LocalStorage", [], function() {
       
 32382 	var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
       
 32383 
       
 32384 	// Check for native support
       
 32385 	try {
       
 32386 		if (window.localStorage) {
       
 32387 			return localStorage;
       
 32388 		}
       
 32389 	} catch (ex) {
       
 32390 		// Ignore
       
 32391 	}
       
 32392 
       
 32393 	userDataKey = "tinymce";
       
 32394 	storageElm = document.documentElement;
       
 32395 	hasOldIEDataSupport = !!storageElm.addBehavior;
       
 32396 
       
 32397 	if (hasOldIEDataSupport) {
       
 32398 		storageElm.addBehavior('#default#userData');
       
 32399 	}
       
 32400 
       
 32401 	/**
       
 32402 	 * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
       
 32403 	 */
       
 32404 	function updateKeys() {
       
 32405 		keys = [];
       
 32406 
       
 32407 		for (var key in items) {
       
 32408 			keys.push(key);
       
 32409 		}
       
 32410 
       
 32411 		LocalStorage.length = keys.length;
       
 32412 	}
       
 32413 
       
 32414 	/**
       
 32415 	 * Loads the userData string and parses it into the items structure.
       
 32416 	 */
       
 32417 	function load() {
       
 32418 		var key, data, value, pos = 0;
       
 32419 
       
 32420 		items = {};
       
 32421 
       
 32422 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
       
 32423 		if (!hasOldIEDataSupport) {
       
 32424 			return;
       
 32425 		}
       
 32426 
       
 32427 		function next(end) {
       
 32428 			var value, nextPos;
       
 32429 
       
 32430 			nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
       
 32431 			if (nextPos === -1 || nextPos > data.length) {
       
 32432 				return null;
       
 32433 			}
       
 32434 
       
 32435 			value = data.substring(pos, nextPos);
       
 32436 			pos = nextPos + 1;
       
 32437 
       
 32438 			return value;
       
 32439 		}
       
 32440 
       
 32441 		storageElm.load(userDataKey);
       
 32442 		data = storageElm.getAttribute(userDataKey) || '';
       
 32443 
       
 32444 		do {
       
 32445 			var offset = next();
       
 32446 			if (offset === null) {
       
 32447 				break;
       
 32448 			}
       
 32449 
       
 32450 			key = next(parseInt(offset, 32) || 0);
       
 32451 			if (key !== null) {
       
 32452 				offset = next();
       
 32453 				if (offset === null) {
       
 32454 					break;
       
 32455 				}
       
 32456 
       
 32457 				value = next(parseInt(offset, 32) || 0);
       
 32458 
       
 32459 				if (key) {
       
 32460 					items[key] = value;
       
 32461 				}
       
 32462 			}
       
 32463 		} while (key !== null);
       
 32464 
       
 32465 		updateKeys();
       
 32466 	}
       
 32467 
       
 32468 	/**
       
 32469 	 * Saves the items structure into a the userData format.
       
 32470 	 */
       
 32471 	function save() {
       
 32472 		var value, data = '';
       
 32473 
       
 32474 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
       
 32475 		if (!hasOldIEDataSupport) {
       
 32476 			return;
       
 32477 		}
       
 32478 
       
 32479 		for (var key in items) {
       
 32480 			value = items[key];
       
 32481 			data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
       
 32482 		}
       
 32483 
       
 32484 		storageElm.setAttribute(userDataKey, data);
       
 32485 
       
 32486 		try {
       
 32487 			storageElm.save(userDataKey);
       
 32488 		} catch (ex) {
       
 32489 			// Ignore disk full
       
 32490 		}
       
 32491 
       
 32492 		updateKeys();
       
 32493 	}
       
 32494 
       
 32495 	LocalStorage = {
       
 32496 		/**
       
 32497 		 * Length of the number of items in storage.
       
 32498 		 *
       
 32499 		 * @property length
       
 32500 		 * @type Number
       
 32501 		 * @return {Number} Number of items in storage.
       
 32502 		 */
       
 32503 		//length:0,
       
 32504 
       
 32505 		/**
       
 32506 		 * Returns the key name by index.
       
 32507 		 *
       
 32508 		 * @method key
       
 32509 		 * @param {Number} index Index of key to return.
       
 32510 		 * @return {String} Key value or null if it wasn't found.
       
 32511 		 */
       
 32512 		key: function(index) {
       
 32513 			return keys[index];
       
 32514 		},
       
 32515 
       
 32516 		/**
       
 32517 		 * Returns the value if the specified key or null if it wasn't found.
       
 32518 		 *
       
 32519 		 * @method getItem
       
 32520 		 * @param {String} key Key of item to retrive.
       
 32521 		 * @return {String} Value of the specified item or null if it wasn't found.
       
 32522 		 */
       
 32523 		getItem: function(key) {
       
 32524 			return key in items ? items[key] : null;
       
 32525 		},
       
 32526 
       
 32527 		/**
       
 32528 		 * Sets the value of the specified item by it's key.
       
 32529 		 *
       
 32530 		 * @method setItem
       
 32531 		 * @param {String} key Key of the item to set.
       
 32532 		 * @param {String} value Value of the item to set.
       
 32533 		 */
       
 32534 		setItem: function(key, value) {
       
 32535 			items[key] = "" + value;
       
 32536 			save();
       
 32537 		},
       
 32538 
       
 32539 		/**
       
 32540 		 * Removes the specified item by key.
       
 32541 		 *
       
 32542 		 * @method removeItem
       
 32543 		 * @param {String} key Key of item to remove.
       
 32544 		 */
       
 32545 		removeItem: function(key) {
       
 32546 			delete items[key];
       
 32547 			save();
       
 32548 		},
       
 32549 
       
 32550 		/**
       
 32551 		 * Removes all items.
       
 32552 		 *
       
 32553 		 * @method clear
       
 32554 		 */
       
 32555 		clear: function() {
       
 32556 			items = {};
       
 32557 			save();
       
 32558 		}
       
 32559 	};
       
 32560 
       
 32561 	load();
       
 32562 
       
 32563 	return LocalStorage;
       
 32564 });
       
 32565 
       
 32566 // Included from: js/tinymce/classes/Compat.js
       
 32567 
       
 32568 /**
       
 32569  * Compat.js
       
 32570  *
       
 32571  * Copyright, Moxiecode Systems AB
       
 32572  * Released under LGPL License.
       
 32573  *
       
 32574  * License: http://www.tinymce.com/license
       
 32575  * Contributing: http://www.tinymce.com/contributing
       
 32576  */
       
 32577 
       
 32578 /**
       
 32579  * TinyMCE core class.
       
 32580  *
       
 32581  * @static
       
 32582  * @class tinymce
       
 32583  * @borrow-members tinymce.EditorManager
       
 32584  * @borrow-members tinymce.util.Tools
       
 32585  */
       
 32586 define("tinymce/Compat", [
       
 32587 	"tinymce/dom/DOMUtils",
       
 32588 	"tinymce/dom/EventUtils",
       
 32589 	"tinymce/dom/ScriptLoader",
       
 32590 	"tinymce/AddOnManager",
       
 32591 	"tinymce/util/Tools",
       
 32592 	"tinymce/Env"
       
 32593 ], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) {
       
 32594 	var tinymce = window.tinymce;
       
 32595 
       
 32596 	/**
       
 32597 	 * @property {tinymce.dom.DOMUtils} DOM Global DOM instance.
       
 32598 	 * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance.
       
 32599 	 * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance.
       
 32600 	 * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance.
       
 32601 	 */
       
 32602 	tinymce.DOM = DOMUtils.DOM;
       
 32603 	tinymce.ScriptLoader = ScriptLoader.ScriptLoader;
       
 32604 	tinymce.PluginManager = AddOnManager.PluginManager;
       
 32605 	tinymce.ThemeManager = AddOnManager.ThemeManager;
       
 32606 
       
 32607 	tinymce.dom = tinymce.dom || {};
       
 32608 	tinymce.dom.Event = EventUtils.Event;
       
 32609 
       
 32610 	Tools.each(Tools, function(func, key) {
       
 32611 		tinymce[key] = func;
       
 32612 	});
       
 32613 
       
 32614 	Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
       
 32615 		tinymce[name] = Env[name.substr(2).toLowerCase()];
       
 32616 	});
       
 32617 
       
 32618 	return {};
       
 32619 });
       
 32620 
       
 32621 // Describe the different namespaces
       
 32622 
       
 32623 /**
       
 32624  * Root level namespace this contains classes directly releated to the TinyMCE editor.
       
 32625  *
       
 32626  * @namespace tinymce
       
 32627  */
       
 32628 
       
 32629 /**
       
 32630  * Contains classes for handling the browsers DOM.
       
 32631  *
       
 32632  * @namespace tinymce.dom
       
 32633  */
       
 32634 
       
 32635 /**
       
 32636  * Contains html parser and serializer logic.
       
 32637  *
       
 32638  * @namespace tinymce.html
       
 32639  */
       
 32640 
       
 32641 /**
       
 32642  * Contains the different UI types such as buttons, listboxes etc.
       
 32643  *
       
 32644  * @namespace tinymce.ui
       
 32645  */
       
 32646 
       
 32647 /**
       
 32648  * Contains various utility classes such as json parser, cookies etc.
       
 32649  *
       
 32650  * @namespace tinymce.util
       
 32651  */
       
 32652 
       
 32653 // Included from: js/tinymce/classes/ui/Layout.js
       
 32654 
       
 32655 /**
       
 32656  * Layout.js
       
 32657  *
       
 32658  * Copyright, Moxiecode Systems AB
       
 32659  * Released under LGPL License.
       
 32660  *
       
 32661  * License: http://www.tinymce.com/license
       
 32662  * Contributing: http://www.tinymce.com/contributing
       
 32663  */
       
 32664 
       
 32665 /**
       
 32666  * Base layout manager class.
       
 32667  *
       
 32668  * @class tinymce.ui.Layout
       
 32669  */
       
 32670 define("tinymce/ui/Layout", [
       
 32671 	"tinymce/util/Class",
       
 32672 	"tinymce/util/Tools"
       
 32673 ], function(Class, Tools) {
       
 32674 	"use strict";
       
 32675 
       
 32676 	return Class.extend({
       
 32677 		Defaults: {
       
 32678 			firstControlClass: 'first',
       
 32679 			lastControlClass: 'last'
       
 32680 		},
       
 32681 
       
 32682 		/**
       
 32683 		 * Constructs a layout instance with the specified settings.
       
 32684 		 *
       
 32685 		 * @constructor
       
 32686 		 * @param {Object} settings Name/value object with settings.
       
 32687 		 */
       
 32688 		init: function(settings) {
       
 32689 			this.settings = Tools.extend({}, this.Defaults, settings);
       
 32690 		},
       
 32691 
       
 32692 		/**
       
 32693 		 * This method gets invoked before the layout renders the controls.
       
 32694 		 *
       
 32695 		 * @method preRender
       
 32696 		 * @param {tinymce.ui.Container} container Container instance to preRender.
       
 32697 		 */
       
 32698 		preRender: function(container) {
       
 32699 			container.addClass(this.settings.containerClass, 'body');
       
 32700 		},
       
 32701 
       
 32702 		/**
       
 32703 		 * Applies layout classes to the container.
       
 32704 		 *
       
 32705 		 * @private
       
 32706 		 */
       
 32707 		applyClasses: function(container) {
       
 32708 			var self = this, settings = self.settings, items, firstClass, lastClass;
       
 32709 
       
 32710 			items = container.items().filter(':visible');
       
 32711 			firstClass = settings.firstControlClass;
       
 32712 			lastClass = settings.lastControlClass;
       
 32713 
       
 32714 			items.each(function(item) {
       
 32715 				item.removeClass(firstClass).removeClass(lastClass);
       
 32716 
       
 32717 				if (settings.controlClass) {
       
 32718 					item.addClass(settings.controlClass);
       
 32719 				}
       
 32720 			});
       
 32721 
       
 32722 			items.eq(0).addClass(firstClass);
       
 32723 			items.eq(-1).addClass(lastClass);
       
 32724 		},
       
 32725 
       
 32726 		/**
       
 32727 		 * Renders the specified container and any layout specific HTML.
       
 32728 		 *
       
 32729 		 * @method renderHtml
       
 32730 		 * @param {tinymce.ui.Container} container Container to render HTML for.
       
 32731 		 */
       
 32732 		renderHtml: function(container) {
       
 32733 			var self = this, settings = self.settings, items, html = '';
       
 32734 
       
 32735 			items = container.items();
       
 32736 			items.eq(0).addClass(settings.firstControlClass);
       
 32737 			items.eq(-1).addClass(settings.lastControlClass);
       
 32738 
       
 32739 			items.each(function(item) {
       
 32740 				if (settings.controlClass) {
       
 32741 					item.addClass(settings.controlClass);
       
 32742 				}
       
 32743 
       
 32744 				html += item.renderHtml();
       
 32745 			});
       
 32746 
       
 32747 			return html;
       
 32748 		},
       
 32749 
       
 32750 		/**
       
 32751 		 * Recalculates the positions of the controls in the specified container.
       
 32752 		 *
       
 32753 		 * @method recalc
       
 32754 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 32755 		 */
       
 32756 		recalc: function() {
       
 32757 		},
       
 32758 
       
 32759 		/**
       
 32760 		 * This method gets invoked after the layout renders the controls.
       
 32761 		 *
       
 32762 		 * @method postRender
       
 32763 		 * @param {tinymce.ui.Container} container Container instance to postRender.
       
 32764 		 */
       
 32765 		postRender: function() {
       
 32766 		}
       
 32767 	});
       
 32768 });
       
 32769 
       
 32770 // Included from: js/tinymce/classes/ui/AbsoluteLayout.js
       
 32771 
       
 32772 /**
       
 32773  * AbsoluteLayout.js
       
 32774  *
       
 32775  * Copyright, Moxiecode Systems AB
       
 32776  * Released under LGPL License.
       
 32777  *
       
 32778  * License: http://www.tinymce.com/license
       
 32779  * Contributing: http://www.tinymce.com/contributing
       
 32780  */
       
 32781 
       
 32782 /**
       
 32783  * LayoutManager for absolute positioning. This layout manager is more of
       
 32784  * a base class for other layouts but can be created and used directly.
       
 32785  *
       
 32786  * @-x-less AbsoluteLayout.less
       
 32787  * @class tinymce.ui.AbsoluteLayout
       
 32788  * @extends tinymce.ui.Layout
       
 32789  */
       
 32790 define("tinymce/ui/AbsoluteLayout", [
       
 32791 	"tinymce/ui/Layout"
       
 32792 ], function(Layout) {
       
 32793 	"use strict";
       
 32794 
       
 32795 	return Layout.extend({
       
 32796 		Defaults: {
       
 32797 			containerClass: 'abs-layout',
       
 32798 			controlClass: 'abs-layout-item'
       
 32799 		},
       
 32800 
       
 32801 		/**
       
 32802 		 * Recalculates the positions of the controls in the specified container.
       
 32803 		 *
       
 32804 		 * @method recalc
       
 32805 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 32806 		 */
       
 32807 		recalc: function(container) {
       
 32808 			container.items().filter(':visible').each(function(ctrl) {
       
 32809 				var settings = ctrl.settings;
       
 32810 
       
 32811 				ctrl.layoutRect({
       
 32812 					x: settings.x,
       
 32813 					y: settings.y,
       
 32814 					w: settings.w,
       
 32815 					h: settings.h
       
 32816 				});
       
 32817 
       
 32818 				if (ctrl.recalc) {
       
 32819 					ctrl.recalc();
       
 32820 				}
       
 32821 			});
       
 32822 		},
       
 32823 
       
 32824 		/**
       
 32825 		 * Renders the specified container and any layout specific HTML.
       
 32826 		 *
       
 32827 		 * @method renderHtml
       
 32828 		 * @param {tinymce.ui.Container} container Container to render HTML for.
       
 32829 		 */
       
 32830 		renderHtml: function(container) {
       
 32831 			return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container);
       
 32832 		}
       
 32833 	});
       
 32834 });
       
 32835 
       
 32836 // Included from: js/tinymce/classes/ui/Tooltip.js
       
 32837 
       
 32838 /**
       
 32839  * Tooltip.js
       
 32840  *
       
 32841  * Copyright, Moxiecode Systems AB
       
 32842  * Released under LGPL License.
       
 32843  *
       
 32844  * License: http://www.tinymce.com/license
       
 32845  * Contributing: http://www.tinymce.com/contributing
       
 32846  */
       
 32847 
       
 32848 /**
       
 32849  * Creates a tooltip instance.
       
 32850  *
       
 32851  * @-x-less ToolTip.less
       
 32852  * @class tinymce.ui.ToolTip
       
 32853  * @extends tinymce.ui.Control
       
 32854  * @mixes tinymce.ui.Movable
       
 32855  */
       
 32856 define("tinymce/ui/Tooltip", [
       
 32857 	"tinymce/ui/Control",
       
 32858 	"tinymce/ui/Movable"
       
 32859 ], function(Control, Movable) {
       
 32860 	return Control.extend({
       
 32861 		Mixins: [Movable],
       
 32862 
       
 32863 		Defaults: {
       
 32864 			classes: 'widget tooltip tooltip-n'
       
 32865 		},
       
 32866 
       
 32867 		/**
       
 32868 		 * Sets/gets the current label text.
       
 32869 		 *
       
 32870 		 * @method text
       
 32871 		 * @param {String} [text] New label text.
       
 32872 		 * @return {String|tinymce.ui.Tooltip} Current text or current label instance.
       
 32873 		 */
       
 32874 		text: function(value) {
       
 32875 			var self = this;
       
 32876 
       
 32877 			if (typeof value != "undefined") {
       
 32878 				self._value = value;
       
 32879 
       
 32880 				if (self._rendered) {
       
 32881 					self.getEl().lastChild.innerHTML = self.encode(value);
       
 32882 				}
       
 32883 
       
 32884 				return self;
       
 32885 			}
       
 32886 
       
 32887 			return self._value;
       
 32888 		},
       
 32889 
       
 32890 		/**
       
 32891 		 * Renders the control as a HTML string.
       
 32892 		 *
       
 32893 		 * @method renderHtml
       
 32894 		 * @return {String} HTML representing the control.
       
 32895 		 */
       
 32896 		renderHtml: function() {
       
 32897 			var self = this, prefix = self.classPrefix;
       
 32898 
       
 32899 			return (
       
 32900 				'<div id="' + self._id + '" class="' + self.classes() + '" role="presentation">' +
       
 32901 					'<div class="' + prefix + 'tooltip-arrow"></div>' +
       
 32902 					'<div class="' + prefix + 'tooltip-inner">' + self.encode(self._text) + '</div>' +
       
 32903 				'</div>'
       
 32904 			);
       
 32905 		},
       
 32906 
       
 32907 		/**
       
 32908 		 * Repaints the control after a layout operation.
       
 32909 		 *
       
 32910 		 * @method repaint
       
 32911 		 */
       
 32912 		repaint: function() {
       
 32913 			var self = this, style, rect;
       
 32914 
       
 32915 			style = self.getEl().style;
       
 32916 			rect = self._layoutRect;
       
 32917 
       
 32918 			style.left = rect.x + 'px';
       
 32919 			style.top = rect.y + 'px';
       
 32920 			style.zIndex = 0xFFFF + 0xFFFF;
       
 32921 		}
       
 32922 	});
       
 32923 });
       
 32924 
       
 32925 // Included from: js/tinymce/classes/ui/Widget.js
       
 32926 
       
 32927 /**
       
 32928  * Widget.js
       
 32929  *
       
 32930  * Copyright, Moxiecode Systems AB
       
 32931  * Released under LGPL License.
       
 32932  *
       
 32933  * License: http://www.tinymce.com/license
       
 32934  * Contributing: http://www.tinymce.com/contributing
       
 32935  */
       
 32936 
       
 32937 /**
       
 32938  * Widget base class a widget is a control that has a tooltip and some basic states.
       
 32939  *
       
 32940  * @class tinymce.ui.Widget
       
 32941  * @extends tinymce.ui.Control
       
 32942  */
       
 32943 define("tinymce/ui/Widget", [
       
 32944 	"tinymce/ui/Control",
       
 32945 	"tinymce/ui/Tooltip"
       
 32946 ], function(Control, Tooltip) {
       
 32947 	"use strict";
       
 32948 
       
 32949 	var tooltip;
       
 32950 
       
 32951 	var Widget = Control.extend({
       
 32952 		/**
       
 32953 		 * Constructs a instance with the specified settings.
       
 32954 		 *
       
 32955 		 * @constructor
       
 32956 		 * @param {Object} settings Name/value object with settings.
       
 32957 		 * @setting {String} tooltip Tooltip text to display when hovering.
       
 32958 		 * @setting {Boolean} autofocus True if the control should be focused when rendered.
       
 32959 		 * @setting {String} text Text to display inside widget.
       
 32960 		 */
       
 32961 		init: function(settings) {
       
 32962 			var self = this;
       
 32963 
       
 32964 			self._super(settings);
       
 32965 			settings = self.settings;
       
 32966 			self.canFocus = true;
       
 32967 
       
 32968 			if (settings.tooltip && Widget.tooltips !== false) {
       
 32969 				self.on('mouseenter', function(e) {
       
 32970 					var tooltip = self.tooltip().moveTo(-0xFFFF);
       
 32971 
       
 32972 					if (e.control == self) {
       
 32973 						var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
       
 32974 
       
 32975 						tooltip.toggleClass('tooltip-n', rel == 'bc-tc');
       
 32976 						tooltip.toggleClass('tooltip-nw', rel == 'bc-tl');
       
 32977 						tooltip.toggleClass('tooltip-ne', rel == 'bc-tr');
       
 32978 
       
 32979 						tooltip.moveRel(self.getEl(), rel);
       
 32980 					} else {
       
 32981 						tooltip.hide();
       
 32982 					}
       
 32983 				});
       
 32984 
       
 32985 				self.on('mouseleave mousedown click', function() {
       
 32986 					self.tooltip().hide();
       
 32987 				});
       
 32988 			}
       
 32989 
       
 32990 			self.aria('label', settings.ariaLabel || settings.tooltip);
       
 32991 		},
       
 32992 
       
 32993 		/**
       
 32994 		 * Returns the current tooltip instance.
       
 32995 		 *
       
 32996 		 * @method tooltip
       
 32997 		 * @return {tinymce.ui.Tooltip} Tooltip instance.
       
 32998 		 */
       
 32999 		tooltip: function() {
       
 33000 			if (!tooltip) {
       
 33001 				tooltip = new Tooltip({type: 'tooltip'});
       
 33002 				tooltip.renderTo();
       
 33003 			}
       
 33004 
       
 33005 			return tooltip;
       
 33006 		},
       
 33007 
       
 33008 		/**
       
 33009 		 * Sets/gets the active state of the widget.
       
 33010 		 *
       
 33011 		 * @method active
       
 33012 		 * @param {Boolean} [state] State if the control is active.
       
 33013 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
       
 33014 		 */
       
 33015 		active: function(state) {
       
 33016 			var self = this, undef;
       
 33017 
       
 33018 			if (state !== undef) {
       
 33019 				self.aria('pressed', state);
       
 33020 				self.toggleClass('active', state);
       
 33021 			}
       
 33022 
       
 33023 			return self._super(state);
       
 33024 		},
       
 33025 
       
 33026 		/**
       
 33027 		 * Sets/gets the disabled state of the widget.
       
 33028 		 *
       
 33029 		 * @method disabled
       
 33030 		 * @param {Boolean} [state] State if the control is disabled.
       
 33031 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
       
 33032 		 */
       
 33033 		disabled: function(state) {
       
 33034 			var self = this, undef;
       
 33035 
       
 33036 			if (state !== undef) {
       
 33037 				self.aria('disabled', state);
       
 33038 				self.toggleClass('disabled', state);
       
 33039 			}
       
 33040 
       
 33041 			return self._super(state);
       
 33042 		},
       
 33043 
       
 33044 		/**
       
 33045 		 * Called after the control has been rendered.
       
 33046 		 *
       
 33047 		 * @method postRender
       
 33048 		 */
       
 33049 		postRender: function() {
       
 33050 			var self = this, settings = self.settings;
       
 33051 
       
 33052 			self._rendered = true;
       
 33053 
       
 33054 			self._super();
       
 33055 
       
 33056 			if (!self.parent() && (settings.width || settings.height)) {
       
 33057 				self.initLayoutRect();
       
 33058 				self.repaint();
       
 33059 			}
       
 33060 
       
 33061 			if (settings.autofocus) {
       
 33062 				self.focus();
       
 33063 			}
       
 33064 		},
       
 33065 
       
 33066 		/**
       
 33067 		 * Removes the current control from DOM and from UI collections.
       
 33068 		 *
       
 33069 		 * @method remove
       
 33070 		 * @return {tinymce.ui.Control} Current control instance.
       
 33071 		 */
       
 33072 		remove: function() {
       
 33073 			this._super();
       
 33074 
       
 33075 			if (tooltip) {
       
 33076 				tooltip.remove();
       
 33077 				tooltip = null;
       
 33078 			}
       
 33079 		}
       
 33080 	});
       
 33081 
       
 33082 	return Widget;
       
 33083 });
       
 33084 
       
 33085 // Included from: js/tinymce/classes/ui/Button.js
       
 33086 
       
 33087 /**
       
 33088  * Button.js
       
 33089  *
       
 33090  * Copyright, Moxiecode Systems AB
       
 33091  * Released under LGPL License.
       
 33092  *
       
 33093  * License: http://www.tinymce.com/license
       
 33094  * Contributing: http://www.tinymce.com/contributing
       
 33095  */
       
 33096 
       
 33097 /**
       
 33098  * This class is used to create buttons. You can create them directly or through the Factory.
       
 33099  *
       
 33100  * @example
       
 33101  * // Create and render a button to the body element
       
 33102  * tinymce.ui.Factory.create({
       
 33103  *     type: 'button',
       
 33104  *     text: 'My button'
       
 33105  * }).renderTo(document.body);
       
 33106  *
       
 33107  * @-x-less Button.less
       
 33108  * @class tinymce.ui.Button
       
 33109  * @extends tinymce.ui.Widget
       
 33110  */
       
 33111 define("tinymce/ui/Button", [
       
 33112 	"tinymce/ui/Widget"
       
 33113 ], function(Widget) {
       
 33114 	"use strict";
       
 33115 
       
 33116 	return Widget.extend({
       
 33117 		Defaults: {
       
 33118 			classes: "widget btn",
       
 33119 			role: "button"
       
 33120 		},
       
 33121 
       
 33122 		/**
       
 33123 		 * Constructs a new button instance with the specified settings.
       
 33124 		 *
       
 33125 		 * @constructor
       
 33126 		 * @param {Object} settings Name/value object with settings.
       
 33127 		 * @setting {String} size Size of the button small|medium|large.
       
 33128 		 * @setting {String} image Image to use for icon.
       
 33129 		 * @setting {String} icon Icon to use for button.
       
 33130 		 */
       
 33131 		init: function(settings) {
       
 33132 			var self = this, size;
       
 33133 
       
 33134 			self.on('click mousedown', function(e) {
       
 33135 				e.preventDefault();
       
 33136 			});
       
 33137 
       
 33138 			self._super(settings);
       
 33139 			size = settings.size;
       
 33140 
       
 33141 			if (settings.subtype) {
       
 33142 				self.addClass(settings.subtype);
       
 33143 			}
       
 33144 
       
 33145 			if (size) {
       
 33146 				self.addClass('btn-' + size);
       
 33147 			}
       
 33148 		},
       
 33149 
       
 33150 		/**
       
 33151 		 * Sets/gets the current button icon.
       
 33152 		 *
       
 33153 		 * @method icon
       
 33154 		 * @param {String} [icon] New icon identifier.
       
 33155 		 * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
       
 33156 		 */
       
 33157 		icon: function(icon) {
       
 33158 			var self = this, prefix = self.classPrefix;
       
 33159 
       
 33160 			if (typeof icon == 'undefined') {
       
 33161 				return self.settings.icon;
       
 33162 			}
       
 33163 
       
 33164 			self.settings.icon = icon;
       
 33165 			icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
       
 33166 
       
 33167 			if (self._rendered) {
       
 33168 				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
       
 33169 
       
 33170 				if (icon) {
       
 33171 					if (!iconElm || iconElm != btnElm.firstChild) {
       
 33172 						iconElm = document.createElement('i');
       
 33173 						btnElm.insertBefore(iconElm, btnElm.firstChild);
       
 33174 					}
       
 33175 
       
 33176 					iconElm.className = icon;
       
 33177 				} else if (iconElm) {
       
 33178 					btnElm.removeChild(iconElm);
       
 33179 				}
       
 33180 
       
 33181 				self.text(self._text); // Set text again to fix whitespace between icon + text
       
 33182 			}
       
 33183 
       
 33184 			return self;
       
 33185 		},
       
 33186 
       
 33187 		/**
       
 33188 		 * Repaints the button for example after it's been resizes by a layout engine.
       
 33189 		 *
       
 33190 		 * @method repaint
       
 33191 		 */
       
 33192 		repaint: function() {
       
 33193 			var btnStyle = this.getEl().firstChild.style;
       
 33194 
       
 33195 			btnStyle.width = btnStyle.height = "100%";
       
 33196 
       
 33197 			this._super();
       
 33198 		},
       
 33199 
       
 33200 		/**
       
 33201 		 * Sets/gets the current button text.
       
 33202 		 *
       
 33203 		 * @method text
       
 33204 		 * @param {String} [text] New button text.
       
 33205 		 * @return {String|tinymce.ui.Button} Current text or current Button instance.
       
 33206 		 */
       
 33207 		text: function(text) {
       
 33208 			var self = this;
       
 33209 
       
 33210 			if (self._rendered) {
       
 33211 				var textNode = self.getEl().lastChild.lastChild;
       
 33212 				if (textNode) {
       
 33213 					textNode.data = self.translate(text);
       
 33214 				}
       
 33215 			}
       
 33216 
       
 33217 			return self._super(text);
       
 33218 		},
       
 33219 
       
 33220 		/**
       
 33221 		 * Renders the control as a HTML string.
       
 33222 		 *
       
 33223 		 * @method renderHtml
       
 33224 		 * @return {String} HTML representing the control.
       
 33225 		 */
       
 33226 		renderHtml: function() {
       
 33227 			var self = this, id = self._id, prefix = self.classPrefix;
       
 33228 			var icon = self.settings.icon, image;
       
 33229 
       
 33230 			image = self.settings.image;
       
 33231 			if (image) {
       
 33232 				icon = 'none';
       
 33233 
       
 33234 				// Support for [high dpi, low dpi] image sources
       
 33235 				if (typeof image != "string") {
       
 33236 					image = window.getSelection ? image[0] : image[1];
       
 33237 				}
       
 33238 
       
 33239 				image = ' style="background-image: url(\'' + image + '\')"';
       
 33240 			} else {
       
 33241 				image = '';
       
 33242 			}
       
 33243 
       
 33244 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
       
 33245 
       
 33246 			return (
       
 33247 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
       
 33248 					'<button role="presentation" type="button" tabindex="-1">' +
       
 33249 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 33250 						(self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') +
       
 33251 					'</button>' +
       
 33252 				'</div>'
       
 33253 			);
       
 33254 		}
       
 33255 	});
       
 33256 });
       
 33257 
       
 33258 // Included from: js/tinymce/classes/ui/ButtonGroup.js
       
 33259 
       
 33260 /**
       
 33261  * ButtonGroup.js
       
 33262  *
       
 33263  * Copyright, Moxiecode Systems AB
       
 33264  * Released under LGPL License.
       
 33265  *
       
 33266  * License: http://www.tinymce.com/license
       
 33267  * Contributing: http://www.tinymce.com/contributing
       
 33268  */
       
 33269 
       
 33270 /**
       
 33271  * This control enables you to put multiple buttons into a group. This is
       
 33272  * useful when you want to combine similar toolbar buttons into a group.
       
 33273  *
       
 33274  * @example
       
 33275  * // Create and render a buttongroup with two buttons to the body element
       
 33276  * tinymce.ui.Factory.create({
       
 33277  *     type: 'buttongroup',
       
 33278  *     items: [
       
 33279  *         {text: 'Button A'},
       
 33280  *         {text: 'Button B'}
       
 33281  *     ]
       
 33282  * }).renderTo(document.body);
       
 33283  *
       
 33284  * @-x-less ButtonGroup.less
       
 33285  * @class tinymce.ui.ButtonGroup
       
 33286  * @extends tinymce.ui.Container
       
 33287  */
       
 33288 define("tinymce/ui/ButtonGroup", [
       
 33289 	"tinymce/ui/Container"
       
 33290 ], function(Container) {
       
 33291 	"use strict";
       
 33292 
       
 33293 	return Container.extend({
       
 33294 		Defaults: {
       
 33295 			defaultType: 'button',
       
 33296 			role: 'group'
       
 33297 		},
       
 33298 
       
 33299 		/**
       
 33300 		 * Renders the control as a HTML string.
       
 33301 		 *
       
 33302 		 * @method renderHtml
       
 33303 		 * @return {String} HTML representing the control.
       
 33304 		 */
       
 33305 		renderHtml: function() {
       
 33306 			var self = this, layout = self._layout;
       
 33307 
       
 33308 			self.addClass('btn-group');
       
 33309 			self.preRender();
       
 33310 			layout.preRender(self);
       
 33311 
       
 33312 			return (
       
 33313 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
       
 33314 					'<div id="' + self._id + '-body">' +
       
 33315 						(self.settings.html || '') + layout.renderHtml(self) +
       
 33316 					'</div>' +
       
 33317 				'</div>'
       
 33318 			);
       
 33319 		}
       
 33320 	});
       
 33321 });
       
 33322 
       
 33323 // Included from: js/tinymce/classes/ui/Checkbox.js
       
 33324 
       
 33325 /**
       
 33326  * Checkbox.js
       
 33327  *
       
 33328  * Copyright, Moxiecode Systems AB
       
 33329  * Released under LGPL License.
       
 33330  *
       
 33331  * License: http://www.tinymce.com/license
       
 33332  * Contributing: http://www.tinymce.com/contributing
       
 33333  */
       
 33334 
       
 33335 /**
       
 33336  * This control creates a custom checkbox.
       
 33337  *
       
 33338  * @example
       
 33339  * // Create and render a checkbox to the body element
       
 33340  * tinymce.ui.Factory.create({
       
 33341  *     type: 'checkbox',
       
 33342  *     checked: true,
       
 33343  *     text: 'My checkbox'
       
 33344  * }).renderTo(document.body);
       
 33345  *
       
 33346  * @-x-less Checkbox.less
       
 33347  * @class tinymce.ui.Checkbox
       
 33348  * @extends tinymce.ui.Widget
       
 33349  */
       
 33350 define("tinymce/ui/Checkbox", [
       
 33351 	"tinymce/ui/Widget"
       
 33352 ], function(Widget) {
       
 33353 	"use strict";
       
 33354 
       
 33355 	return Widget.extend({
       
 33356 		Defaults: {
       
 33357 			classes: "checkbox",
       
 33358 			role: "checkbox",
       
 33359 			checked: false
       
 33360 		},
       
 33361 
       
 33362 		/**
       
 33363 		 * Constructs a new Checkbox instance with the specified settings.
       
 33364 		 *
       
 33365 		 * @constructor
       
 33366 		 * @param {Object} settings Name/value object with settings.
       
 33367 		 * @setting {Boolean} checked True if the checkbox should be checked by default.
       
 33368 		 */
       
 33369 		init: function(settings) {
       
 33370 			var self = this;
       
 33371 
       
 33372 			self._super(settings);
       
 33373 
       
 33374 			self.on('click mousedown', function(e) {
       
 33375 				e.preventDefault();
       
 33376 			});
       
 33377 
       
 33378 			self.on('click', function(e) {
       
 33379 				e.preventDefault();
       
 33380 
       
 33381 				if (!self.disabled()) {
       
 33382 					self.checked(!self.checked());
       
 33383 				}
       
 33384 			});
       
 33385 
       
 33386 			self.checked(self.settings.checked);
       
 33387 		},
       
 33388 
       
 33389 		/**
       
 33390 		 * Getter/setter function for the checked state.
       
 33391 		 *
       
 33392 		 * @method checked
       
 33393 		 * @param {Boolean} [state] State to be set.
       
 33394 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
       
 33395 		 */
       
 33396 		checked: function(state) {
       
 33397 			var self = this;
       
 33398 
       
 33399 			if (typeof state != "undefined") {
       
 33400 				if (state) {
       
 33401 					self.addClass('checked');
       
 33402 				} else {
       
 33403 					self.removeClass('checked');
       
 33404 				}
       
 33405 
       
 33406 				self._checked = state;
       
 33407 				self.aria('checked', state);
       
 33408 
       
 33409 				return self;
       
 33410 			}
       
 33411 
       
 33412 			return self._checked;
       
 33413 		},
       
 33414 
       
 33415 		/**
       
 33416 		 * Getter/setter function for the value state.
       
 33417 		 *
       
 33418 		 * @method value
       
 33419 		 * @param {Boolean} [state] State to be set.
       
 33420 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
       
 33421 		 */
       
 33422 		value: function(state) {
       
 33423 			return this.checked(state);
       
 33424 		},
       
 33425 
       
 33426 		/**
       
 33427 		 * Renders the control as a HTML string.
       
 33428 		 *
       
 33429 		 * @method renderHtml
       
 33430 		 * @return {String} HTML representing the control.
       
 33431 		 */
       
 33432 		renderHtml: function() {
       
 33433 			var self = this, id = self._id, prefix = self.classPrefix;
       
 33434 
       
 33435 			return (
       
 33436 				'<div id="' + id + '" class="' + self.classes() + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' +
       
 33437 					'<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' +
       
 33438 					'<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' +
       
 33439 				'</div>'
       
 33440 			);
       
 33441 		}
       
 33442 	});
       
 33443 });
       
 33444 
       
 33445 // Included from: js/tinymce/classes/ui/ComboBox.js
       
 33446 
       
 33447 /**
       
 33448  * ComboBox.js
       
 33449  *
       
 33450  * Copyright, Moxiecode Systems AB
       
 33451  * Released under LGPL License.
       
 33452  *
       
 33453  * License: http://www.tinymce.com/license
       
 33454  * Contributing: http://www.tinymce.com/contributing
       
 33455  */
       
 33456 
       
 33457 /**
       
 33458  * This class creates a combobox control. Select box that you select a value from or
       
 33459  * type a value into.
       
 33460  *
       
 33461  * @-x-less ComboBox.less
       
 33462  * @class tinymce.ui.ComboBox
       
 33463  * @extends tinymce.ui.Widget
       
 33464  */
       
 33465 define("tinymce/ui/ComboBox", [
       
 33466 	"tinymce/ui/Widget",
       
 33467 	"tinymce/ui/Factory",
       
 33468 	"tinymce/ui/DomUtils"
       
 33469 ], function(Widget, Factory, DomUtils) {
       
 33470 	"use strict";
       
 33471 
       
 33472 	return Widget.extend({
       
 33473 		/**
       
 33474 		 * Constructs a new control instance with the specified settings.
       
 33475 		 *
       
 33476 		 * @constructor
       
 33477 		 * @param {Object} settings Name/value object with settings.
       
 33478 		 * @setting {String} placeholder Placeholder text to display.
       
 33479 		 */
       
 33480 		init: function(settings) {
       
 33481 			var self = this;
       
 33482 
       
 33483 			self._super(settings);
       
 33484 			self.addClass('combobox');
       
 33485 			self.subinput = true;
       
 33486 			self.ariaTarget = 'inp'; // TODO: Figure out a better way
       
 33487 
       
 33488 			settings = self.settings;
       
 33489 			settings.menu = settings.menu || settings.values;
       
 33490 
       
 33491 			if (settings.menu) {
       
 33492 				settings.icon = 'caret';
       
 33493 			}
       
 33494 
       
 33495 			self.on('click', function(e) {
       
 33496 				var elm = e.target, root = self.getEl();
       
 33497 
       
 33498 				while (elm && elm != root) {
       
 33499 					if (elm.id && elm.id.indexOf('-open') != -1) {
       
 33500 						self.fire('action');
       
 33501 
       
 33502 						if (settings.menu) {
       
 33503 							self.showMenu();
       
 33504 
       
 33505 							if (e.aria) {
       
 33506 								self.menu.items()[0].focus();
       
 33507 							}
       
 33508 						}
       
 33509 					}
       
 33510 
       
 33511 					elm = elm.parentNode;
       
 33512 				}
       
 33513 			});
       
 33514 
       
 33515 			// TODO: Rework this
       
 33516 			self.on('keydown', function(e) {
       
 33517 				if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
       
 33518 					self.parents().reverse().each(function(ctrl) {
       
 33519 						e.preventDefault();
       
 33520 						self.fire('change');
       
 33521 
       
 33522 						if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
       
 33523 							ctrl.fire('submit', {data: ctrl.toJSON()});
       
 33524 							return false;
       
 33525 						}
       
 33526 					});
       
 33527 				}
       
 33528 			});
       
 33529 
       
 33530 			if (settings.placeholder) {
       
 33531 				self.addClass('placeholder');
       
 33532 
       
 33533 				self.on('focusin', function() {
       
 33534 					if (!self._hasOnChange) {
       
 33535 						DomUtils.on(self.getEl('inp'), 'change', function() {
       
 33536 							self.fire('change');
       
 33537 						});
       
 33538 
       
 33539 						self._hasOnChange = true;
       
 33540 					}
       
 33541 
       
 33542 					if (self.hasClass('placeholder')) {
       
 33543 						self.getEl('inp').value = '';
       
 33544 						self.removeClass('placeholder');
       
 33545 					}
       
 33546 				});
       
 33547 
       
 33548 				self.on('focusout', function() {
       
 33549 					if (self.value().length === 0) {
       
 33550 						self.getEl('inp').value = settings.placeholder;
       
 33551 						self.addClass('placeholder');
       
 33552 					}
       
 33553 				});
       
 33554 			}
       
 33555 		},
       
 33556 
       
 33557 		showMenu: function() {
       
 33558 			var self = this, settings = self.settings, menu;
       
 33559 
       
 33560 			if (!self.menu) {
       
 33561 				menu = settings.menu || [];
       
 33562 
       
 33563 				// Is menu array then auto constuct menu control
       
 33564 				if (menu.length) {
       
 33565 					menu = {
       
 33566 						type: 'menu',
       
 33567 						items: menu
       
 33568 					};
       
 33569 				} else {
       
 33570 					menu.type = menu.type || 'menu';
       
 33571 				}
       
 33572 
       
 33573 				self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
       
 33574 				self.fire('createmenu');
       
 33575 				self.menu.reflow();
       
 33576 				self.menu.on('cancel', function(e) {
       
 33577 					if (e.control === self.menu) {
       
 33578 						self.focus();
       
 33579 					}
       
 33580 				});
       
 33581 
       
 33582 				self.menu.on('show hide', function(e) {
       
 33583 					e.control.items().each(function(ctrl) {
       
 33584 						ctrl.active(ctrl.value() == self.value());
       
 33585 					});
       
 33586 				}).fire('show');
       
 33587 
       
 33588 				self.menu.on('select', function(e) {
       
 33589 					self.value(e.control.value());
       
 33590 				});
       
 33591 
       
 33592 				self.on('focusin', function(e) {
       
 33593 					if (e.target.tagName.toUpperCase() == 'INPUT') {
       
 33594 						self.menu.hide();
       
 33595 					}
       
 33596 				});
       
 33597 
       
 33598 				self.aria('expanded', true);
       
 33599 			}
       
 33600 
       
 33601 			self.menu.show();
       
 33602 			self.menu.layoutRect({w: self.layoutRect().w});
       
 33603 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
       
 33604 		},
       
 33605 
       
 33606 		/**
       
 33607 		 * Getter/setter function for the control value.
       
 33608 		 *
       
 33609 		 * @method value
       
 33610 		 * @param {String} [value] Value to be set.
       
 33611 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
       
 33612 		 */
       
 33613 		value: function(value) {
       
 33614 			var self = this;
       
 33615 
       
 33616 			if (typeof value != "undefined") {
       
 33617 				self._value = value;
       
 33618 				self.removeClass('placeholder');
       
 33619 
       
 33620 				if (self._rendered) {
       
 33621 					self.getEl('inp').value = value;
       
 33622 				}
       
 33623 
       
 33624 				return self;
       
 33625 			}
       
 33626 
       
 33627 			if (self._rendered) {
       
 33628 				value = self.getEl('inp').value;
       
 33629 
       
 33630 				if (value != self.settings.placeholder) {
       
 33631 					return value;
       
 33632 				}
       
 33633 
       
 33634 				return '';
       
 33635 			}
       
 33636 
       
 33637 			return self._value;
       
 33638 		},
       
 33639 
       
 33640 		/**
       
 33641 		 * Getter/setter function for the disabled state.
       
 33642 		 *
       
 33643 		 * @method value
       
 33644 		 * @param {Boolean} [state] State to be set.
       
 33645 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
       
 33646 		 */
       
 33647 		disabled: function(state) {
       
 33648 			var self = this;
       
 33649 
       
 33650 			if (self._rendered && typeof state != 'undefined') {
       
 33651 				self.getEl('inp').disabled = state;
       
 33652 			}
       
 33653 
       
 33654 			return self._super(state);
       
 33655 		},
       
 33656 
       
 33657 		/**
       
 33658 		 * Focuses the input area of the control.
       
 33659 		 *
       
 33660 		 * @method focus
       
 33661 		 */
       
 33662 		focus: function() {
       
 33663 			this.getEl('inp').focus();
       
 33664 		},
       
 33665 
       
 33666 		/**
       
 33667 		 * Repaints the control after a layout operation.
       
 33668 		 *
       
 33669 		 * @method repaint
       
 33670 		 */
       
 33671 		repaint: function() {
       
 33672 			var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
       
 33673 			var width, lineHeight;
       
 33674 
       
 33675 			if (openElm) {
       
 33676 				width = rect.w - DomUtils.getSize(openElm).width - 10;
       
 33677 			} else {
       
 33678 				width = rect.w - 10;
       
 33679 			}
       
 33680 
       
 33681 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
       
 33682 			var doc = document;
       
 33683 			if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
       
 33684 				lineHeight = (self.layoutRect().h - 2) + 'px';
       
 33685 			}
       
 33686 
       
 33687 			DomUtils.css(elm.firstChild, {
       
 33688 				width: width,
       
 33689 				lineHeight: lineHeight
       
 33690 			});
       
 33691 
       
 33692 			self._super();
       
 33693 
       
 33694 			return self;
       
 33695 		},
       
 33696 
       
 33697 		/**
       
 33698 		 * Post render method. Called after the control has been rendered to the target.
       
 33699 		 *
       
 33700 		 * @method postRender
       
 33701 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
       
 33702 		 */
       
 33703 		postRender: function() {
       
 33704 			var self = this;
       
 33705 
       
 33706 			DomUtils.on(this.getEl('inp'), 'change', function() {
       
 33707 				self.fire('change');
       
 33708 			});
       
 33709 
       
 33710 			return self._super();
       
 33711 		},
       
 33712 
       
 33713 		remove: function() {
       
 33714 			DomUtils.off(this.getEl('inp'));
       
 33715 			this._super();
       
 33716 		},
       
 33717 
       
 33718 		/**
       
 33719 		 * Renders the control as a HTML string.
       
 33720 		 *
       
 33721 		 * @method renderHtml
       
 33722 		 * @return {String} HTML representing the control.
       
 33723 		 */
       
 33724 		renderHtml: function() {
       
 33725 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
       
 33726 			var value = settings.value || settings.placeholder || '';
       
 33727 			var icon, text, openBtnHtml = '', extraAttrs = '';
       
 33728 
       
 33729 			if ("spellcheck" in settings) {
       
 33730 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
       
 33731 			}
       
 33732 
       
 33733 			if (settings.maxLength) {
       
 33734 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
       
 33735 			}
       
 33736 
       
 33737 			if (settings.size) {
       
 33738 				extraAttrs += ' size="' + settings.size + '"';
       
 33739 			}
       
 33740 
       
 33741 			if (settings.subtype) {
       
 33742 				extraAttrs += ' type="' + settings.subtype + '"';
       
 33743 			}
       
 33744 
       
 33745 			if (self.disabled()) {
       
 33746 				extraAttrs += ' disabled="disabled"';
       
 33747 			}
       
 33748 
       
 33749 			icon = settings.icon;
       
 33750 			if (icon && icon != 'caret') {
       
 33751 				icon = prefix + 'ico ' + prefix + 'i-' + settings.icon;
       
 33752 			}
       
 33753 
       
 33754 			text = self._text;
       
 33755 
       
 33756 			if (icon || text) {
       
 33757 				openBtnHtml = (
       
 33758 					'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' +
       
 33759 						'<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' +
       
 33760 							(icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') +
       
 33761 							(text ? (icon ? ' ' : '') + text : '') +
       
 33762 						'</button>' +
       
 33763 					'</div>'
       
 33764 				);
       
 33765 
       
 33766 				self.addClass('has-open');
       
 33767 			}
       
 33768 
       
 33769 			return (
       
 33770 				'<div id="' + id + '" class="' + self.classes() + '">' +
       
 33771 					'<input id="' + id + '-inp" class="' + prefix + 'textbox ' + prefix + 'placeholder" value="' +
       
 33772 					value + '" hidefocus="1"' + extraAttrs + ' />' +
       
 33773 					openBtnHtml +
       
 33774 				'</div>'
       
 33775 			);
       
 33776 		}
       
 33777 	});
       
 33778 });
       
 33779 
       
 33780 // Included from: js/tinymce/classes/ui/ColorBox.js
       
 33781 
       
 33782 /**
       
 33783  * ColorBox.js
       
 33784  *
       
 33785  * Copyright, Moxiecode Systems AB
       
 33786  * Released under LGPL License.
       
 33787  *
       
 33788  * License: http://www.tinymce.com/license
       
 33789  * Contributing: http://www.tinymce.com/contributing
       
 33790  */
       
 33791 
       
 33792 /**
       
 33793  * This widget lets you enter colors and browse for colors by pressing the color button. It also displays
       
 33794  * a preview of the current color.
       
 33795  *
       
 33796  * @-x-less ColorBox.less
       
 33797  * @class tinymce.ui.ColorBox
       
 33798  * @extends tinymce.ui.ComboBox
       
 33799  */
       
 33800 define("tinymce/ui/ColorBox", [
       
 33801 	"tinymce/ui/ComboBox"
       
 33802 ], function(ComboBox) {
       
 33803 	"use strict";
       
 33804 
       
 33805 	return ComboBox.extend({
       
 33806 		/**
       
 33807 		 * Constructs a new control instance with the specified settings.
       
 33808 		 *
       
 33809 		 * @constructor
       
 33810 		 * @param {Object} settings Name/value object with settings.
       
 33811 		 */
       
 33812 		init: function(settings) {
       
 33813 			var self = this;
       
 33814 
       
 33815 			settings.spellcheck = false;
       
 33816 
       
 33817 			if (settings.onaction) {
       
 33818 				settings.icon = 'none';
       
 33819 			}
       
 33820 
       
 33821 			self._super(settings);
       
 33822 
       
 33823 			self.addClass('colorbox');
       
 33824 			self.on('change keyup postrender', function() {
       
 33825 				self.repaintColor(self.value());
       
 33826 			});
       
 33827 		},
       
 33828 
       
 33829 		repaintColor: function(value) {
       
 33830 			var elm = this.getEl().getElementsByTagName('i')[0];
       
 33831 
       
 33832 			if (elm) {
       
 33833 				try {
       
 33834 					elm.style.background = value;
       
 33835 				} catch (ex) {
       
 33836 					// Ignore
       
 33837 				}
       
 33838 			}
       
 33839 		},
       
 33840 
       
 33841 		value: function(value) {
       
 33842 			var self = this;
       
 33843 
       
 33844 			if (typeof value != "undefined") {
       
 33845 				if (self._rendered) {
       
 33846 					self.repaintColor(value);
       
 33847 				}
       
 33848 			}
       
 33849 
       
 33850 			return self._super(value);
       
 33851 		}
       
 33852 	});
       
 33853 });
       
 33854 
       
 33855 // Included from: js/tinymce/classes/ui/PanelButton.js
       
 33856 
       
 33857 /**
       
 33858  * PanelButton.js
       
 33859  *
       
 33860  * Copyright, Moxiecode Systems AB
       
 33861  * Released under LGPL License.
       
 33862  *
       
 33863  * License: http://www.tinymce.com/license
       
 33864  * Contributing: http://www.tinymce.com/contributing
       
 33865  */
       
 33866 
       
 33867 /**
       
 33868  * Creates a new panel button.
       
 33869  *
       
 33870  * @class tinymce.ui.PanelButton
       
 33871  * @extends tinymce.ui.Button
       
 33872  */
       
 33873 define("tinymce/ui/PanelButton", [
       
 33874 	"tinymce/ui/Button",
       
 33875 	"tinymce/ui/FloatPanel"
       
 33876 ], function(Button, FloatPanel) {
       
 33877 	"use strict";
       
 33878 
       
 33879 	return Button.extend({
       
 33880 		/**
       
 33881 		 * Shows the panel for the button.
       
 33882 		 *
       
 33883 		 * @method showPanel
       
 33884 		 */
       
 33885 		showPanel: function() {
       
 33886 			var self = this, settings = self.settings;
       
 33887 
       
 33888 			self.active(true);
       
 33889 
       
 33890 			if (!self.panel) {
       
 33891 				var panelSettings = settings.panel;
       
 33892 
       
 33893 				// Wrap panel in grid layout if type if specified
       
 33894 				// This makes it possible to add forms or other containers directly in the panel option
       
 33895 				if (panelSettings.type) {
       
 33896 					panelSettings = {
       
 33897 						layout: 'grid',
       
 33898 						items: panelSettings
       
 33899 					};
       
 33900 				}
       
 33901 
       
 33902 				panelSettings.role = panelSettings.role || 'dialog';
       
 33903 				panelSettings.popover = true;
       
 33904 				panelSettings.autohide = true;
       
 33905 				panelSettings.ariaRoot = true;
       
 33906 
       
 33907 				self.panel = new FloatPanel(panelSettings).on('hide', function() {
       
 33908 					self.active(false);
       
 33909 				}).on('cancel', function(e) {
       
 33910 					e.stopPropagation();
       
 33911 					self.focus();
       
 33912 					self.hidePanel();
       
 33913 				}).parent(self).renderTo(self.getContainerElm());
       
 33914 
       
 33915 				self.panel.fire('show');
       
 33916 				self.panel.reflow();
       
 33917 			} else {
       
 33918 				self.panel.show();
       
 33919 			}
       
 33920 
       
 33921 			self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc']));
       
 33922 		},
       
 33923 
       
 33924 		/**
       
 33925 		 * Hides the panel for the button.
       
 33926 		 *
       
 33927 		 * @method hidePanel
       
 33928 		 */
       
 33929 		hidePanel: function() {
       
 33930 			var self = this;
       
 33931 
       
 33932 			if (self.panel) {
       
 33933 				self.panel.hide();
       
 33934 			}
       
 33935 		},
       
 33936 
       
 33937 		/**
       
 33938 		 * Called after the control has been rendered.
       
 33939 		 *
       
 33940 		 * @method postRender
       
 33941 		 */
       
 33942 		postRender: function() {
       
 33943 			var self = this;
       
 33944 
       
 33945 			self.aria('haspopup', true);
       
 33946 
       
 33947 			self.on('click', function(e) {
       
 33948 				if (e.control === self) {
       
 33949 					if (self.panel && self.panel.visible()) {
       
 33950 						self.hidePanel();
       
 33951 					} else {
       
 33952 						self.showPanel();
       
 33953 						self.panel.focus(!!e.aria);
       
 33954 					}
       
 33955 				}
       
 33956 			});
       
 33957 
       
 33958 			return self._super();
       
 33959 		},
       
 33960 
       
 33961 		remove: function() {
       
 33962 			if (this.panel) {
       
 33963 				this.panel.remove();
       
 33964 				this.panel = null;
       
 33965 			}
       
 33966 
       
 33967 			return this._super();
       
 33968 		}
       
 33969 	});
       
 33970 });
       
 33971 
       
 33972 // Included from: js/tinymce/classes/ui/ColorButton.js
       
 33973 
       
 33974 /**
       
 33975  * ColorButton.js
       
 33976  *
       
 33977  * Copyright, Moxiecode Systems AB
       
 33978  * Released under LGPL License.
       
 33979  *
       
 33980  * License: http://www.tinymce.com/license
       
 33981  * Contributing: http://www.tinymce.com/contributing
       
 33982  */
       
 33983 
       
 33984 /**
       
 33985  * This class creates a color button control. This is a split button in which the main
       
 33986  * button has a visual representation of the currently selected color. When clicked
       
 33987  * the caret button displays a color picker, allowing the user to select a new color.
       
 33988  *
       
 33989  * @-x-less ColorButton.less
       
 33990  * @class tinymce.ui.ColorButton
       
 33991  * @extends tinymce.ui.PanelButton
       
 33992  */
       
 33993 define("tinymce/ui/ColorButton", [
       
 33994 	"tinymce/ui/PanelButton",
       
 33995 	"tinymce/dom/DOMUtils"
       
 33996 ], function(PanelButton, DomUtils) {
       
 33997 	"use strict";
       
 33998 
       
 33999 	var DOM = DomUtils.DOM;
       
 34000 
       
 34001 	return PanelButton.extend({
       
 34002 		/**
       
 34003 		 * Constructs a new ColorButton instance with the specified settings.
       
 34004 		 *
       
 34005 		 * @constructor
       
 34006 		 * @param {Object} settings Name/value object with settings.
       
 34007 		 */
       
 34008 		init: function(settings) {
       
 34009 			this._super(settings);
       
 34010 			this.addClass('colorbutton');
       
 34011 		},
       
 34012 
       
 34013 		/**
       
 34014 		 * Getter/setter for the current color.
       
 34015 		 *
       
 34016 		 * @method color
       
 34017 		 * @param {String} [color] Color to set.
       
 34018 		 * @return {String|tinymce.ui.ColorButton} Current color or current instance.
       
 34019 		 */
       
 34020 		color: function(color) {
       
 34021 			if (color) {
       
 34022 				this._color = color;
       
 34023 				this.getEl('preview').style.backgroundColor = color;
       
 34024 				return this;
       
 34025 			}
       
 34026 
       
 34027 			return this._color;
       
 34028 		},
       
 34029 
       
 34030 		/**
       
 34031 		 * Resets the current color.
       
 34032 		 *
       
 34033 		 * @method resetColor
       
 34034 		 * @return {tinymce.ui.ColorButton} Current instance.
       
 34035 		 */
       
 34036 		resetColor: function() {
       
 34037 			this._color = null;
       
 34038 			this.getEl('preview').style.backgroundColor = null;
       
 34039 			return this;
       
 34040 		},
       
 34041 
       
 34042 		/**
       
 34043 		 * Renders the control as a HTML string.
       
 34044 		 *
       
 34045 		 * @method renderHtml
       
 34046 		 * @return {String} HTML representing the control.
       
 34047 		 */
       
 34048 		renderHtml: function() {
       
 34049 			var self = this, id = self._id, prefix = self.classPrefix;
       
 34050 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
       
 34051 			var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '';
       
 34052 
       
 34053 			return (
       
 34054 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1" aria-haspopup="true">' +
       
 34055 					'<button role="presentation" hidefocus="1" type="button" tabindex="-1">' +
       
 34056 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 34057 						'<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' +
       
 34058 						(self._text ? (icon ? ' ' : '') + (self._text) : '') +
       
 34059 					'</button>' +
       
 34060 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
       
 34061 						' <i class="' + prefix + 'caret"></i>' +
       
 34062 					'</button>' +
       
 34063 				'</div>'
       
 34064 			);
       
 34065 		},
       
 34066 
       
 34067 		/**
       
 34068 		 * Called after the control has been rendered.
       
 34069 		 *
       
 34070 		 * @method postRender
       
 34071 		 */
       
 34072 		postRender: function() {
       
 34073 			var self = this, onClickHandler = self.settings.onclick;
       
 34074 
       
 34075 			self.on('click', function(e) {
       
 34076 				if (e.aria && e.aria.key == 'down') {
       
 34077 					return;
       
 34078 				}
       
 34079 
       
 34080 				if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) {
       
 34081 					e.stopImmediatePropagation();
       
 34082 					onClickHandler.call(self, e);
       
 34083 				}
       
 34084 			});
       
 34085 
       
 34086 			delete self.settings.onclick;
       
 34087 
       
 34088 			return self._super();
       
 34089 		}
       
 34090 	});
       
 34091 });
       
 34092 
       
 34093 // Included from: js/tinymce/classes/util/Color.js
       
 34094 
       
 34095 /**
       
 34096  * Color.js
       
 34097  *
       
 34098  * Copyright, Moxiecode Systems AB
       
 34099  * Released under LGPL License.
       
 34100  *
       
 34101  * License: http://www.tinymce.com/license
       
 34102  * Contributing: http://www.tinymce.com/contributing
       
 34103  */
       
 34104 
       
 34105 /**
       
 34106  * This class lets you parse/serialize colors and convert rgb/hsb.
       
 34107  *
       
 34108  * @class tinymce.util.Color
       
 34109  * @example
       
 34110  * var white = new tinymce.util.Color({r: 255, g: 255, b: 255});
       
 34111  * var red = new tinymce.util.Color('#FF0000');
       
 34112  *
       
 34113  * console.log(white.toHex(), red.toHsv());
       
 34114  */
       
 34115 define("tinymce/util/Color", [], function() {
       
 34116 	var min = Math.min, max = Math.max, round = Math.round;
       
 34117 
       
 34118 	/**
       
 34119 	 * Constructs a new color instance.
       
 34120 	 *
       
 34121 	 * @constructor
       
 34122 	 * @method Color
       
 34123 	 * @param {String} value Optional initial value to parse.
       
 34124 	 */
       
 34125 	function Color(value) {
       
 34126 		var self = this, r = 0, g = 0, b = 0;
       
 34127 
       
 34128 		function rgb2hsv(r, g, b) {
       
 34129 			var h, s, v, d, minRGB, maxRGB;
       
 34130 
       
 34131 			h = 0;
       
 34132 			s = 0;
       
 34133 			v = 0;
       
 34134 			r = r / 255;
       
 34135 			g = g / 255;
       
 34136 			b = b / 255;
       
 34137 
       
 34138 			minRGB = min(r, min(g, b));
       
 34139 			maxRGB = max(r, max(g, b));
       
 34140 
       
 34141 			if (minRGB == maxRGB) {
       
 34142 				v = minRGB;
       
 34143 
       
 34144 				return {
       
 34145 					h: 0,
       
 34146 					s: 0,
       
 34147 					v: v * 100
       
 34148 				};
       
 34149 			}
       
 34150 
       
 34151 			/*eslint no-nested-ternary:0 */
       
 34152 			d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r);
       
 34153 			h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5);
       
 34154 			h = 60 * (h - d / (maxRGB - minRGB));
       
 34155 			s = (maxRGB - minRGB) / maxRGB;
       
 34156 			v = maxRGB;
       
 34157 
       
 34158 			return {
       
 34159 				h: round(h),
       
 34160 				s: round(s * 100),
       
 34161 				v: round(v * 100)
       
 34162 			};
       
 34163 		}
       
 34164 
       
 34165 		function hsvToRgb(hue, saturation, brightness) {
       
 34166 			var side, chroma, x, match;
       
 34167 
       
 34168 			hue = (parseInt(hue, 10) || 0) % 360;
       
 34169 			saturation = parseInt(saturation, 10) / 100;
       
 34170 			brightness = parseInt(brightness, 10) / 100;
       
 34171 			saturation = max(0, min(saturation, 1));
       
 34172 			brightness = max(0, min(brightness, 1));
       
 34173 
       
 34174 			if (saturation === 0) {
       
 34175 				r = g = b = round(255 * brightness);
       
 34176 				return;
       
 34177 			}
       
 34178 
       
 34179 			side = hue / 60;
       
 34180 			chroma = brightness * saturation;
       
 34181 			x = chroma * (1 - Math.abs(side % 2 - 1));
       
 34182 			match = brightness - chroma;
       
 34183 
       
 34184 			switch (Math.floor(side)) {
       
 34185 				case 0:
       
 34186 					r = chroma;
       
 34187 					g = x;
       
 34188 					b = 0;
       
 34189 					break;
       
 34190 
       
 34191 				case 1:
       
 34192 					r = x;
       
 34193 					g = chroma;
       
 34194 					b = 0;
       
 34195 					break;
       
 34196 
       
 34197 				case 2:
       
 34198 					r = 0;
       
 34199 					g = chroma;
       
 34200 					b = x;
       
 34201 					break;
       
 34202 
       
 34203 				case 3:
       
 34204 					r = 0;
       
 34205 					g = x;
       
 34206 					b = chroma;
       
 34207 					break;
       
 34208 
       
 34209 				case 4:
       
 34210 					r = x;
       
 34211 					g = 0;
       
 34212 					b = chroma;
       
 34213 					break;
       
 34214 
       
 34215 				case 5:
       
 34216 					r = chroma;
       
 34217 					g = 0;
       
 34218 					b = x;
       
 34219 					break;
       
 34220 
       
 34221 				default:
       
 34222 					r = g = b = 0;
       
 34223 			}
       
 34224 
       
 34225 			r = round(255 * (r + match));
       
 34226 			g = round(255 * (g + match));
       
 34227 			b = round(255 * (b + match));
       
 34228 		}
       
 34229 
       
 34230 		/**
       
 34231 		 * Returns the hex string of the current color. For example: #ff00ff
       
 34232 		 *
       
 34233 		 * @method toHex
       
 34234 		 * @return {String} Hex string of current color.
       
 34235 		 */
       
 34236 		function toHex() {
       
 34237 			function hex(val) {
       
 34238 				val = parseInt(val, 10).toString(16);
       
 34239 
       
 34240 				return val.length > 1 ? val : '0' + val;
       
 34241 			}
       
 34242 
       
 34243 			return '#' + hex(r) + hex(g) + hex(b);
       
 34244 		}
       
 34245 
       
 34246 		/**
       
 34247 		 * Returns the r, g, b values of the color. Each channel has a range from 0-255.
       
 34248 		 *
       
 34249 		 * @method toRgb
       
 34250 		 * @return {Object} Object with r, g, b fields.
       
 34251 		 */
       
 34252 		function toRgb() {
       
 34253 			return {
       
 34254 				r: r,
       
 34255 				g: g,
       
 34256 				b: b
       
 34257 			};
       
 34258 		}
       
 34259 
       
 34260 		/**
       
 34261 		 * Returns the h, s, v values of the color. Ranges: h=0-360, s=0-100, v=0-100.
       
 34262 		 *
       
 34263 		 * @method toHsv
       
 34264 		 * @return {Object} Object with h, s, v fields.
       
 34265 		 */
       
 34266 		function toHsv() {
       
 34267 			return rgb2hsv(r, g, b);
       
 34268 		}
       
 34269 
       
 34270 		/**
       
 34271 		 * Parses the specified value and populates the color instance.
       
 34272 		 *
       
 34273 		 * Supported format examples:
       
 34274 		 *  * rbg(255,0,0)
       
 34275 		 *  * #ff0000
       
 34276 		 *  * #fff
       
 34277 		 *  * {r: 255, g: 0, b: 0}
       
 34278 		 *  * {h: 360, s: 100, v: 100}
       
 34279 		 *
       
 34280 		 * @method parse
       
 34281 		 * @param {Object/String} value Color value to parse.
       
 34282 		 * @return {tinymce.util.Color} Current color instance.
       
 34283 		 */
       
 34284 		function parse(value) {
       
 34285 			var matches;
       
 34286 
       
 34287 			if (typeof value == 'object') {
       
 34288 				if ("r" in value) {
       
 34289 					r = value.r;
       
 34290 					g = value.g;
       
 34291 					b = value.b;
       
 34292 				} else if ("v" in value) {
       
 34293 					hsvToRgb(value.h, value.s, value.v);
       
 34294 				}
       
 34295 			} else {
       
 34296 				if ((matches = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(value))) {
       
 34297 					r = parseInt(matches[1], 10);
       
 34298 					g = parseInt(matches[2], 10);
       
 34299 					b = parseInt(matches[3], 10);
       
 34300 				} else if ((matches = /#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(value))) {
       
 34301 					r = parseInt(matches[1], 16);
       
 34302 					g = parseInt(matches[2], 16);
       
 34303 					b = parseInt(matches[3], 16);
       
 34304 				} else if ((matches = /#([0-F])([0-F])([0-F])/gi.exec(value))) {
       
 34305 					r = parseInt(matches[1] + matches[1], 16);
       
 34306 					g = parseInt(matches[2] + matches[2], 16);
       
 34307 					b = parseInt(matches[3] + matches[3], 16);
       
 34308 				}
       
 34309 			}
       
 34310 
       
 34311 			r = r < 0 ? 0 : (r > 255 ? 255 : r);
       
 34312 			g = g < 0 ? 0 : (g > 255 ? 255 : g);
       
 34313 			b = b < 0 ? 0 : (b > 255 ? 255 : b);
       
 34314 
       
 34315 			return self;
       
 34316 		}
       
 34317 
       
 34318 		if (value) {
       
 34319 			parse(value);
       
 34320 		}
       
 34321 
       
 34322 		self.toRgb = toRgb;
       
 34323 		self.toHsv = toHsv;
       
 34324 		self.toHex = toHex;
       
 34325 		self.parse = parse;
       
 34326 	}
       
 34327 
       
 34328 	return Color;
       
 34329 });
       
 34330 
       
 34331 // Included from: js/tinymce/classes/ui/ColorPicker.js
       
 34332 
       
 34333 /**
       
 34334  * ColorPicker.js
       
 34335  *
       
 34336  * Copyright, Moxiecode Systems AB
       
 34337  * Released under LGPL License.
       
 34338  *
       
 34339  * License: http://www.tinymce.com/license
       
 34340  * Contributing: http://www.tinymce.com/contributing
       
 34341  */
       
 34342 
       
 34343 /**
       
 34344  * Color picker widget lets you select colors.
       
 34345  *
       
 34346  * @-x-less ColorPicker.less
       
 34347  * @class tinymce.ui.ColorPicker
       
 34348  * @extends tinymce.ui.Widget
       
 34349  */
       
 34350 define("tinymce/ui/ColorPicker", [
       
 34351 	"tinymce/ui/Widget",
       
 34352 	"tinymce/ui/DragHelper",
       
 34353 	"tinymce/ui/DomUtils",
       
 34354 	"tinymce/util/Color"
       
 34355 ], function(Widget, DragHelper, DomUtils, Color) {
       
 34356 	"use strict";
       
 34357 
       
 34358 	return Widget.extend({
       
 34359 		Defaults: {
       
 34360 			classes: "widget colorpicker"
       
 34361 		},
       
 34362 
       
 34363 		/**
       
 34364 		 * Constructs a new colorpicker instance with the specified settings.
       
 34365 		 *
       
 34366 		 * @constructor
       
 34367 		 * @param {Object} settings Name/value object with settings.
       
 34368 		 * @setting {String} color Initial color value.
       
 34369 		 */
       
 34370 		init: function(settings) {
       
 34371 			this._super(settings);
       
 34372 		},
       
 34373 
       
 34374 		postRender: function() {
       
 34375 			var self = this, color = self.color(), hsv, hueRootElm, huePointElm, svRootElm, svPointElm;
       
 34376 
       
 34377 			hueRootElm = self.getEl('h');
       
 34378 			huePointElm = self.getEl('hp');
       
 34379 			svRootElm = self.getEl('sv');
       
 34380 			svPointElm = self.getEl('svp');
       
 34381 
       
 34382 			function getPos(elm, event) {
       
 34383 				var pos = DomUtils.getPos(elm), x, y;
       
 34384 
       
 34385 				x = event.pageX - pos.x;
       
 34386 				y = event.pageY - pos.y;
       
 34387 
       
 34388 				x = Math.max(0, Math.min(x / elm.clientWidth, 1));
       
 34389 				y = Math.max(0, Math.min(y / elm.clientHeight, 1));
       
 34390 
       
 34391 				return {
       
 34392 					x: x,
       
 34393 					y: y
       
 34394 				};
       
 34395 			}
       
 34396 
       
 34397 			function updateColor(hsv, hueUpdate) {
       
 34398 				var hue = (360 - hsv.h) / 360;
       
 34399 
       
 34400 				DomUtils.css(huePointElm, {
       
 34401 					top: (hue * 100) + '%'
       
 34402 				});
       
 34403 
       
 34404 				if (!hueUpdate) {
       
 34405 					DomUtils.css(svPointElm, {
       
 34406 						left: hsv.s + '%',
       
 34407 						top: (100 - hsv.v) + '%'
       
 34408 					});
       
 34409 				}
       
 34410 
       
 34411 				svRootElm.style.background = new Color({s: 100, v: 100, h: hsv.h}).toHex();
       
 34412 				self.color().parse({s: hsv.s, v: hsv.v, h: hsv.h});
       
 34413 			}
       
 34414 
       
 34415 			function updateSaturationAndValue(e) {
       
 34416 				var pos;
       
 34417 
       
 34418 				pos = getPos(svRootElm, e);
       
 34419 				hsv.s = pos.x * 100;
       
 34420 				hsv.v = (1 - pos.y) * 100;
       
 34421 
       
 34422 				updateColor(hsv);
       
 34423 				self.fire('change');
       
 34424 			}
       
 34425 
       
 34426 			function updateHue(e) {
       
 34427 				var pos;
       
 34428 
       
 34429 				pos = getPos(hueRootElm, e);
       
 34430 				hsv = color.toHsv();
       
 34431 				hsv.h = (1 - pos.y) * 360;
       
 34432 				updateColor(hsv, true);
       
 34433 				self.fire('change');
       
 34434 			}
       
 34435 
       
 34436 			self._repaint = function() {
       
 34437 				hsv = color.toHsv();
       
 34438 				updateColor(hsv);
       
 34439 			};
       
 34440 
       
 34441 			self._super();
       
 34442 
       
 34443 			self._svdraghelper = new DragHelper(self._id + '-sv', {
       
 34444 				start: updateSaturationAndValue,
       
 34445 				drag: updateSaturationAndValue
       
 34446 			});
       
 34447 
       
 34448 			self._hdraghelper = new DragHelper(self._id + '-h', {
       
 34449 				start: updateHue,
       
 34450 				drag: updateHue
       
 34451 			});
       
 34452 
       
 34453 			self._repaint();
       
 34454 		},
       
 34455 
       
 34456 		rgb: function() {
       
 34457 			return this.color().toRgb();
       
 34458 		},
       
 34459 
       
 34460 		value: function(value) {
       
 34461 			var self = this;
       
 34462 
       
 34463 			if (arguments.length) {
       
 34464 				self.color().parse(value);
       
 34465 
       
 34466 				if (self._rendered) {
       
 34467 					self._repaint();
       
 34468 				}
       
 34469 			} else {
       
 34470 				return self.color().toHex();
       
 34471 			}
       
 34472 		},
       
 34473 
       
 34474 		color: function() {
       
 34475 			if (!this._color) {
       
 34476 				this._color = new Color();
       
 34477 			}
       
 34478 
       
 34479 			return this._color;
       
 34480 		},
       
 34481 
       
 34482 		/**
       
 34483 		 * Renders the control as a HTML string.
       
 34484 		 *
       
 34485 		 * @method renderHtml
       
 34486 		 * @return {String} HTML representing the control.
       
 34487 		 */
       
 34488 		renderHtml: function() {
       
 34489 			var self = this, id = self._id, prefix = self.classPrefix, hueHtml;
       
 34490 			var stops = '#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000';
       
 34491 
       
 34492 			function getOldIeFallbackHtml() {
       
 34493 				var i, l, html = '', gradientPrefix, stopsList;
       
 34494 
       
 34495 				gradientPrefix = 'filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=';
       
 34496 				stopsList = stops.split(',');
       
 34497 				for (i = 0, l = stopsList.length - 1; i < l; i++) {
       
 34498 					html += (
       
 34499 						'<div class="' + prefix + 'colorpicker-h-chunk" style="' +
       
 34500 							'height:' + (100 / l) + '%;' +
       
 34501 							gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ');' +
       
 34502 							'-ms-' + gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ')' +
       
 34503 						'"></div>'
       
 34504 					);
       
 34505 				}
       
 34506 
       
 34507 				return html;
       
 34508 			}
       
 34509 
       
 34510 			var gradientCssText = (
       
 34511 				'background: -ms-linear-gradient(top,' + stops + ');' +
       
 34512 				'background: linear-gradient(to bottom,' + stops + ');'
       
 34513 			);
       
 34514 
       
 34515 			hueHtml = (
       
 34516 				'<div id="' + id + '-h" class="' + prefix + 'colorpicker-h" style="' + gradientCssText + '">' +
       
 34517 					getOldIeFallbackHtml() +
       
 34518 					'<div id="' + id + '-hp" class="' + prefix + 'colorpicker-h-marker"></div>' +
       
 34519 				'</div>'
       
 34520 			);
       
 34521 
       
 34522 			return (
       
 34523 				'<div id="' + id + '" class="' + self.classes() + '">' +
       
 34524 					'<div id="' + id + '-sv" class="' + prefix + 'colorpicker-sv">' +
       
 34525 						'<div class="' + prefix + 'colorpicker-overlay1">' +
       
 34526 							'<div class="' + prefix + 'colorpicker-overlay2">' +
       
 34527 								'<div id="' + id + '-svp" class="' + prefix + 'colorpicker-selector1">' +
       
 34528 									'<div class="' + prefix + 'colorpicker-selector2"></div>' +
       
 34529 								'</div>' +
       
 34530 							'</div>' +
       
 34531 						'</div>' +
       
 34532 					'</div>' +
       
 34533 					hueHtml +
       
 34534 				'</div>'
       
 34535 			);
       
 34536 		}
       
 34537 	});
       
 34538 });
       
 34539 
       
 34540 // Included from: js/tinymce/classes/ui/Path.js
       
 34541 
       
 34542 /**
       
 34543  * Path.js
       
 34544  *
       
 34545  * Copyright, Moxiecode Systems AB
       
 34546  * Released under LGPL License.
       
 34547  *
       
 34548  * License: http://www.tinymce.com/license
       
 34549  * Contributing: http://www.tinymce.com/contributing
       
 34550  */
       
 34551 
       
 34552 /**
       
 34553  * Creates a new path control.
       
 34554  *
       
 34555  * @-x-less Path.less
       
 34556  * @class tinymce.ui.Path
       
 34557  * @extends tinymce.ui.Widget
       
 34558  */
       
 34559 define("tinymce/ui/Path", [
       
 34560 	"tinymce/ui/Widget"
       
 34561 ], function(Widget) {
       
 34562 	"use strict";
       
 34563 
       
 34564 	return Widget.extend({
       
 34565 		/**
       
 34566 		 * Constructs a instance with the specified settings.
       
 34567 		 *
       
 34568 		 * @constructor
       
 34569 		 * @param {Object} settings Name/value object with settings.
       
 34570 		 * @setting {String} delimiter Delimiter to display between items in path.
       
 34571 		 */
       
 34572 		init: function(settings) {
       
 34573 			var self = this;
       
 34574 
       
 34575 			if (!settings.delimiter) {
       
 34576 				settings.delimiter = '\u00BB';
       
 34577 			}
       
 34578 
       
 34579 			self._super(settings);
       
 34580 			self.addClass('path');
       
 34581 			self.canFocus = true;
       
 34582 
       
 34583 			self.on('click', function(e) {
       
 34584 				var index, target = e.target;
       
 34585 
       
 34586 				if ((index = target.getAttribute('data-index'))) {
       
 34587 					self.fire('select', {value: self.data()[index], index: index});
       
 34588 				}
       
 34589 			});
       
 34590 		},
       
 34591 
       
 34592 		/**
       
 34593 		 * Focuses the current control.
       
 34594 		 *
       
 34595 		 * @method focus
       
 34596 		 * @return {tinymce.ui.Control} Current control instance.
       
 34597 		 */
       
 34598 		focus: function() {
       
 34599 			var self = this;
       
 34600 
       
 34601 			self.getEl().firstChild.focus();
       
 34602 
       
 34603 			return self;
       
 34604 		},
       
 34605 
       
 34606 		/**
       
 34607 		 * Sets/gets the data to be used for the path.
       
 34608 		 *
       
 34609 		 * @method data
       
 34610 		 * @param {Array} data Array with items name is rendered to path.
       
 34611 		 */
       
 34612 		data: function(data) {
       
 34613 			var self = this;
       
 34614 
       
 34615 			if (typeof data !== "undefined") {
       
 34616 				self._data = data;
       
 34617 				self.update();
       
 34618 
       
 34619 				return self;
       
 34620 			}
       
 34621 
       
 34622 			return self._data;
       
 34623 		},
       
 34624 
       
 34625 		/**
       
 34626 		 * Updated the path.
       
 34627 		 *
       
 34628 		 * @private
       
 34629 		 */
       
 34630 		update: function() {
       
 34631 			this.innerHtml(this._getPathHtml());
       
 34632 		},
       
 34633 
       
 34634 		/**
       
 34635 		 * Called after the control has been rendered.
       
 34636 		 *
       
 34637 		 * @method postRender
       
 34638 		 */
       
 34639 		postRender: function() {
       
 34640 			var self = this;
       
 34641 
       
 34642 			self._super();
       
 34643 
       
 34644 			self.data(self.settings.data);
       
 34645 		},
       
 34646 
       
 34647 		/**
       
 34648 		 * Renders the control as a HTML string.
       
 34649 		 *
       
 34650 		 * @method renderHtml
       
 34651 		 * @return {String} HTML representing the control.
       
 34652 		 */
       
 34653 		renderHtml: function() {
       
 34654 			var self = this;
       
 34655 
       
 34656 			return (
       
 34657 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
       
 34658 					self._getPathHtml() +
       
 34659 				'</div>'
       
 34660 			);
       
 34661 		},
       
 34662 
       
 34663 		_getPathHtml: function() {
       
 34664 			var self = this, parts = self._data || [], i, l, html = '', prefix = self.classPrefix;
       
 34665 
       
 34666 			for (i = 0, l = parts.length; i < l; i++) {
       
 34667 				html += (
       
 34668 					(i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') +
       
 34669 					'<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' +
       
 34670 					i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + i + '">' + parts[i].name + '</div>'
       
 34671 				);
       
 34672 			}
       
 34673 
       
 34674 			if (!html) {
       
 34675 				html = '<div class="' + prefix + 'path-item">\u00a0</div>';
       
 34676 			}
       
 34677 
       
 34678 			return html;
       
 34679 		}
       
 34680 	});
       
 34681 });
       
 34682 
       
 34683 // Included from: js/tinymce/classes/ui/ElementPath.js
       
 34684 
       
 34685 /**
       
 34686  * ElementPath.js
       
 34687  *
       
 34688  * Copyright, Moxiecode Systems AB
       
 34689  * Released under LGPL License.
       
 34690  *
       
 34691  * License: http://www.tinymce.com/license
       
 34692  * Contributing: http://www.tinymce.com/contributing
       
 34693  */
       
 34694 
       
 34695 /**
       
 34696  * This control creates an path for the current selections parent elements in TinyMCE.
       
 34697  *
       
 34698  * @class tinymce.ui.ElementPath
       
 34699  * @extends tinymce.ui.Path
       
 34700  */
       
 34701 define("tinymce/ui/ElementPath", [
       
 34702 	"tinymce/ui/Path",
       
 34703 	"tinymce/EditorManager"
       
 34704 ], function(Path, EditorManager) {
       
 34705 	return Path.extend({
       
 34706 		/**
       
 34707 		 * Post render method. Called after the control has been rendered to the target.
       
 34708 		 *
       
 34709 		 * @method postRender
       
 34710 		 * @return {tinymce.ui.ElementPath} Current combobox instance.
       
 34711 		 */
       
 34712 		postRender: function() {
       
 34713 			var self = this, editor = EditorManager.activeEditor;
       
 34714 
       
 34715 			function isHidden(elm) {
       
 34716 				if (elm.nodeType === 1) {
       
 34717 					if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) {
       
 34718 						return true;
       
 34719 					}
       
 34720 
       
 34721 					if (elm.getAttribute('data-mce-type') === 'bookmark') {
       
 34722 						return true;
       
 34723 					}
       
 34724 				}
       
 34725 
       
 34726 				return false;
       
 34727 			}
       
 34728 
       
 34729 			if (editor.settings.elementpath !== false) {
       
 34730 				self.on('select', function(e) {
       
 34731 					editor.focus();
       
 34732 					editor.selection.select(this.data()[e.index].element);
       
 34733 					editor.nodeChanged();
       
 34734 				});
       
 34735 
       
 34736 				editor.on('nodeChange', function(e) {
       
 34737 					var outParents = [], parents = e.parents, i = parents.length;
       
 34738 
       
 34739 					while (i--) {
       
 34740 						if (parents[i].nodeType == 1 && !isHidden(parents[i])) {
       
 34741 							var args = editor.fire('ResolveName', {
       
 34742 								name: parents[i].nodeName.toLowerCase(),
       
 34743 								target: parents[i]
       
 34744 							});
       
 34745 
       
 34746 							if (!args.isDefaultPrevented()) {
       
 34747 								outParents.push({name: args.name, element: parents[i]});
       
 34748 							}
       
 34749 
       
 34750 							if (args.isPropagationStopped()) {
       
 34751 								break;
       
 34752 							}
       
 34753 						}
       
 34754 					}
       
 34755 
       
 34756 					self.data(outParents);
       
 34757 				});
       
 34758 			}
       
 34759 
       
 34760 			return self._super();
       
 34761 		}
       
 34762 	});
       
 34763 });
       
 34764 
       
 34765 // Included from: js/tinymce/classes/ui/FormItem.js
       
 34766 
       
 34767 /**
       
 34768  * FormItem.js
       
 34769  *
       
 34770  * Copyright, Moxiecode Systems AB
       
 34771  * Released under LGPL License.
       
 34772  *
       
 34773  * License: http://www.tinymce.com/license
       
 34774  * Contributing: http://www.tinymce.com/contributing
       
 34775  */
       
 34776 
       
 34777 /**
       
 34778  * This class is a container created by the form element with
       
 34779  * a label and control item.
       
 34780  *
       
 34781  * @class tinymce.ui.FormItem
       
 34782  * @extends tinymce.ui.Container
       
 34783  * @setting {String} label Label to display for the form item.
       
 34784  */
       
 34785 define("tinymce/ui/FormItem", [
       
 34786 	"tinymce/ui/Container"
       
 34787 ], function(Container) {
       
 34788 	"use strict";
       
 34789 
       
 34790 	return Container.extend({
       
 34791 		Defaults: {
       
 34792 			layout: 'flex',
       
 34793 			align: 'center',
       
 34794 			defaults: {
       
 34795 				flex: 1
       
 34796 			}
       
 34797 		},
       
 34798 
       
 34799 		/**
       
 34800 		 * Renders the control as a HTML string.
       
 34801 		 *
       
 34802 		 * @method renderHtml
       
 34803 		 * @return {String} HTML representing the control.
       
 34804 		 */
       
 34805 		renderHtml: function() {
       
 34806 			var self = this, layout = self._layout, prefix = self.classPrefix;
       
 34807 
       
 34808 			self.addClass('formitem');
       
 34809 			layout.preRender(self);
       
 34810 
       
 34811 			return (
       
 34812 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
       
 34813 					(self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' +
       
 34814 						self.settings.title + '</div>') : '') +
       
 34815 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 34816 						(self.settings.html || '') + layout.renderHtml(self) +
       
 34817 					'</div>' +
       
 34818 				'</div>'
       
 34819 			);
       
 34820 		}
       
 34821 	});
       
 34822 });
       
 34823 
       
 34824 // Included from: js/tinymce/classes/ui/Form.js
       
 34825 
       
 34826 /**
       
 34827  * Form.js
       
 34828  *
       
 34829  * Copyright, Moxiecode Systems AB
       
 34830  * Released under LGPL License.
       
 34831  *
       
 34832  * License: http://www.tinymce.com/license
       
 34833  * Contributing: http://www.tinymce.com/contributing
       
 34834  */
       
 34835 
       
 34836 /**
       
 34837  * This class creates a form container. A form container has the ability
       
 34838  * to automatically wrap items in tinymce.ui.FormItem instances.
       
 34839  *
       
 34840  * Each FormItem instance is a container for the label and the item.
       
 34841  *
       
 34842  * @example
       
 34843  * tinymce.ui.Factory.create({
       
 34844  *     type: 'form',
       
 34845  *     items: [
       
 34846  *         {type: 'textbox', label: 'My text box'}
       
 34847  *     ]
       
 34848  * }).renderTo(document.body);
       
 34849  *
       
 34850  * @class tinymce.ui.Form
       
 34851  * @extends tinymce.ui.Container
       
 34852  */
       
 34853 define("tinymce/ui/Form", [
       
 34854 	"tinymce/ui/Container",
       
 34855 	"tinymce/ui/FormItem",
       
 34856 	"tinymce/util/Tools"
       
 34857 ], function(Container, FormItem, Tools) {
       
 34858 	"use strict";
       
 34859 
       
 34860 	return Container.extend({
       
 34861 		Defaults: {
       
 34862 			containerCls: 'form',
       
 34863 			layout: 'flex',
       
 34864 			direction: 'column',
       
 34865 			align: 'stretch',
       
 34866 			flex: 1,
       
 34867 			padding: 20,
       
 34868 			labelGap: 30,
       
 34869 			spacing: 10,
       
 34870 			callbacks: {
       
 34871 				submit: function() {
       
 34872 					this.submit();
       
 34873 				}
       
 34874 			}
       
 34875 		},
       
 34876 
       
 34877 		/**
       
 34878 		 * This method gets invoked before the control is rendered.
       
 34879 		 *
       
 34880 		 * @method preRender
       
 34881 		 */
       
 34882 		preRender: function() {
       
 34883 			var self = this, items = self.items();
       
 34884 
       
 34885 			if (!self.settings.formItemDefaults) {
       
 34886 				self.settings.formItemDefaults = {
       
 34887 					layout: 'flex',
       
 34888 					autoResize: "overflow",
       
 34889 					defaults: {flex: 1}
       
 34890 				};
       
 34891 			}
       
 34892 
       
 34893 			// Wrap any labeled items in FormItems
       
 34894 			items.each(function(ctrl) {
       
 34895 				var formItem, label = ctrl.settings.label;
       
 34896 
       
 34897 				if (label) {
       
 34898 					formItem = new FormItem(Tools.extend({
       
 34899 						items: {
       
 34900 							type: 'label',
       
 34901 							id: ctrl._id + '-l',
       
 34902 							text: label,
       
 34903 							flex: 0,
       
 34904 							forId: ctrl._id,
       
 34905 							disabled: ctrl.disabled()
       
 34906 						}
       
 34907 					}, self.settings.formItemDefaults));
       
 34908 
       
 34909 					formItem.type = 'formitem';
       
 34910 					ctrl.aria('labelledby', ctrl._id + '-l');
       
 34911 
       
 34912 					if (typeof ctrl.settings.flex == "undefined") {
       
 34913 						ctrl.settings.flex = 1;
       
 34914 					}
       
 34915 
       
 34916 					self.replace(ctrl, formItem);
       
 34917 					formItem.add(ctrl);
       
 34918 				}
       
 34919 			});
       
 34920 		},
       
 34921 
       
 34922 		/**
       
 34923 		 * Recalcs label widths.
       
 34924 		 *
       
 34925 		 * @private
       
 34926 		 */
       
 34927 		recalcLabels: function() {
       
 34928 			var self = this, maxLabelWidth = 0, labels = [], i, labelGap, items;
       
 34929 
       
 34930 			if (self.settings.labelGapCalc === false) {
       
 34931 				return;
       
 34932 			}
       
 34933 
       
 34934 			if (self.settings.labelGapCalc == "children") {
       
 34935 				items = self.find('formitem');
       
 34936 			} else {
       
 34937 				items = self.items();
       
 34938 			}
       
 34939 
       
 34940 			items.filter('formitem').each(function(item) {
       
 34941 				var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
       
 34942 
       
 34943 				maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
       
 34944 				labels.push(labelCtrl);
       
 34945 			});
       
 34946 
       
 34947 			labelGap = self.settings.labelGap || 0;
       
 34948 
       
 34949 			i = labels.length;
       
 34950 			while (i--) {
       
 34951 				labels[i].settings.minWidth = maxLabelWidth + labelGap;
       
 34952 			}
       
 34953 		},
       
 34954 
       
 34955 		/**
       
 34956 		 * Getter/setter for the visibility state.
       
 34957 		 *
       
 34958 		 * @method visible
       
 34959 		 * @param {Boolean} [state] True/false state to show/hide.
       
 34960 		 * @return {tinymce.ui.Form|Boolean} True/false state or current control.
       
 34961 		 */
       
 34962 		visible: function(state) {
       
 34963 			var val = this._super(state);
       
 34964 
       
 34965 			if (state === true && this._rendered) {
       
 34966 				this.recalcLabels();
       
 34967 			}
       
 34968 
       
 34969 			return val;
       
 34970 		},
       
 34971 
       
 34972 		/**
       
 34973 		 * Fires a submit event with the serialized form.
       
 34974 		 *
       
 34975 		 * @method submit
       
 34976 		 * @return {Object} Event arguments object.
       
 34977 		 */
       
 34978 		submit: function() {
       
 34979 			return this.fire('submit', {data: this.toJSON()});
       
 34980 		},
       
 34981 
       
 34982 		/**
       
 34983 		 * Post render method. Called after the control has been rendered to the target.
       
 34984 		 *
       
 34985 		 * @method postRender
       
 34986 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
       
 34987 		 */
       
 34988 		postRender: function() {
       
 34989 			var self = this;
       
 34990 
       
 34991 			self._super();
       
 34992 			self.recalcLabels();
       
 34993 			self.fromJSON(self.settings.data);
       
 34994 		}
       
 34995 	});
       
 34996 });
       
 34997 
       
 34998 // Included from: js/tinymce/classes/ui/FieldSet.js
       
 34999 
       
 35000 /**
       
 35001  * FieldSet.js
       
 35002  *
       
 35003  * Copyright, Moxiecode Systems AB
       
 35004  * Released under LGPL License.
       
 35005  *
       
 35006  * License: http://www.tinymce.com/license
       
 35007  * Contributing: http://www.tinymce.com/contributing
       
 35008  */
       
 35009 
       
 35010 /**
       
 35011  * This class creates fieldset containers.
       
 35012  *
       
 35013  * @-x-less FieldSet.less
       
 35014  * @class tinymce.ui.FieldSet
       
 35015  * @extends tinymce.ui.Form
       
 35016  */
       
 35017 define("tinymce/ui/FieldSet", [
       
 35018 	"tinymce/ui/Form"
       
 35019 ], function(Form) {
       
 35020 	"use strict";
       
 35021 
       
 35022 	return Form.extend({
       
 35023 		Defaults: {
       
 35024 			containerCls: 'fieldset',
       
 35025 			layout: 'flex',
       
 35026 			direction: 'column',
       
 35027 			align: 'stretch',
       
 35028 			flex: 1,
       
 35029 			padding: "25 15 5 15",
       
 35030 			labelGap: 30,
       
 35031 			spacing: 10,
       
 35032 			border: 1
       
 35033 		},
       
 35034 
       
 35035 		/**
       
 35036 		 * Renders the control as a HTML string.
       
 35037 		 *
       
 35038 		 * @method renderHtml
       
 35039 		 * @return {String} HTML representing the control.
       
 35040 		 */
       
 35041 		renderHtml: function() {
       
 35042 			var self = this, layout = self._layout, prefix = self.classPrefix;
       
 35043 
       
 35044 			self.preRender();
       
 35045 			layout.preRender(self);
       
 35046 
       
 35047 			return (
       
 35048 				'<fieldset id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
       
 35049 					(self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' +
       
 35050 						self.settings.title + '</legend>') : '') +
       
 35051 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 35052 						(self.settings.html || '') + layout.renderHtml(self) +
       
 35053 					'</div>' +
       
 35054 				'</fieldset>'
       
 35055 			);
       
 35056 		}
       
 35057 	});
       
 35058 });
       
 35059 
       
 35060 // Included from: js/tinymce/classes/ui/FilePicker.js
       
 35061 
       
 35062 /**
       
 35063  * FilePicker.js
       
 35064  *
       
 35065  * Copyright, Moxiecode Systems AB
       
 35066  * Released under LGPL License.
       
 35067  *
       
 35068  * License: http://www.tinymce.com/license
       
 35069  * Contributing: http://www.tinymce.com/contributing
       
 35070  */
       
 35071 
       
 35072 /*global tinymce:true */
       
 35073 
       
 35074 /**
       
 35075  * This class creates a file picker control.
       
 35076  *
       
 35077  * @class tinymce.ui.FilePicker
       
 35078  * @extends tinymce.ui.ComboBox
       
 35079  */
       
 35080 define("tinymce/ui/FilePicker", [
       
 35081 	"tinymce/ui/ComboBox",
       
 35082 	"tinymce/util/Tools"
       
 35083 ], function(ComboBox, Tools) {
       
 35084 	"use strict";
       
 35085 
       
 35086 	return ComboBox.extend({
       
 35087 		/**
       
 35088 		 * Constructs a new control instance with the specified settings.
       
 35089 		 *
       
 35090 		 * @constructor
       
 35091 		 * @param {Object} settings Name/value object with settings.
       
 35092 		 */
       
 35093 		init: function(settings) {
       
 35094 			var self = this, editor = tinymce.activeEditor, editorSettings = editor.settings;
       
 35095 			var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes;
       
 35096 
       
 35097 			settings.spellcheck = false;
       
 35098 
       
 35099 			fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types;
       
 35100 			if (fileBrowserCallbackTypes) {
       
 35101 				fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
       
 35102 			}
       
 35103 
       
 35104 			if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype]) {
       
 35105 				fileBrowserCallback = editorSettings.file_picker_callback;
       
 35106 				if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
       
 35107 					actionCallback = function() {
       
 35108 						var meta = self.fire('beforecall').meta;
       
 35109 
       
 35110 						meta = Tools.extend({filetype: settings.filetype}, meta);
       
 35111 
       
 35112 						// file_picker_callback(callback, currentValue, metaData)
       
 35113 						fileBrowserCallback.call(
       
 35114 							editor,
       
 35115 							function(value, meta) {
       
 35116 								self.value(value).fire('change', {meta: meta});
       
 35117 							},
       
 35118 							self.value(),
       
 35119 							meta
       
 35120 						);
       
 35121 					};
       
 35122 				} else {
       
 35123 					// Legacy callback: file_picker_callback(id, currentValue, filetype, window)
       
 35124 					fileBrowserCallback = editorSettings.file_browser_callback;
       
 35125 					if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
       
 35126 						actionCallback = function() {
       
 35127 							fileBrowserCallback(
       
 35128 								self.getEl('inp').id,
       
 35129 								self.value(),
       
 35130 								settings.filetype,
       
 35131 								window
       
 35132 							);
       
 35133 						};
       
 35134 					}
       
 35135 				}
       
 35136 			}
       
 35137 
       
 35138 			if (actionCallback) {
       
 35139 				settings.icon = 'browse';
       
 35140 				settings.onaction = actionCallback;
       
 35141 			}
       
 35142 
       
 35143 			self._super(settings);
       
 35144 		}
       
 35145 	});
       
 35146 });
       
 35147 
       
 35148 // Included from: js/tinymce/classes/ui/FitLayout.js
       
 35149 
       
 35150 /**
       
 35151  * FitLayout.js
       
 35152  *
       
 35153  * Copyright, Moxiecode Systems AB
       
 35154  * Released under LGPL License.
       
 35155  *
       
 35156  * License: http://www.tinymce.com/license
       
 35157  * Contributing: http://www.tinymce.com/contributing
       
 35158  */
       
 35159 
       
 35160 /**
       
 35161  * This layout manager will resize the control to be the size of it's parent container.
       
 35162  * In other words width: 100% and height: 100%.
       
 35163  *
       
 35164  * @-x-less FitLayout.less
       
 35165  * @class tinymce.ui.FitLayout
       
 35166  * @extends tinymce.ui.AbsoluteLayout
       
 35167  */
       
 35168 define("tinymce/ui/FitLayout", [
       
 35169 	"tinymce/ui/AbsoluteLayout"
       
 35170 ], function(AbsoluteLayout) {
       
 35171 	"use strict";
       
 35172 
       
 35173 	return AbsoluteLayout.extend({
       
 35174 		/**
       
 35175 		 * Recalculates the positions of the controls in the specified container.
       
 35176 		 *
       
 35177 		 * @method recalc
       
 35178 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 35179 		 */
       
 35180 		recalc: function(container) {
       
 35181 			var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox();
       
 35182 
       
 35183 			container.items().filter(':visible').each(function(ctrl) {
       
 35184 				ctrl.layoutRect({
       
 35185 					x: paddingBox.left,
       
 35186 					y: paddingBox.top,
       
 35187 					w: contLayoutRect.innerW - paddingBox.right - paddingBox.left,
       
 35188 					h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom
       
 35189 				});
       
 35190 
       
 35191 				if (ctrl.recalc) {
       
 35192 					ctrl.recalc();
       
 35193 				}
       
 35194 			});
       
 35195 		}
       
 35196 	});
       
 35197 });
       
 35198 
       
 35199 // Included from: js/tinymce/classes/ui/FlexLayout.js
       
 35200 
       
 35201 /**
       
 35202  * FlexLayout.js
       
 35203  *
       
 35204  * Copyright, Moxiecode Systems AB
       
 35205  * Released under LGPL License.
       
 35206  *
       
 35207  * License: http://www.tinymce.com/license
       
 35208  * Contributing: http://www.tinymce.com/contributing
       
 35209  */
       
 35210 
       
 35211 /**
       
 35212  * This layout manager works similar to the CSS flex box.
       
 35213  *
       
 35214  * @setting {String} direction row|row-reverse|column|column-reverse
       
 35215  * @setting {Number} flex A positive-number to flex by.
       
 35216  * @setting {String} align start|end|center|stretch
       
 35217  * @setting {String} pack start|end|justify
       
 35218  *
       
 35219  * @class tinymce.ui.FlexLayout
       
 35220  * @extends tinymce.ui.AbsoluteLayout
       
 35221  */
       
 35222 define("tinymce/ui/FlexLayout", [
       
 35223 	"tinymce/ui/AbsoluteLayout"
       
 35224 ], function(AbsoluteLayout) {
       
 35225 	"use strict";
       
 35226 
       
 35227 	return AbsoluteLayout.extend({
       
 35228 		/**
       
 35229 		 * Recalculates the positions of the controls in the specified container.
       
 35230 		 *
       
 35231 		 * @method recalc
       
 35232 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 35233 		 */
       
 35234 		recalc: function(container) {
       
 35235 			// A ton of variables, needs to be in the same scope for performance
       
 35236 			var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
       
 35237 			var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
       
 35238 			var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName;
       
 35239 			var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName;
       
 35240 			var alignDeltaSizeName, alignContentSizeName;
       
 35241 			var max = Math.max, min = Math.min;
       
 35242 
       
 35243 			// Get container items, properties and settings
       
 35244 			items = container.items().filter(':visible');
       
 35245 			contLayoutRect = container.layoutRect();
       
 35246 			contPaddingBox = container._paddingBox;
       
 35247 			contSettings = container.settings;
       
 35248 			direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
       
 35249 			align = contSettings.align;
       
 35250 			pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
       
 35251 			spacing = contSettings.spacing || 0;
       
 35252 
       
 35253 			if (direction == "row-reversed" || direction == "column-reverse") {
       
 35254 				items = items.set(items.toArray().reverse());
       
 35255 				direction = direction.split('-')[0];
       
 35256 			}
       
 35257 
       
 35258 			// Setup axis variable name for row/column direction since the calculations is the same
       
 35259 			if (direction == "column") {
       
 35260 				posName = "y";
       
 35261 				sizeName = "h";
       
 35262 				minSizeName = "minH";
       
 35263 				maxSizeName = "maxH";
       
 35264 				innerSizeName = "innerH";
       
 35265 				beforeName = 'top';
       
 35266 				deltaSizeName = "deltaH";
       
 35267 				contentSizeName = "contentH";
       
 35268 
       
 35269 				alignBeforeName = "left";
       
 35270 				alignSizeName = "w";
       
 35271 				alignAxisName = "x";
       
 35272 				alignInnerSizeName = "innerW";
       
 35273 				alignMinSizeName = "minW";
       
 35274 				alignAfterName = "right";
       
 35275 				alignDeltaSizeName = "deltaW";
       
 35276 				alignContentSizeName = "contentW";
       
 35277 			} else {
       
 35278 				posName = "x";
       
 35279 				sizeName = "w";
       
 35280 				minSizeName = "minW";
       
 35281 				maxSizeName = "maxW";
       
 35282 				innerSizeName = "innerW";
       
 35283 				beforeName = 'left';
       
 35284 				deltaSizeName = "deltaW";
       
 35285 				contentSizeName = "contentW";
       
 35286 
       
 35287 				alignBeforeName = "top";
       
 35288 				alignSizeName = "h";
       
 35289 				alignAxisName = "y";
       
 35290 				alignInnerSizeName = "innerH";
       
 35291 				alignMinSizeName = "minH";
       
 35292 				alignAfterName = "bottom";
       
 35293 				alignDeltaSizeName = "deltaH";
       
 35294 				alignContentSizeName = "contentH";
       
 35295 			}
       
 35296 
       
 35297 			// Figure out total flex, availableSpace and collect any max size elements
       
 35298 			availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
       
 35299 			maxAlignEndPos = totalFlex = 0;
       
 35300 			for (i = 0, l = items.length; i < l; i++) {
       
 35301 				ctrl = items[i];
       
 35302 				ctrlLayoutRect = ctrl.layoutRect();
       
 35303 				ctrlSettings = ctrl.settings;
       
 35304 				flex = ctrlSettings.flex;
       
 35305 				availableSpace -= (i < l - 1 ? spacing : 0);
       
 35306 
       
 35307 				if (flex > 0) {
       
 35308 					totalFlex += flex;
       
 35309 
       
 35310 					// Flexed item has a max size then we need to check if we will hit that size
       
 35311 					if (ctrlLayoutRect[maxSizeName]) {
       
 35312 						maxSizeItems.push(ctrl);
       
 35313 					}
       
 35314 
       
 35315 					ctrlLayoutRect.flex = flex;
       
 35316 				}
       
 35317 
       
 35318 				availableSpace -= ctrlLayoutRect[minSizeName];
       
 35319 
       
 35320 				// Calculate the align end position to be used to check for overflow/underflow
       
 35321 				size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
       
 35322 				if (size > maxAlignEndPos) {
       
 35323 					maxAlignEndPos = size;
       
 35324 				}
       
 35325 			}
       
 35326 
       
 35327 			// Calculate minW/minH
       
 35328 			rect = {};
       
 35329 			if (availableSpace < 0) {
       
 35330 				rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
       
 35331 			} else {
       
 35332 				rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
       
 35333 			}
       
 35334 
       
 35335 			rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
       
 35336 
       
 35337 			rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
       
 35338 			rect[alignContentSizeName] = maxAlignEndPos;
       
 35339 			rect.minW = min(rect.minW, contLayoutRect.maxW);
       
 35340 			rect.minH = min(rect.minH, contLayoutRect.maxH);
       
 35341 			rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
       
 35342 			rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
       
 35343 
       
 35344 			// Resize container container if minSize was changed
       
 35345 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
       
 35346 				rect.w = rect.minW;
       
 35347 				rect.h = rect.minH;
       
 35348 
       
 35349 				container.layoutRect(rect);
       
 35350 				this.recalc(container);
       
 35351 
       
 35352 				// Forced recalc for example if items are hidden/shown
       
 35353 				if (container._lastRect === null) {
       
 35354 					var parentCtrl = container.parent();
       
 35355 					if (parentCtrl) {
       
 35356 						parentCtrl._lastRect = null;
       
 35357 						parentCtrl.recalc();
       
 35358 					}
       
 35359 				}
       
 35360 
       
 35361 				return;
       
 35362 			}
       
 35363 
       
 35364 			// Handle max size elements, check if they will become to wide with current options
       
 35365 			ratio = availableSpace / totalFlex;
       
 35366 			for (i = 0, l = maxSizeItems.length; i < l; i++) {
       
 35367 				ctrl = maxSizeItems[i];
       
 35368 				ctrlLayoutRect = ctrl.layoutRect();
       
 35369 				maxSize = ctrlLayoutRect[maxSizeName];
       
 35370 				size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
       
 35371 
       
 35372 				if (size > maxSize) {
       
 35373 					availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
       
 35374 					totalFlex -= ctrlLayoutRect.flex;
       
 35375 					ctrlLayoutRect.flex = 0;
       
 35376 					ctrlLayoutRect.maxFlexSize = maxSize;
       
 35377 				} else {
       
 35378 					ctrlLayoutRect.maxFlexSize = 0;
       
 35379 				}
       
 35380 			}
       
 35381 
       
 35382 			// Setup new ratio, target layout rect, start position
       
 35383 			ratio = availableSpace / totalFlex;
       
 35384 			pos = contPaddingBox[beforeName];
       
 35385 			rect = {};
       
 35386 
       
 35387 			// Handle pack setting moves the start position to end, center
       
 35388 			if (totalFlex === 0) {
       
 35389 				if (pack == "end") {
       
 35390 					pos = availableSpace + contPaddingBox[beforeName];
       
 35391 				} else if (pack == "center") {
       
 35392 					pos = Math.round(
       
 35393 						(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
       
 35394 					) + contPaddingBox[beforeName];
       
 35395 
       
 35396 					if (pos < 0) {
       
 35397 						pos = contPaddingBox[beforeName];
       
 35398 					}
       
 35399 				} else if (pack == "justify") {
       
 35400 					pos = contPaddingBox[beforeName];
       
 35401 					spacing = Math.floor(availableSpace / (items.length - 1));
       
 35402 				}
       
 35403 			}
       
 35404 
       
 35405 			// Default aligning (start) the other ones needs to be calculated while doing the layout
       
 35406 			rect[alignAxisName] = contPaddingBox[alignBeforeName];
       
 35407 
       
 35408 			// Start laying out controls
       
 35409 			for (i = 0, l = items.length; i < l; i++) {
       
 35410 				ctrl = items[i];
       
 35411 				ctrlLayoutRect = ctrl.layoutRect();
       
 35412 				size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
       
 35413 
       
 35414 				// Align the control on the other axis
       
 35415 				if (align === "center") {
       
 35416 					rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
       
 35417 				} else if (align === "stretch") {
       
 35418 					rect[alignSizeName] = max(
       
 35419 						ctrlLayoutRect[alignMinSizeName] || 0,
       
 35420 						contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
       
 35421 					);
       
 35422 					rect[alignAxisName] = contPaddingBox[alignBeforeName];
       
 35423 				} else if (align === "end") {
       
 35424 					rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top;
       
 35425 				}
       
 35426 
       
 35427 				// Calculate new size based on flex
       
 35428 				if (ctrlLayoutRect.flex > 0) {
       
 35429 					size += ctrlLayoutRect.flex * ratio;
       
 35430 				}
       
 35431 
       
 35432 				rect[sizeName] = size;
       
 35433 				rect[posName] = pos;
       
 35434 				ctrl.layoutRect(rect);
       
 35435 
       
 35436 				// Recalculate containers
       
 35437 				if (ctrl.recalc) {
       
 35438 					ctrl.recalc();
       
 35439 				}
       
 35440 
       
 35441 				// Move x/y position
       
 35442 				pos += size + spacing;
       
 35443 			}
       
 35444 		}
       
 35445 	});
       
 35446 });
       
 35447 
       
 35448 // Included from: js/tinymce/classes/ui/FlowLayout.js
       
 35449 
       
 35450 /**
       
 35451  * FlowLayout.js
       
 35452  *
       
 35453  * Copyright, Moxiecode Systems AB
       
 35454  * Released under LGPL License.
       
 35455  *
       
 35456  * License: http://www.tinymce.com/license
       
 35457  * Contributing: http://www.tinymce.com/contributing
       
 35458  */
       
 35459 
       
 35460 /**
       
 35461  * This layout manager will place the controls by using the browsers native layout.
       
 35462  *
       
 35463  * @-x-less FlowLayout.less
       
 35464  * @class tinymce.ui.FlowLayout
       
 35465  * @extends tinymce.ui.Layout
       
 35466  */
       
 35467 define("tinymce/ui/FlowLayout", [
       
 35468 	"tinymce/ui/Layout"
       
 35469 ], function(Layout) {
       
 35470 	return Layout.extend({
       
 35471 		Defaults: {
       
 35472 			containerClass: 'flow-layout',
       
 35473 			controlClass: 'flow-layout-item',
       
 35474 			endClass: 'break'
       
 35475 		},
       
 35476 
       
 35477 		/**
       
 35478 		 * Recalculates the positions of the controls in the specified container.
       
 35479 		 *
       
 35480 		 * @method recalc
       
 35481 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 35482 		 */
       
 35483 		recalc: function(container) {
       
 35484 			container.items().filter(':visible').each(function(ctrl) {
       
 35485 				if (ctrl.recalc) {
       
 35486 					ctrl.recalc();
       
 35487 				}
       
 35488 			});
       
 35489 		}
       
 35490 	});
       
 35491 });
       
 35492 
       
 35493 // Included from: js/tinymce/classes/ui/FormatControls.js
       
 35494 
       
 35495 /**
       
 35496  * FormatControls.js
       
 35497  *
       
 35498  * Copyright, Moxiecode Systems AB
       
 35499  * Released under LGPL License.
       
 35500  *
       
 35501  * License: http://www.tinymce.com/license
       
 35502  * Contributing: http://www.tinymce.com/contributing
       
 35503  */
       
 35504 
       
 35505 /**
       
 35506  * Internal class containing all TinyMCE specific control types such as
       
 35507  * format listboxes, fontlist boxes, toolbar buttons etc.
       
 35508  *
       
 35509  * @class tinymce.ui.FormatControls
       
 35510  */
       
 35511 define("tinymce/ui/FormatControls", [
       
 35512 	"tinymce/ui/Control",
       
 35513 	"tinymce/ui/Widget",
       
 35514 	"tinymce/ui/FloatPanel",
       
 35515 	"tinymce/util/Tools",
       
 35516 	"tinymce/EditorManager",
       
 35517 	"tinymce/Env"
       
 35518 ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
       
 35519 	var each = Tools.each;
       
 35520 
       
 35521 	EditorManager.on('AddEditor', function(e) {
       
 35522 		if (e.editor.rtl) {
       
 35523 			Control.rtl = true;
       
 35524 		}
       
 35525 
       
 35526 		registerControls(e.editor);
       
 35527 	});
       
 35528 
       
 35529 	Control.translate = function(text) {
       
 35530 		return EditorManager.translate(text);
       
 35531 	};
       
 35532 
       
 35533 	Widget.tooltips = !Env.iOS;
       
 35534 
       
 35535 	function registerControls(editor) {
       
 35536 		var formatMenu;
       
 35537 
       
 35538 		function createListBoxChangeHandler(items, formatName) {
       
 35539 			return function() {
       
 35540 				var self = this;
       
 35541 
       
 35542 				editor.on('nodeChange', function(e) {
       
 35543 					var formatter = editor.formatter;
       
 35544 					var value = null;
       
 35545 
       
 35546 					each(e.parents, function(node) {
       
 35547 						each(items, function(item) {
       
 35548 							if (formatName) {
       
 35549 								if (formatter.matchNode(node, formatName, {value: item.value})) {
       
 35550 									value = item.value;
       
 35551 								}
       
 35552 							} else {
       
 35553 								if (formatter.matchNode(node, item.value)) {
       
 35554 									value = item.value;
       
 35555 								}
       
 35556 							}
       
 35557 
       
 35558 							if (value) {
       
 35559 								return false;
       
 35560 							}
       
 35561 						});
       
 35562 
       
 35563 						if (value) {
       
 35564 							return false;
       
 35565 						}
       
 35566 					});
       
 35567 
       
 35568 					self.value(value);
       
 35569 				});
       
 35570 			};
       
 35571 		}
       
 35572 
       
 35573 		function createFormats(formats) {
       
 35574 			formats = formats.replace(/;$/, '').split(';');
       
 35575 
       
 35576 			var i = formats.length;
       
 35577 			while (i--) {
       
 35578 				formats[i] = formats[i].split('=');
       
 35579 			}
       
 35580 
       
 35581 			return formats;
       
 35582 		}
       
 35583 
       
 35584 		function createFormatMenu() {
       
 35585 			var count = 0, newFormats = [];
       
 35586 
       
 35587 			var defaultStyleFormats = [
       
 35588 				{title: 'Headings', items: [
       
 35589 					{title: 'Heading 1', format: 'h1'},
       
 35590 					{title: 'Heading 2', format: 'h2'},
       
 35591 					{title: 'Heading 3', format: 'h3'},
       
 35592 					{title: 'Heading 4', format: 'h4'},
       
 35593 					{title: 'Heading 5', format: 'h5'},
       
 35594 					{title: 'Heading 6', format: 'h6'}
       
 35595 				]},
       
 35596 
       
 35597 				{title: 'Inline', items: [
       
 35598 					{title: 'Bold', icon: 'bold', format: 'bold'},
       
 35599 					{title: 'Italic', icon: 'italic', format: 'italic'},
       
 35600 					{title: 'Underline', icon: 'underline', format: 'underline'},
       
 35601 					{title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
       
 35602 					{title: 'Superscript', icon: 'superscript', format: 'superscript'},
       
 35603 					{title: 'Subscript', icon: 'subscript', format: 'subscript'},
       
 35604 					{title: 'Code', icon: 'code', format: 'code'}
       
 35605 				]},
       
 35606 
       
 35607 				{title: 'Blocks', items: [
       
 35608 					{title: 'Paragraph', format: 'p'},
       
 35609 					{title: 'Blockquote', format: 'blockquote'},
       
 35610 					{title: 'Div', format: 'div'},
       
 35611 					{title: 'Pre', format: 'pre'}
       
 35612 				]},
       
 35613 
       
 35614 				{title: 'Alignment', items: [
       
 35615 					{title: 'Left', icon: 'alignleft', format: 'alignleft'},
       
 35616 					{title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
       
 35617 					{title: 'Right', icon: 'alignright', format: 'alignright'},
       
 35618 					{title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
       
 35619 				]}
       
 35620 			];
       
 35621 
       
 35622 			function createMenu(formats) {
       
 35623 				var menu = [];
       
 35624 
       
 35625 				if (!formats) {
       
 35626 					return;
       
 35627 				}
       
 35628 
       
 35629 				each(formats, function(format) {
       
 35630 					var menuItem = {
       
 35631 						text: format.title,
       
 35632 						icon: format.icon
       
 35633 					};
       
 35634 
       
 35635 					if (format.items) {
       
 35636 						menuItem.menu = createMenu(format.items);
       
 35637 					} else {
       
 35638 						var formatName = format.format || "custom" + count++;
       
 35639 
       
 35640 						if (!format.format) {
       
 35641 							format.name = formatName;
       
 35642 							newFormats.push(format);
       
 35643 						}
       
 35644 
       
 35645 						menuItem.format = formatName;
       
 35646 						menuItem.cmd = format.cmd;
       
 35647 					}
       
 35648 
       
 35649 					menu.push(menuItem);
       
 35650 				});
       
 35651 
       
 35652 				return menu;
       
 35653 			}
       
 35654 
       
 35655 			function createStylesMenu() {
       
 35656 				var menu;
       
 35657 
       
 35658 				if (editor.settings.style_formats_merge) {
       
 35659 					if (editor.settings.style_formats) {
       
 35660 						menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats));
       
 35661 					} else {
       
 35662 						menu = createMenu(defaultStyleFormats);
       
 35663 					}
       
 35664 				} else {
       
 35665 					menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
       
 35666 				}
       
 35667 
       
 35668 				return menu;
       
 35669 			}
       
 35670 
       
 35671 			editor.on('init', function() {
       
 35672 				each(newFormats, function(format) {
       
 35673 					editor.formatter.register(format.name, format);
       
 35674 				});
       
 35675 			});
       
 35676 
       
 35677 			return {
       
 35678 				type: 'menu',
       
 35679 				items: createStylesMenu(),
       
 35680 				onPostRender: function(e) {
       
 35681 					editor.fire('renderFormatsMenu', {control: e.control});
       
 35682 				},
       
 35683 				itemDefaults: {
       
 35684 					preview: true,
       
 35685 
       
 35686 					textStyle: function() {
       
 35687 						if (this.settings.format) {
       
 35688 							return editor.formatter.getCssText(this.settings.format);
       
 35689 						}
       
 35690 					},
       
 35691 
       
 35692 					onPostRender: function() {
       
 35693 						var self = this;
       
 35694 
       
 35695 						self.parent().on('show', function() {
       
 35696 							var formatName, command;
       
 35697 
       
 35698 							formatName = self.settings.format;
       
 35699 							if (formatName) {
       
 35700 								self.disabled(!editor.formatter.canApply(formatName));
       
 35701 								self.active(editor.formatter.match(formatName));
       
 35702 							}
       
 35703 
       
 35704 							command = self.settings.cmd;
       
 35705 							if (command) {
       
 35706 								self.active(editor.queryCommandState(command));
       
 35707 							}
       
 35708 						});
       
 35709 					},
       
 35710 
       
 35711 					onclick: function() {
       
 35712 						if (this.settings.format) {
       
 35713 							toggleFormat(this.settings.format);
       
 35714 						}
       
 35715 
       
 35716 						if (this.settings.cmd) {
       
 35717 							editor.execCommand(this.settings.cmd);
       
 35718 						}
       
 35719 					}
       
 35720 				}
       
 35721 			};
       
 35722 		}
       
 35723 
       
 35724 		formatMenu = createFormatMenu();
       
 35725 
       
 35726 		// Simple format controls <control/format>:<UI text>
       
 35727 		each({
       
 35728 			bold: 'Bold',
       
 35729 			italic: 'Italic',
       
 35730 			underline: 'Underline',
       
 35731 			strikethrough: 'Strikethrough',
       
 35732 			subscript: 'Subscript',
       
 35733 			superscript: 'Superscript'
       
 35734 		}, function(text, name) {
       
 35735 			editor.addButton(name, {
       
 35736 				tooltip: text,
       
 35737 				onPostRender: function() {
       
 35738 					var self = this;
       
 35739 
       
 35740 					// TODO: Fix this
       
 35741 					if (editor.formatter) {
       
 35742 						editor.formatter.formatChanged(name, function(state) {
       
 35743 							self.active(state);
       
 35744 						});
       
 35745 					} else {
       
 35746 						editor.on('init', function() {
       
 35747 							editor.formatter.formatChanged(name, function(state) {
       
 35748 								self.active(state);
       
 35749 							});
       
 35750 						});
       
 35751 					}
       
 35752 				},
       
 35753 				onclick: function() {
       
 35754 					toggleFormat(name);
       
 35755 				}
       
 35756 			});
       
 35757 		});
       
 35758 
       
 35759 		// Simple command controls <control>:[<UI text>,<Command>]
       
 35760 		each({
       
 35761 			outdent: ['Decrease indent', 'Outdent'],
       
 35762 			indent: ['Increase indent', 'Indent'],
       
 35763 			cut: ['Cut', 'Cut'],
       
 35764 			copy: ['Copy', 'Copy'],
       
 35765 			paste: ['Paste', 'Paste'],
       
 35766 			help: ['Help', 'mceHelp'],
       
 35767 			selectall: ['Select all', 'SelectAll'],
       
 35768 			removeformat: ['Clear formatting', 'RemoveFormat'],
       
 35769 			visualaid: ['Visual aids', 'mceToggleVisualAid'],
       
 35770 			newdocument: ['New document', 'mceNewDocument']
       
 35771 		}, function(item, name) {
       
 35772 			editor.addButton(name, {
       
 35773 				tooltip: item[0],
       
 35774 				cmd: item[1]
       
 35775 			});
       
 35776 		});
       
 35777 
       
 35778 		// Simple command controls with format state
       
 35779 		each({
       
 35780 			blockquote: ['Blockquote', 'mceBlockQuote'],
       
 35781 			numlist: ['Numbered list', 'InsertOrderedList'],
       
 35782 			bullist: ['Bullet list', 'InsertUnorderedList'],
       
 35783 			subscript: ['Subscript', 'Subscript'],
       
 35784 			superscript: ['Superscript', 'Superscript'],
       
 35785 			alignleft: ['Align left', 'JustifyLeft'],
       
 35786 			aligncenter: ['Align center', 'JustifyCenter'],
       
 35787 			alignright: ['Align right', 'JustifyRight'],
       
 35788 			alignjustify: ['Justify', 'JustifyFull']
       
 35789 		}, function(item, name) {
       
 35790 			editor.addButton(name, {
       
 35791 				tooltip: item[0],
       
 35792 				cmd: item[1],
       
 35793 				onPostRender: function() {
       
 35794 					var self = this;
       
 35795 
       
 35796 					// TODO: Fix this
       
 35797 					if (editor.formatter) {
       
 35798 						editor.formatter.formatChanged(name, function(state) {
       
 35799 							self.active(state);
       
 35800 						});
       
 35801 					} else {
       
 35802 						editor.on('init', function() {
       
 35803 							editor.formatter.formatChanged(name, function(state) {
       
 35804 								self.active(state);
       
 35805 							});
       
 35806 						});
       
 35807 					}
       
 35808 				}
       
 35809 			});
       
 35810 		});
       
 35811 
       
 35812 		function toggleUndoRedoState(type) {
       
 35813 			return function() {
       
 35814 				var self = this;
       
 35815 
       
 35816 				type = type == 'redo' ? 'hasRedo' : 'hasUndo';
       
 35817 
       
 35818 				function checkState() {
       
 35819 					return editor.undoManager ? editor.undoManager[type]() : false;
       
 35820 				}
       
 35821 
       
 35822 				self.disabled(!checkState());
       
 35823 				editor.on('Undo Redo AddUndo TypingUndo ClearUndos', function() {
       
 35824 					self.disabled(!checkState());
       
 35825 				});
       
 35826 			};
       
 35827 		}
       
 35828 
       
 35829 		function toggleVisualAidState() {
       
 35830 			var self = this;
       
 35831 
       
 35832 			editor.on('VisualAid', function(e) {
       
 35833 				self.active(e.hasVisual);
       
 35834 			});
       
 35835 
       
 35836 			self.active(editor.hasVisual);
       
 35837 		}
       
 35838 
       
 35839 		editor.addButton('undo', {
       
 35840 			tooltip: 'Undo',
       
 35841 			onPostRender: toggleUndoRedoState('undo'),
       
 35842 			cmd: 'undo'
       
 35843 		});
       
 35844 
       
 35845 		editor.addButton('redo', {
       
 35846 			tooltip: 'Redo',
       
 35847 			onPostRender: toggleUndoRedoState('redo'),
       
 35848 			cmd: 'redo'
       
 35849 		});
       
 35850 
       
 35851 		editor.addMenuItem('newdocument', {
       
 35852 			text: 'New document',
       
 35853 			icon: 'newdocument',
       
 35854 			cmd: 'mceNewDocument'
       
 35855 		});
       
 35856 
       
 35857 		editor.addMenuItem('undo', {
       
 35858 			text: 'Undo',
       
 35859 			icon: 'undo',
       
 35860 			shortcut: 'Meta+Z',
       
 35861 			onPostRender: toggleUndoRedoState('undo'),
       
 35862 			cmd: 'undo'
       
 35863 		});
       
 35864 
       
 35865 		editor.addMenuItem('redo', {
       
 35866 			text: 'Redo',
       
 35867 			icon: 'redo',
       
 35868 			shortcut: 'Meta+Y',
       
 35869 			onPostRender: toggleUndoRedoState('redo'),
       
 35870 			cmd: 'redo'
       
 35871 		});
       
 35872 
       
 35873 		editor.addMenuItem('visualaid', {
       
 35874 			text: 'Visual aids',
       
 35875 			selectable: true,
       
 35876 			onPostRender: toggleVisualAidState,
       
 35877 			cmd: 'mceToggleVisualAid'
       
 35878 		});
       
 35879 
       
 35880 		each({
       
 35881 			cut: ['Cut', 'Cut', 'Meta+X'],
       
 35882 			copy: ['Copy', 'Copy', 'Meta+C'],
       
 35883 			paste: ['Paste', 'Paste', 'Meta+V'],
       
 35884 			selectall: ['Select all', 'SelectAll', 'Meta+A'],
       
 35885 			bold: ['Bold', 'Bold', 'Meta+B'],
       
 35886 			italic: ['Italic', 'Italic', 'Meta+I'],
       
 35887 			underline: ['Underline', 'Underline'],
       
 35888 			strikethrough: ['Strikethrough', 'Strikethrough'],
       
 35889 			subscript: ['Subscript', 'Subscript'],
       
 35890 			superscript: ['Superscript', 'Superscript'],
       
 35891 			removeformat: ['Clear formatting', 'RemoveFormat']
       
 35892 		}, function(item, name) {
       
 35893 			editor.addMenuItem(name, {
       
 35894 				text: item[0],
       
 35895 				icon: name,
       
 35896 				shortcut: item[2],
       
 35897 				cmd: item[1]
       
 35898 			});
       
 35899 		});
       
 35900 
       
 35901 		editor.on('mousedown', function() {
       
 35902 			FloatPanel.hideAll();
       
 35903 		});
       
 35904 
       
 35905 		function toggleFormat(fmt) {
       
 35906 			if (fmt.control) {
       
 35907 				fmt = fmt.control.value();
       
 35908 			}
       
 35909 
       
 35910 			if (fmt) {
       
 35911 				editor.execCommand('mceToggleFormat', false, fmt);
       
 35912 			}
       
 35913 		}
       
 35914 
       
 35915 		editor.addButton('styleselect', {
       
 35916 			type: 'menubutton',
       
 35917 			text: 'Formats',
       
 35918 			menu: formatMenu
       
 35919 		});
       
 35920 
       
 35921 		editor.addButton('formatselect', function() {
       
 35922 			var items = [], blocks = createFormats(editor.settings.block_formats ||
       
 35923 				'Paragraph=p;' +
       
 35924 				'Heading 1=h1;' +
       
 35925 				'Heading 2=h2;' +
       
 35926 				'Heading 3=h3;' +
       
 35927 				'Heading 4=h4;' +
       
 35928 				'Heading 5=h5;' +
       
 35929 				'Heading 6=h6;' +
       
 35930 				'Preformatted=pre'
       
 35931 			);
       
 35932 
       
 35933 			each(blocks, function(block) {
       
 35934 				items.push({
       
 35935 					text: block[0],
       
 35936 					value: block[1],
       
 35937 					textStyle: function() {
       
 35938 						return editor.formatter.getCssText(block[1]);
       
 35939 					}
       
 35940 				});
       
 35941 			});
       
 35942 
       
 35943 			return {
       
 35944 				type: 'listbox',
       
 35945 				text: blocks[0][0],
       
 35946 				values: items,
       
 35947 				fixedWidth: true,
       
 35948 				onselect: toggleFormat,
       
 35949 				onPostRender: createListBoxChangeHandler(items)
       
 35950 			};
       
 35951 		});
       
 35952 
       
 35953 		editor.addButton('fontselect', function() {
       
 35954 			var defaultFontsFormats =
       
 35955 				'Andale Mono=andale mono,monospace;' +
       
 35956 				'Arial=arial,helvetica,sans-serif;' +
       
 35957 				'Arial Black=arial black,sans-serif;' +
       
 35958 				'Book Antiqua=book antiqua,palatino,serif;' +
       
 35959 				'Comic Sans MS=comic sans ms,sans-serif;' +
       
 35960 				'Courier New=courier new,courier,monospace;' +
       
 35961 				'Georgia=georgia,palatino,serif;' +
       
 35962 				'Helvetica=helvetica,arial,sans-serif;' +
       
 35963 				'Impact=impact,sans-serif;' +
       
 35964 				'Symbol=symbol;' +
       
 35965 				'Tahoma=tahoma,arial,helvetica,sans-serif;' +
       
 35966 				'Terminal=terminal,monaco,monospace;' +
       
 35967 				'Times New Roman=times new roman,times,serif;' +
       
 35968 				'Trebuchet MS=trebuchet ms,geneva,sans-serif;' +
       
 35969 				'Verdana=verdana,geneva,sans-serif;' +
       
 35970 				'Webdings=webdings;' +
       
 35971 				'Wingdings=wingdings,zapf dingbats';
       
 35972 
       
 35973 			var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
       
 35974 
       
 35975 			each(fonts, function(font) {
       
 35976 				items.push({
       
 35977 					text: {raw: font[0]},
       
 35978 					value: font[1],
       
 35979 					textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
       
 35980 				});
       
 35981 			});
       
 35982 
       
 35983 			return {
       
 35984 				type: 'listbox',
       
 35985 				text: 'Font Family',
       
 35986 				tooltip: 'Font Family',
       
 35987 				values: items,
       
 35988 				fixedWidth: true,
       
 35989 				onPostRender: createListBoxChangeHandler(items, 'fontname'),
       
 35990 				onselect: function(e) {
       
 35991 					if (e.control.settings.value) {
       
 35992 						editor.execCommand('FontName', false, e.control.settings.value);
       
 35993 					}
       
 35994 				}
       
 35995 			};
       
 35996 		});
       
 35997 
       
 35998 		editor.addButton('fontsizeselect', function() {
       
 35999 			var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
       
 36000 			var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
       
 36001 
       
 36002 			each(fontsize_formats.split(' '), function(item) {
       
 36003 				var text = item, value = item;
       
 36004 				// Allow text=value font sizes.
       
 36005 				var values = item.split('=');
       
 36006 				if (values.length > 1) {
       
 36007 					text = values[0];
       
 36008 					value = values[1];
       
 36009 				}
       
 36010 				items.push({text: text, value: value});
       
 36011 			});
       
 36012 
       
 36013 			return {
       
 36014 				type: 'listbox',
       
 36015 				text: 'Font Sizes',
       
 36016 				tooltip: 'Font Sizes',
       
 36017 				values: items,
       
 36018 				fixedWidth: true,
       
 36019 				onPostRender: createListBoxChangeHandler(items, 'fontsize'),
       
 36020 				onclick: function(e) {
       
 36021 					if (e.control.settings.value) {
       
 36022 						editor.execCommand('FontSize', false, e.control.settings.value);
       
 36023 					}
       
 36024 				}
       
 36025 			};
       
 36026 		});
       
 36027 
       
 36028 		editor.addMenuItem('formats', {
       
 36029 			text: 'Formats',
       
 36030 			menu: formatMenu
       
 36031 		});
       
 36032 	}
       
 36033 });
       
 36034 
       
 36035 // Included from: js/tinymce/classes/ui/GridLayout.js
       
 36036 
       
 36037 /**
       
 36038  * GridLayout.js
       
 36039  *
       
 36040  * Copyright, Moxiecode Systems AB
       
 36041  * Released under LGPL License.
       
 36042  *
       
 36043  * License: http://www.tinymce.com/license
       
 36044  * Contributing: http://www.tinymce.com/contributing
       
 36045  */
       
 36046 
       
 36047 /**
       
 36048  * This layout manager places controls in a grid.
       
 36049  *
       
 36050  * @setting {Number} spacing Spacing between controls.
       
 36051  * @setting {Number} spacingH Horizontal spacing between controls.
       
 36052  * @setting {Number} spacingV Vertical spacing between controls.
       
 36053  * @setting {Number} columns Number of columns to use.
       
 36054  * @setting {String/Array} alignH start|end|center|stretch or array of values for each column.
       
 36055  * @setting {String/Array} alignV start|end|center|stretch or array of values for each column.
       
 36056  * @setting {String} pack start|end
       
 36057  *
       
 36058  * @class tinymce.ui.GridLayout
       
 36059  * @extends tinymce.ui.AbsoluteLayout
       
 36060  */
       
 36061 define("tinymce/ui/GridLayout", [
       
 36062 	"tinymce/ui/AbsoluteLayout"
       
 36063 ], function(AbsoluteLayout) {
       
 36064 	"use strict";
       
 36065 
       
 36066 	return AbsoluteLayout.extend({
       
 36067 		/**
       
 36068 		 * Recalculates the positions of the controls in the specified container.
       
 36069 		 *
       
 36070 		 * @method recalc
       
 36071 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 36072 		 */
       
 36073 		recalc: function(container) {
       
 36074 			var settings = container.settings, rows, cols, items, contLayoutRect, width, height, rect,
       
 36075 				ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY,
       
 36076 				colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight, reverseRows, idx;
       
 36077 
       
 36078 			// Get layout settings
       
 36079 			settings = container.settings;
       
 36080 			items = container.items().filter(':visible');
       
 36081 			contLayoutRect = container.layoutRect();
       
 36082 			cols = settings.columns || Math.ceil(Math.sqrt(items.length));
       
 36083 			rows = Math.ceil(items.length / cols);
       
 36084 			spacingH = settings.spacingH || settings.spacing || 0;
       
 36085 			spacingV = settings.spacingV || settings.spacing || 0;
       
 36086 			alignH = settings.alignH || settings.align;
       
 36087 			alignV = settings.alignV || settings.align;
       
 36088 			contPaddingBox = container._paddingBox;
       
 36089 			reverseRows = 'reverseRows' in settings ? settings.reverseRows : container.isRtl();
       
 36090 
       
 36091 			if (alignH && typeof alignH == "string") {
       
 36092 				alignH = [alignH];
       
 36093 			}
       
 36094 
       
 36095 			if (alignV && typeof alignV == "string") {
       
 36096 				alignV = [alignV];
       
 36097 			}
       
 36098 
       
 36099 			// Zero padd columnWidths
       
 36100 			for (x = 0; x < cols; x++) {
       
 36101 				colWidths.push(0);
       
 36102 			}
       
 36103 
       
 36104 			// Zero padd rowHeights
       
 36105 			for (y = 0; y < rows; y++) {
       
 36106 				rowHeights.push(0);
       
 36107 			}
       
 36108 
       
 36109 			// Calculate columnWidths and rowHeights
       
 36110 			for (y = 0; y < rows; y++) {
       
 36111 				for (x = 0; x < cols; x++) {
       
 36112 					ctrl = items[y * cols + x];
       
 36113 
       
 36114 					// Out of bounds
       
 36115 					if (!ctrl) {
       
 36116 						break;
       
 36117 					}
       
 36118 
       
 36119 					ctrlLayoutRect = ctrl.layoutRect();
       
 36120 					ctrlMinWidth = ctrlLayoutRect.minW;
       
 36121 					ctrlMinHeight = ctrlLayoutRect.minH;
       
 36122 
       
 36123 					colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x];
       
 36124 					rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y];
       
 36125 				}
       
 36126 			}
       
 36127 
       
 36128 			// Calculate maxX
       
 36129 			availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right;
       
 36130 			for (maxX = 0, x = 0; x < cols; x++) {
       
 36131 				maxX += colWidths[x] + (x > 0 ? spacingH : 0);
       
 36132 				availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x];
       
 36133 			}
       
 36134 
       
 36135 			// Calculate maxY
       
 36136 			availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom;
       
 36137 			for (maxY = 0, y = 0; y < rows; y++) {
       
 36138 				maxY += rowHeights[y] + (y > 0 ? spacingV : 0);
       
 36139 				availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y];
       
 36140 			}
       
 36141 
       
 36142 			maxX += contPaddingBox.left + contPaddingBox.right;
       
 36143 			maxY += contPaddingBox.top + contPaddingBox.bottom;
       
 36144 
       
 36145 			// Calculate minW/minH
       
 36146 			rect = {};
       
 36147 			rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW);
       
 36148 			rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH);
       
 36149 
       
 36150 			rect.contentW = rect.minW - contLayoutRect.deltaW;
       
 36151 			rect.contentH = rect.minH - contLayoutRect.deltaH;
       
 36152 			rect.minW = Math.min(rect.minW, contLayoutRect.maxW);
       
 36153 			rect.minH = Math.min(rect.minH, contLayoutRect.maxH);
       
 36154 			rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth);
       
 36155 			rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight);
       
 36156 
       
 36157 			// Resize container container if minSize was changed
       
 36158 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
       
 36159 				rect.w = rect.minW;
       
 36160 				rect.h = rect.minH;
       
 36161 
       
 36162 				container.layoutRect(rect);
       
 36163 				this.recalc(container);
       
 36164 
       
 36165 				// Forced recalc for example if items are hidden/shown
       
 36166 				if (container._lastRect === null) {
       
 36167 					var parentCtrl = container.parent();
       
 36168 					if (parentCtrl) {
       
 36169 						parentCtrl._lastRect = null;
       
 36170 						parentCtrl.recalc();
       
 36171 					}
       
 36172 				}
       
 36173 
       
 36174 				return;
       
 36175 			}
       
 36176 
       
 36177 			// Update contentW/contentH so absEnd moves correctly
       
 36178 			if (contLayoutRect.autoResize) {
       
 36179 				rect = container.layoutRect(rect);
       
 36180 				rect.contentW = rect.minW - contLayoutRect.deltaW;
       
 36181 				rect.contentH = rect.minH - contLayoutRect.deltaH;
       
 36182 			}
       
 36183 
       
 36184 			var flexV;
       
 36185 
       
 36186 			if (settings.packV == 'start') {
       
 36187 				flexV = 0;
       
 36188 			} else {
       
 36189 				flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0;
       
 36190 			}
       
 36191 
       
 36192 			// Calculate totalFlex
       
 36193 			var totalFlex = 0;
       
 36194 			var flexWidths = settings.flexWidths;
       
 36195 			if (flexWidths) {
       
 36196 				for (x = 0; x < flexWidths.length; x++) {
       
 36197 					totalFlex += flexWidths[x];
       
 36198 				}
       
 36199 			} else {
       
 36200 				totalFlex = cols;
       
 36201 			}
       
 36202 
       
 36203 			// Calculate new column widths based on flex values
       
 36204 			var ratio = availableWidth / totalFlex;
       
 36205 			for (x = 0; x < cols; x++) {
       
 36206 				colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
       
 36207 			}
       
 36208 
       
 36209 			// Move/resize controls
       
 36210 			posY = contPaddingBox.top;
       
 36211 			for (y = 0; y < rows; y++) {
       
 36212 				posX = contPaddingBox.left;
       
 36213 				height = rowHeights[y] + flexV;
       
 36214 
       
 36215 				for (x = 0; x < cols; x++) {
       
 36216 					if (reverseRows) {
       
 36217 						idx = y * cols + cols - 1 - x;
       
 36218 					} else {
       
 36219 						idx = y * cols + x;
       
 36220 					}
       
 36221 
       
 36222 					ctrl = items[idx];
       
 36223 
       
 36224 					// No more controls to render then break
       
 36225 					if (!ctrl) {
       
 36226 						break;
       
 36227 					}
       
 36228 
       
 36229 					// Get control settings and calculate x, y
       
 36230 					ctrlSettings = ctrl.settings;
       
 36231 					ctrlLayoutRect = ctrl.layoutRect();
       
 36232 					width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth);
       
 36233 					ctrlLayoutRect.x = posX;
       
 36234 					ctrlLayoutRect.y = posY;
       
 36235 
       
 36236 					// Align control horizontal
       
 36237 					align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null);
       
 36238 					if (align == "center") {
       
 36239 						ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2);
       
 36240 					} else if (align == "right") {
       
 36241 						ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w;
       
 36242 					} else if (align == "stretch") {
       
 36243 						ctrlLayoutRect.w = width;
       
 36244 					}
       
 36245 
       
 36246 					// Align control vertical
       
 36247 					align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null);
       
 36248 					if (align == "center") {
       
 36249 						ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2);
       
 36250 					} else if (align == "bottom") {
       
 36251 						ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h;
       
 36252 					} else if (align == "stretch") {
       
 36253 						ctrlLayoutRect.h = height;
       
 36254 					}
       
 36255 
       
 36256 					ctrl.layoutRect(ctrlLayoutRect);
       
 36257 
       
 36258 					posX += width + spacingH;
       
 36259 
       
 36260 					if (ctrl.recalc) {
       
 36261 						ctrl.recalc();
       
 36262 					}
       
 36263 				}
       
 36264 
       
 36265 				posY += height + spacingV;
       
 36266 			}
       
 36267 		}
       
 36268 	});
       
 36269 });
       
 36270 
       
 36271 // Included from: js/tinymce/classes/ui/Iframe.js
       
 36272 
       
 36273 /**
       
 36274  * Iframe.js
       
 36275  *
       
 36276  * Copyright, Moxiecode Systems AB
       
 36277  * Released under LGPL License.
       
 36278  *
       
 36279  * License: http://www.tinymce.com/license
       
 36280  * Contributing: http://www.tinymce.com/contributing
       
 36281  */
       
 36282 
       
 36283 /*jshint scripturl:true */
       
 36284 
       
 36285 /**
       
 36286  * This class creates an iframe.
       
 36287  *
       
 36288  * @setting {String} url Url to open in the iframe.
       
 36289  *
       
 36290  * @-x-less Iframe.less
       
 36291  * @class tinymce.ui.Iframe
       
 36292  * @extends tinymce.ui.Widget
       
 36293  */
       
 36294 define("tinymce/ui/Iframe", [
       
 36295 	"tinymce/ui/Widget"
       
 36296 ], function(Widget) {
       
 36297 	"use strict";
       
 36298 
       
 36299 	return Widget.extend({
       
 36300 		/**
       
 36301 		 * Renders the control as a HTML string.
       
 36302 		 *
       
 36303 		 * @method renderHtml
       
 36304 		 * @return {String} HTML representing the control.
       
 36305 		 */
       
 36306 		renderHtml: function() {
       
 36307 			var self = this;
       
 36308 
       
 36309 			self.addClass('iframe');
       
 36310 			self.canFocus = false;
       
 36311 
       
 36312 			/*eslint no-script-url:0 */
       
 36313 			return (
       
 36314 				'<iframe id="' + self._id + '" class="' + self.classes() + '" tabindex="-1" src="' +
       
 36315 				(self.settings.url || "javascript:\'\'") + '" frameborder="0"></iframe>'
       
 36316 			);
       
 36317 		},
       
 36318 
       
 36319 		/**
       
 36320 		 * Setter for the iframe source.
       
 36321 		 *
       
 36322 		 * @method src
       
 36323 		 * @param {String} src Source URL for iframe.
       
 36324 		 */
       
 36325 		src: function(src) {
       
 36326 			this.getEl().src = src;
       
 36327 		},
       
 36328 
       
 36329 		/**
       
 36330 		 * Inner HTML for the iframe.
       
 36331 		 *
       
 36332 		 * @method html
       
 36333 		 * @param {String} html HTML string to set as HTML inside the iframe.
       
 36334 		 * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
       
 36335 		 * @return {tinymce.ui.Iframe} Current iframe control.
       
 36336 		 */
       
 36337 		html: function(html, callback) {
       
 36338 			var self = this, body = this.getEl().contentWindow.document.body;
       
 36339 
       
 36340 			// Wait for iframe to initialize IE 10 takes time
       
 36341 			if (!body) {
       
 36342 				setTimeout(function() {
       
 36343 					self.html(html);
       
 36344 				}, 0);
       
 36345 			} else {
       
 36346 				body.innerHTML = html;
       
 36347 
       
 36348 				if (callback) {
       
 36349 					callback();
       
 36350 				}
       
 36351 			}
       
 36352 
       
 36353 			return this;
       
 36354 		}
       
 36355 	});
       
 36356 });
       
 36357 
       
 36358 // Included from: js/tinymce/classes/ui/Label.js
       
 36359 
       
 36360 /**
       
 36361  * Label.js
       
 36362  *
       
 36363  * Copyright, Moxiecode Systems AB
       
 36364  * Released under LGPL License.
       
 36365  *
       
 36366  * License: http://www.tinymce.com/license
       
 36367  * Contributing: http://www.tinymce.com/contributing
       
 36368  */
       
 36369 
       
 36370 /**
       
 36371  * This class creates a label element. A label is a simple text control
       
 36372  * that can be bound to other controls.
       
 36373  *
       
 36374  * @-x-less Label.less
       
 36375  * @class tinymce.ui.Label
       
 36376  * @extends tinymce.ui.Widget
       
 36377  */
       
 36378 define("tinymce/ui/Label", [
       
 36379 	"tinymce/ui/Widget",
       
 36380 	"tinymce/ui/DomUtils"
       
 36381 ], function(Widget, DomUtils) {
       
 36382 	"use strict";
       
 36383 
       
 36384 	return Widget.extend({
       
 36385 		/**
       
 36386 		 * Constructs a instance with the specified settings.
       
 36387 		 *
       
 36388 		 * @constructor
       
 36389 		 * @param {Object} settings Name/value object with settings.
       
 36390 		 * @param {Boolean} multiline Multiline label.
       
 36391 		 */
       
 36392 		init: function(settings) {
       
 36393 			var self = this;
       
 36394 
       
 36395 			self._super(settings);
       
 36396 			self.addClass('widget');
       
 36397 			self.addClass('label');
       
 36398 			self.canFocus = false;
       
 36399 
       
 36400 			if (settings.multiline) {
       
 36401 				self.addClass('autoscroll');
       
 36402 			}
       
 36403 
       
 36404 			if (settings.strong) {
       
 36405 				self.addClass('strong');
       
 36406 			}
       
 36407 		},
       
 36408 
       
 36409 		/**
       
 36410 		 * Initializes the current controls layout rect.
       
 36411 		 * This will be executed by the layout managers to determine the
       
 36412 		 * default minWidth/minHeight etc.
       
 36413 		 *
       
 36414 		 * @method initLayoutRect
       
 36415 		 * @return {Object} Layout rect instance.
       
 36416 		 */
       
 36417 		initLayoutRect: function() {
       
 36418 			var self = this, layoutRect = self._super();
       
 36419 
       
 36420 			if (self.settings.multiline) {
       
 36421 				var size = DomUtils.getSize(self.getEl());
       
 36422 
       
 36423 				// Check if the text fits within maxW if not then try word wrapping it
       
 36424 				if (size.width > layoutRect.maxW) {
       
 36425 					layoutRect.minW = layoutRect.maxW;
       
 36426 					self.addClass('multiline');
       
 36427 				}
       
 36428 
       
 36429 				self.getEl().style.width = layoutRect.minW + 'px';
       
 36430 				layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height);
       
 36431 			}
       
 36432 
       
 36433 			return layoutRect;
       
 36434 		},
       
 36435 
       
 36436 		/**
       
 36437 		 * Repaints the control after a layout operation.
       
 36438 		 *
       
 36439 		 * @method repaint
       
 36440 		 */
       
 36441 		repaint: function() {
       
 36442 			var self = this;
       
 36443 
       
 36444 			if (!self.settings.multiline) {
       
 36445 				self.getEl().style.lineHeight = self.layoutRect().h + 'px';
       
 36446 			}
       
 36447 
       
 36448 			return self._super();
       
 36449 		},
       
 36450 
       
 36451 		/**
       
 36452 		 * Sets/gets the current label text.
       
 36453 		 *
       
 36454 		 * @method text
       
 36455 		 * @param {String} [text] New label text.
       
 36456 		 * @return {String|tinymce.ui.Label} Current text or current label instance.
       
 36457 		 */
       
 36458 		text: function(text) {
       
 36459 			var self = this;
       
 36460 
       
 36461 			if (self._rendered && text) {
       
 36462 				this.innerHtml(self.encode(text));
       
 36463 			}
       
 36464 
       
 36465 			return self._super(text);
       
 36466 		},
       
 36467 
       
 36468 		/**
       
 36469 		 * Renders the control as a HTML string.
       
 36470 		 *
       
 36471 		 * @method renderHtml
       
 36472 		 * @return {String} HTML representing the control.
       
 36473 		 */
       
 36474 		renderHtml: function() {
       
 36475 			var self = this, forId = self.settings.forId;
       
 36476 
       
 36477 			return (
       
 36478 				'<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId + '"' : '') + '>' +
       
 36479 					self.encode(self._text) +
       
 36480 				'</label>'
       
 36481 			);
       
 36482 		}
       
 36483 	});
       
 36484 });
       
 36485 
       
 36486 // Included from: js/tinymce/classes/ui/Toolbar.js
       
 36487 
       
 36488 /**
       
 36489  * Toolbar.js
       
 36490  *
       
 36491  * Copyright, Moxiecode Systems AB
       
 36492  * Released under LGPL License.
       
 36493  *
       
 36494  * License: http://www.tinymce.com/license
       
 36495  * Contributing: http://www.tinymce.com/contributing
       
 36496  */
       
 36497 
       
 36498 /**
       
 36499  * Creates a new toolbar.
       
 36500  *
       
 36501  * @class tinymce.ui.Toolbar
       
 36502  * @extends tinymce.ui.Container
       
 36503  */
       
 36504 define("tinymce/ui/Toolbar", [
       
 36505 	"tinymce/ui/Container"
       
 36506 ], function(Container) {
       
 36507 	"use strict";
       
 36508 
       
 36509 	return Container.extend({
       
 36510 		Defaults: {
       
 36511 			role: 'toolbar',
       
 36512 			layout: 'flow'
       
 36513 		},
       
 36514 
       
 36515 		/**
       
 36516 		 * Constructs a instance with the specified settings.
       
 36517 		 *
       
 36518 		 * @constructor
       
 36519 		 * @param {Object} settings Name/value object with settings.
       
 36520 		 */
       
 36521 		init: function(settings) {
       
 36522 			var self = this;
       
 36523 
       
 36524 			self._super(settings);
       
 36525 			self.addClass('toolbar');
       
 36526 		},
       
 36527 
       
 36528 		/**
       
 36529 		 * Called after the control has been rendered.
       
 36530 		 *
       
 36531 		 * @method postRender
       
 36532 		 */
       
 36533 		postRender: function() {
       
 36534 			var self = this;
       
 36535 
       
 36536 			self.items().addClass('toolbar-item');
       
 36537 
       
 36538 			return self._super();
       
 36539 		}
       
 36540 	});
       
 36541 });
       
 36542 
       
 36543 // Included from: js/tinymce/classes/ui/MenuBar.js
       
 36544 
       
 36545 /**
       
 36546  * MenuBar.js
       
 36547  *
       
 36548  * Copyright, Moxiecode Systems AB
       
 36549  * Released under LGPL License.
       
 36550  *
       
 36551  * License: http://www.tinymce.com/license
       
 36552  * Contributing: http://www.tinymce.com/contributing
       
 36553  */
       
 36554 
       
 36555 /**
       
 36556  * Creates a new menubar.
       
 36557  *
       
 36558  * @-x-less MenuBar.less
       
 36559  * @class tinymce.ui.MenuBar
       
 36560  * @extends tinymce.ui.Toolbar
       
 36561  */
       
 36562 define("tinymce/ui/MenuBar", [
       
 36563 	"tinymce/ui/Toolbar"
       
 36564 ], function(Toolbar) {
       
 36565 	"use strict";
       
 36566 
       
 36567 	return Toolbar.extend({
       
 36568 		Defaults: {
       
 36569 			role: 'menubar',
       
 36570 			containerCls: 'menubar',
       
 36571 			ariaRoot: true,
       
 36572 			defaults: {
       
 36573 				type: 'menubutton'
       
 36574 			}
       
 36575 		}
       
 36576 	});
       
 36577 });
       
 36578 
       
 36579 // Included from: js/tinymce/classes/ui/MenuButton.js
       
 36580 
       
 36581 /**
       
 36582  * MenuButton.js
       
 36583  *
       
 36584  * Copyright, Moxiecode Systems AB
       
 36585  * Released under LGPL License.
       
 36586  *
       
 36587  * License: http://www.tinymce.com/license
       
 36588  * Contributing: http://www.tinymce.com/contributing
       
 36589  */
       
 36590 
       
 36591 /**
       
 36592  * Creates a new menu button.
       
 36593  *
       
 36594  * @-x-less MenuButton.less
       
 36595  * @class tinymce.ui.MenuButton
       
 36596  * @extends tinymce.ui.Button
       
 36597  */
       
 36598 define("tinymce/ui/MenuButton", [
       
 36599 	"tinymce/ui/Button",
       
 36600 	"tinymce/ui/Factory",
       
 36601 	"tinymce/ui/MenuBar"
       
 36602 ], function(Button, Factory, MenuBar) {
       
 36603 	"use strict";
       
 36604 
       
 36605 	// TODO: Maybe add as some global function
       
 36606 	function isChildOf(node, parent) {
       
 36607 		while (node) {
       
 36608 			if (parent === node) {
       
 36609 				return true;
       
 36610 			}
       
 36611 
       
 36612 			node = node.parentNode;
       
 36613 		}
       
 36614 
       
 36615 		return false;
       
 36616 	}
       
 36617 
       
 36618 	var MenuButton = Button.extend({
       
 36619 		/**
       
 36620 		 * Constructs a instance with the specified settings.
       
 36621 		 *
       
 36622 		 * @constructor
       
 36623 		 * @param {Object} settings Name/value object with settings.
       
 36624 		 */
       
 36625 		init: function(settings) {
       
 36626 			var self = this;
       
 36627 
       
 36628 			self._renderOpen = true;
       
 36629 			self._super(settings);
       
 36630 
       
 36631 			self.addClass('menubtn');
       
 36632 
       
 36633 			if (settings.fixedWidth) {
       
 36634 				self.addClass('fixed-width');
       
 36635 			}
       
 36636 
       
 36637 			self.aria('haspopup', true);
       
 36638 			self.hasPopup = true;
       
 36639 		},
       
 36640 
       
 36641 		/**
       
 36642 		 * Shows the menu for the button.
       
 36643 		 *
       
 36644 		 * @method showMenu
       
 36645 		 */
       
 36646 		showMenu: function() {
       
 36647 			var self = this, settings = self.settings, menu;
       
 36648 
       
 36649 			if (self.menu && self.menu.visible()) {
       
 36650 				return self.hideMenu();
       
 36651 			}
       
 36652 
       
 36653 			if (!self.menu) {
       
 36654 				menu = settings.menu || [];
       
 36655 
       
 36656 				// Is menu array then auto constuct menu control
       
 36657 				if (menu.length) {
       
 36658 					menu = {
       
 36659 						type: 'menu',
       
 36660 						items: menu
       
 36661 					};
       
 36662 				} else {
       
 36663 					menu.type = menu.type || 'menu';
       
 36664 				}
       
 36665 
       
 36666 				self.menu = Factory.create(menu).parent(self).renderTo();
       
 36667 				self.fire('createmenu');
       
 36668 				self.menu.reflow();
       
 36669 				self.menu.on('cancel', function(e) {
       
 36670 					if (e.control.parent() === self.menu) {
       
 36671 						e.stopPropagation();
       
 36672 						self.focus();
       
 36673 						self.hideMenu();
       
 36674 					}
       
 36675 				});
       
 36676 
       
 36677 				// Move focus to button when a menu item is selected/clicked
       
 36678 				self.menu.on('select', function() {
       
 36679 					self.focus();
       
 36680 				});
       
 36681 
       
 36682 				self.menu.on('show hide', function(e) {
       
 36683 					if (e.control == self.menu) {
       
 36684 						self.activeMenu(e.type == 'show');
       
 36685 					}
       
 36686 
       
 36687 					self.aria('expanded', e.type == 'show');
       
 36688 				}).fire('show');
       
 36689 			}
       
 36690 
       
 36691 			self.menu.show();
       
 36692 			self.menu.layoutRect({w: self.layoutRect().w});
       
 36693 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
       
 36694 		},
       
 36695 
       
 36696 		/**
       
 36697 		 * Hides the menu for the button.
       
 36698 		 *
       
 36699 		 * @method hideMenu
       
 36700 		 */
       
 36701 		hideMenu: function() {
       
 36702 			var self = this;
       
 36703 
       
 36704 			if (self.menu) {
       
 36705 				self.menu.items().each(function(item) {
       
 36706 					if (item.hideMenu) {
       
 36707 						item.hideMenu();
       
 36708 					}
       
 36709 				});
       
 36710 
       
 36711 				self.menu.hide();
       
 36712 			}
       
 36713 		},
       
 36714 
       
 36715 		/**
       
 36716 		 * Sets the active menu state.
       
 36717 		 *
       
 36718 		 * @private
       
 36719 		 */
       
 36720 		activeMenu: function(state) {
       
 36721 			this.toggleClass('active', state);
       
 36722 		},
       
 36723 
       
 36724 		/**
       
 36725 		 * Renders the control as a HTML string.
       
 36726 		 *
       
 36727 		 * @method renderHtml
       
 36728 		 * @return {String} HTML representing the control.
       
 36729 		 */
       
 36730 		renderHtml: function() {
       
 36731 			var self = this, id = self._id, prefix = self.classPrefix;
       
 36732 			var icon = self.settings.icon, image;
       
 36733 
       
 36734 			image = self.settings.image;
       
 36735 			if (image) {
       
 36736 				icon = 'none';
       
 36737 
       
 36738 				// Support for [high dpi, low dpi] image sources
       
 36739 				if (typeof image != "string") {
       
 36740 					image = window.getSelection ? image[0] : image[1];
       
 36741 				}
       
 36742 
       
 36743 				image = ' style="background-image: url(\'' + image + '\')"';
       
 36744 			} else {
       
 36745 				image = '';
       
 36746 			}
       
 36747 
       
 36748 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
       
 36749 
       
 36750 			self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
       
 36751 
       
 36752 			return (
       
 36753 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
       
 36754 					'<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
       
 36755 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 36756 						'<span>' + (self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') + '</span>' +
       
 36757 						' <i class="' + prefix + 'caret"></i>' +
       
 36758 					'</button>' +
       
 36759 				'</div>'
       
 36760 			);
       
 36761 		},
       
 36762 
       
 36763 		/**
       
 36764 		 * Gets invoked after the control has been rendered.
       
 36765 		 *
       
 36766 		 * @method postRender
       
 36767 		 */
       
 36768 		postRender: function() {
       
 36769 			var self = this;
       
 36770 
       
 36771 			self.on('click', function(e) {
       
 36772 				if (e.control === self && isChildOf(e.target, self.getEl())) {
       
 36773 					self.showMenu();
       
 36774 
       
 36775 					if (e.aria) {
       
 36776 						self.menu.items()[0].focus();
       
 36777 					}
       
 36778 				}
       
 36779 			});
       
 36780 
       
 36781 			self.on('mouseenter', function(e) {
       
 36782 				var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
       
 36783 
       
 36784 				if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
       
 36785 					parent.items().filter('MenuButton').each(function(ctrl) {
       
 36786 						if (ctrl.hideMenu && ctrl != overCtrl) {
       
 36787 							if (ctrl.menu && ctrl.menu.visible()) {
       
 36788 								hasVisibleSiblingMenu = true;
       
 36789 							}
       
 36790 
       
 36791 							ctrl.hideMenu();
       
 36792 						}
       
 36793 					});
       
 36794 
       
 36795 					if (hasVisibleSiblingMenu) {
       
 36796 						overCtrl.focus(); // Fix for: #5887
       
 36797 						overCtrl.showMenu();
       
 36798 					}
       
 36799 				}
       
 36800 			});
       
 36801 
       
 36802 			return self._super();
       
 36803 		},
       
 36804 
       
 36805 		/**
       
 36806 		 * Sets/gets the current button text.
       
 36807 		 *
       
 36808 		 * @method text
       
 36809 		 * @param {String} [text] New button text.
       
 36810 		 * @return {String|tinymce.ui.MenuButton} Current text or current MenuButton instance.
       
 36811 		 */
       
 36812 		text: function(text) {
       
 36813 			var self = this, i, children;
       
 36814 
       
 36815 			if (self._rendered) {
       
 36816 				children = self.getEl('open').getElementsByTagName('span');
       
 36817 				for (i = 0; i < children.length; i++) {
       
 36818 					children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
       
 36819 				}
       
 36820 			}
       
 36821 
       
 36822 			return this._super(text);
       
 36823 		},
       
 36824 
       
 36825 		/**
       
 36826 		 * Removes the control and it's menus.
       
 36827 		 *
       
 36828 		 * @method remove
       
 36829 		 */
       
 36830 		remove: function() {
       
 36831 			this._super();
       
 36832 
       
 36833 			if (this.menu) {
       
 36834 				this.menu.remove();
       
 36835 			}
       
 36836 		}
       
 36837 	});
       
 36838 
       
 36839 	return MenuButton;
       
 36840 });
       
 36841 
       
 36842 // Included from: js/tinymce/classes/ui/ListBox.js
       
 36843 
       
 36844 /**
       
 36845  * ListBox.js
       
 36846  *
       
 36847  * Copyright, Moxiecode Systems AB
       
 36848  * Released under LGPL License.
       
 36849  *
       
 36850  * License: http://www.tinymce.com/license
       
 36851  * Contributing: http://www.tinymce.com/contributing
       
 36852  */
       
 36853 
       
 36854 /**
       
 36855  * Creates a new list box control.
       
 36856  *
       
 36857  * @-x-less ListBox.less
       
 36858  * @class tinymce.ui.ListBox
       
 36859  * @extends tinymce.ui.MenuButton
       
 36860  */
       
 36861 define("tinymce/ui/ListBox", [
       
 36862 	"tinymce/ui/MenuButton"
       
 36863 ], function(MenuButton) {
       
 36864 	"use strict";
       
 36865 
       
 36866 	return MenuButton.extend({
       
 36867 		/**
       
 36868 		 * Constructs a instance with the specified settings.
       
 36869 		 *
       
 36870 		 * @constructor
       
 36871 		 * @param {Object} settings Name/value object with settings.
       
 36872 		 * @setting {Array} values Array with values to add to list box.
       
 36873 		 */
       
 36874 		init: function(settings) {
       
 36875 			var self = this, values, selected, selectedText, lastItemCtrl;
       
 36876 
       
 36877 			function setSelected(menuValues) {
       
 36878 				// Try to find a selected value
       
 36879 				for (var i = 0; i < menuValues.length; i++) {
       
 36880 					selected = menuValues[i].selected || settings.value === menuValues[i].value;
       
 36881 
       
 36882 					if (selected) {
       
 36883 						selectedText = selectedText || menuValues[i].text;
       
 36884 						self._value = menuValues[i].value;
       
 36885 						break;
       
 36886 					}
       
 36887 
       
 36888 					// If the value has a submenu, try to find the selected values in that menu
       
 36889 					if (menuValues[i].menu) {
       
 36890 						setSelected(menuValues[i].menu);
       
 36891 					}
       
 36892 				}
       
 36893 			}
       
 36894 
       
 36895 			self._values = values = settings.values;
       
 36896 			if (values) {
       
 36897 				setSelected(values);
       
 36898 
       
 36899 				// Default with first item
       
 36900 				if (!selected && values.length > 0) {
       
 36901 					selectedText = values[0].text;
       
 36902 					self._value = values[0].value;
       
 36903 				}
       
 36904 
       
 36905 				settings.menu = values;
       
 36906 			}
       
 36907 
       
 36908 			settings.text = settings.text || selectedText || values[0].text;
       
 36909 
       
 36910 			self._super(settings);
       
 36911 			self.addClass('listbox');
       
 36912 
       
 36913 			self.on('select', function(e) {
       
 36914 				var ctrl = e.control;
       
 36915 
       
 36916 				if (lastItemCtrl) {
       
 36917 					e.lastControl = lastItemCtrl;
       
 36918 				}
       
 36919 
       
 36920 				if (settings.multiple) {
       
 36921 					ctrl.active(!ctrl.active());
       
 36922 				} else {
       
 36923 					self.value(e.control.settings.value);
       
 36924 				}
       
 36925 
       
 36926 				lastItemCtrl = ctrl;
       
 36927 			});
       
 36928 		},
       
 36929 
       
 36930 		/**
       
 36931 		 * Getter/setter function for the control value.
       
 36932 		 *
       
 36933 		 * @method value
       
 36934 		 * @param {String} [value] Value to be set.
       
 36935 		 * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
       
 36936 		 */
       
 36937 		value: function(value) {
       
 36938 			var self = this, active, selectedText, menu;
       
 36939 
       
 36940 			function activateByValue(menu, value) {
       
 36941 				menu.items().each(function(ctrl) {
       
 36942 					active = ctrl.value() === value;
       
 36943 
       
 36944 					if (active) {
       
 36945 						selectedText = selectedText || ctrl.text();
       
 36946 					}
       
 36947 
       
 36948 					ctrl.active(active);
       
 36949 
       
 36950 					if (ctrl.menu) {
       
 36951 						activateByValue(ctrl.menu, value);
       
 36952 					}
       
 36953 				});
       
 36954 			}
       
 36955 
       
 36956 			function setActiveValues(menuValues) {
       
 36957 				for (var i = 0; i < menuValues.length; i++) {
       
 36958 					active = menuValues[i].value == value;
       
 36959 
       
 36960 					if (active) {
       
 36961 						selectedText = selectedText || menuValues[i].text;
       
 36962 					}
       
 36963 
       
 36964 					menuValues[i].active = active;
       
 36965 
       
 36966 					if (menuValues[i].menu) {
       
 36967 						setActiveValues(menuValues[i].menu);
       
 36968 					}
       
 36969 				}
       
 36970 			}
       
 36971 
       
 36972 			if (typeof value != "undefined") {
       
 36973 				if (self.menu) {
       
 36974 					activateByValue(self.menu, value);
       
 36975 				} else {
       
 36976 					menu = self.settings.menu;
       
 36977 					setActiveValues(menu);
       
 36978 				}
       
 36979 
       
 36980 				self.text(selectedText || this.settings.text);
       
 36981 			}
       
 36982 
       
 36983 			return self._super(value);
       
 36984 		}
       
 36985 	});
       
 36986 });
       
 36987 
       
 36988 // Included from: js/tinymce/classes/ui/MenuItem.js
       
 36989 
       
 36990 /**
       
 36991  * MenuItem.js
       
 36992  *
       
 36993  * Copyright, Moxiecode Systems AB
       
 36994  * Released under LGPL License.
       
 36995  *
       
 36996  * License: http://www.tinymce.com/license
       
 36997  * Contributing: http://www.tinymce.com/contributing
       
 36998  */
       
 36999 
       
 37000 /**
       
 37001  * Creates a new menu item.
       
 37002  *
       
 37003  * @-x-less MenuItem.less
       
 37004  * @class tinymce.ui.MenuItem
       
 37005  * @extends tinymce.ui.Widget
       
 37006  */
       
 37007 define("tinymce/ui/MenuItem", [
       
 37008 	"tinymce/ui/Widget",
       
 37009 	"tinymce/ui/Factory",
       
 37010 	"tinymce/Env"
       
 37011 ], function(Widget, Factory, Env) {
       
 37012 	"use strict";
       
 37013 
       
 37014 	return Widget.extend({
       
 37015 		Defaults: {
       
 37016 			border: 0,
       
 37017 			role: 'menuitem'
       
 37018 		},
       
 37019 
       
 37020 		/**
       
 37021 		 * Constructs a instance with the specified settings.
       
 37022 		 *
       
 37023 		 * @constructor
       
 37024 		 * @param {Object} settings Name/value object with settings.
       
 37025 		 * @setting {Boolean} selectable Selectable menu.
       
 37026 		 * @setting {Array} menu Submenu array with items.
       
 37027 		 * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
       
 37028 		 */
       
 37029 		init: function(settings) {
       
 37030 			var self = this;
       
 37031 
       
 37032 			self.hasPopup = true;
       
 37033 
       
 37034 			self._super(settings);
       
 37035 
       
 37036 			settings = self.settings;
       
 37037 
       
 37038 			self.addClass('menu-item');
       
 37039 
       
 37040 			if (settings.menu) {
       
 37041 				self.addClass('menu-item-expand');
       
 37042 			}
       
 37043 
       
 37044 			if (settings.preview) {
       
 37045 				self.addClass('menu-item-preview');
       
 37046 			}
       
 37047 
       
 37048 			if (self._text === '-' || self._text === '|') {
       
 37049 				self.addClass('menu-item-sep');
       
 37050 				self.aria('role', 'separator');
       
 37051 				self._text = '-';
       
 37052 			}
       
 37053 
       
 37054 			if (settings.selectable) {
       
 37055 				self.aria('role', 'menuitemcheckbox');
       
 37056 				self.addClass('menu-item-checkbox');
       
 37057 				settings.icon = 'selected';
       
 37058 			}
       
 37059 
       
 37060 			if (!settings.preview && !settings.selectable) {
       
 37061 				self.addClass('menu-item-normal');
       
 37062 			}
       
 37063 
       
 37064 			self.on('mousedown', function(e) {
       
 37065 				e.preventDefault();
       
 37066 			});
       
 37067 
       
 37068 			if (settings.menu && !settings.ariaHideMenu) {
       
 37069 				self.aria('haspopup', true);
       
 37070 			}
       
 37071 		},
       
 37072 
       
 37073 		/**
       
 37074 		 * Returns true/false if the menuitem has sub menu.
       
 37075 		 *
       
 37076 		 * @method hasMenus
       
 37077 		 * @return {Boolean} True/false state if it has submenu.
       
 37078 		 */
       
 37079 		hasMenus: function() {
       
 37080 			return !!this.settings.menu;
       
 37081 		},
       
 37082 
       
 37083 		/**
       
 37084 		 * Shows the menu for the menu item.
       
 37085 		 *
       
 37086 		 * @method showMenu
       
 37087 		 */
       
 37088 		showMenu: function() {
       
 37089 			var self = this, settings = self.settings, menu, parent = self.parent();
       
 37090 
       
 37091 			parent.items().each(function(ctrl) {
       
 37092 				if (ctrl !== self) {
       
 37093 					ctrl.hideMenu();
       
 37094 				}
       
 37095 			});
       
 37096 
       
 37097 			if (settings.menu) {
       
 37098 				menu = self.menu;
       
 37099 
       
 37100 				if (!menu) {
       
 37101 					menu = settings.menu;
       
 37102 
       
 37103 					// Is menu array then auto constuct menu control
       
 37104 					if (menu.length) {
       
 37105 						menu = {
       
 37106 							type: 'menu',
       
 37107 							items: menu
       
 37108 						};
       
 37109 					} else {
       
 37110 						menu.type = menu.type || 'menu';
       
 37111 					}
       
 37112 
       
 37113 					if (parent.settings.itemDefaults) {
       
 37114 						menu.itemDefaults = parent.settings.itemDefaults;
       
 37115 					}
       
 37116 
       
 37117 					menu = self.menu = Factory.create(menu).parent(self).renderTo();
       
 37118 					menu.reflow();
       
 37119 					menu.on('cancel', function(e) {
       
 37120 						e.stopPropagation();
       
 37121 						self.focus();
       
 37122 						menu.hide();
       
 37123 					});
       
 37124 					menu.on('show hide', function(e) {
       
 37125 						e.control.items().each(function(ctrl) {
       
 37126 							ctrl.active(ctrl.settings.selected);
       
 37127 						});
       
 37128 					}).fire('show');
       
 37129 
       
 37130 					menu.on('hide', function(e) {
       
 37131 						if (e.control === menu) {
       
 37132 							self.removeClass('selected');
       
 37133 						}
       
 37134 					});
       
 37135 
       
 37136 					menu.submenu = true;
       
 37137 				} else {
       
 37138 					menu.show();
       
 37139 				}
       
 37140 
       
 37141 				menu._parentMenu = parent;
       
 37142 
       
 37143 				menu.addClass('menu-sub');
       
 37144 
       
 37145 				var rel = menu.testMoveRel(
       
 37146 					self.getEl(),
       
 37147 					self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
       
 37148 				);
       
 37149 
       
 37150 				menu.moveRel(self.getEl(), rel);
       
 37151 				menu.rel = rel;
       
 37152 
       
 37153 				rel = 'menu-sub-' + rel;
       
 37154 				menu.removeClass(menu._lastRel);
       
 37155 				menu.addClass(rel);
       
 37156 				menu._lastRel = rel;
       
 37157 
       
 37158 				self.addClass('selected');
       
 37159 				self.aria('expanded', true);
       
 37160 			}
       
 37161 		},
       
 37162 
       
 37163 		/**
       
 37164 		 * Hides the menu for the menu item.
       
 37165 		 *
       
 37166 		 * @method hideMenu
       
 37167 		 */
       
 37168 		hideMenu: function() {
       
 37169 			var self = this;
       
 37170 
       
 37171 			if (self.menu) {
       
 37172 				self.menu.items().each(function(item) {
       
 37173 					if (item.hideMenu) {
       
 37174 						item.hideMenu();
       
 37175 					}
       
 37176 				});
       
 37177 
       
 37178 				self.menu.hide();
       
 37179 				self.aria('expanded', false);
       
 37180 			}
       
 37181 
       
 37182 			return self;
       
 37183 		},
       
 37184 
       
 37185 		/**
       
 37186 		 * Renders the control as a HTML string.
       
 37187 		 *
       
 37188 		 * @method renderHtml
       
 37189 		 * @return {String} HTML representing the control.
       
 37190 		 */
       
 37191 		renderHtml: function() {
       
 37192 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
       
 37193 			var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
       
 37194 
       
 37195 			// Converts shortcut format to Mac/PC variants
       
 37196 			function convertShortcut(shortcut) {
       
 37197 				var i, value, replace = {};
       
 37198 
       
 37199 				if (Env.mac) {
       
 37200 					replace = {
       
 37201 						alt: '&#x2325;',
       
 37202 						ctrl: '&#x2318;',
       
 37203 						shift: '&#x21E7;',
       
 37204 						meta: '&#x2318;'
       
 37205 					};
       
 37206 				} else {
       
 37207 					replace = {
       
 37208 						meta: 'Ctrl'
       
 37209 					};
       
 37210 				}
       
 37211 
       
 37212 				shortcut = shortcut.split('+');
       
 37213 
       
 37214 				for (i = 0; i < shortcut.length; i++) {
       
 37215 					value = replace[shortcut[i].toLowerCase()];
       
 37216 
       
 37217 					if (value) {
       
 37218 						shortcut[i] = value;
       
 37219 					}
       
 37220 				}
       
 37221 
       
 37222 				return shortcut.join('+');
       
 37223 			}
       
 37224 
       
 37225 			if (icon) {
       
 37226 				self.parent().addClass('menu-has-icons');
       
 37227 			}
       
 37228 
       
 37229 			if (settings.image) {
       
 37230 				icon = 'none';
       
 37231 				image = ' style="background-image: url(\'' + settings.image + '\')"';
       
 37232 			}
       
 37233 
       
 37234 			if (shortcut) {
       
 37235 				shortcut = convertShortcut(shortcut);
       
 37236 			}
       
 37237 
       
 37238 			icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
       
 37239 
       
 37240 			return (
       
 37241 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
       
 37242 					(text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
       
 37243 					(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
       
 37244 					(shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
       
 37245 					(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
       
 37246 				'</div>'
       
 37247 			);
       
 37248 		},
       
 37249 
       
 37250 		/**
       
 37251 		 * Gets invoked after the control has been rendered.
       
 37252 		 *
       
 37253 		 * @method postRender
       
 37254 		 */
       
 37255 		postRender: function() {
       
 37256 			var self = this, settings = self.settings;
       
 37257 
       
 37258 			var textStyle = settings.textStyle;
       
 37259 			if (typeof textStyle == "function") {
       
 37260 				textStyle = textStyle.call(this);
       
 37261 			}
       
 37262 
       
 37263 			if (textStyle) {
       
 37264 				var textElm = self.getEl('text');
       
 37265 				if (textElm) {
       
 37266 					textElm.setAttribute('style', textStyle);
       
 37267 				}
       
 37268 			}
       
 37269 
       
 37270 			self.on('mouseenter click', function(e) {
       
 37271 				if (e.control === self) {
       
 37272 					if (!settings.menu && e.type === 'click') {
       
 37273 						self.fire('select');
       
 37274 						self.parent().hideAll();
       
 37275 					} else {
       
 37276 						self.showMenu();
       
 37277 
       
 37278 						if (e.aria) {
       
 37279 							self.menu.focus(true);
       
 37280 						}
       
 37281 					}
       
 37282 				}
       
 37283 			});
       
 37284 
       
 37285 			self._super();
       
 37286 
       
 37287 			return self;
       
 37288 		},
       
 37289 
       
 37290 		active: function(state) {
       
 37291 			if (typeof state != "undefined") {
       
 37292 				this.aria('checked', state);
       
 37293 			}
       
 37294 
       
 37295 			return this._super(state);
       
 37296 		},
       
 37297 
       
 37298 		/**
       
 37299 		 * Removes the control and it's menus.
       
 37300 		 *
       
 37301 		 * @method remove
       
 37302 		 */
       
 37303 		remove: function() {
       
 37304 			this._super();
       
 37305 
       
 37306 			if (this.menu) {
       
 37307 				this.menu.remove();
       
 37308 			}
       
 37309 		}
       
 37310 	});
       
 37311 });
       
 37312 
       
 37313 // Included from: js/tinymce/classes/ui/Menu.js
       
 37314 
       
 37315 /**
       
 37316  * Menu.js
       
 37317  *
       
 37318  * Copyright, Moxiecode Systems AB
       
 37319  * Released under LGPL License.
       
 37320  *
       
 37321  * License: http://www.tinymce.com/license
       
 37322  * Contributing: http://www.tinymce.com/contributing
       
 37323  */
       
 37324 
       
 37325 /**
       
 37326  * Creates a new menu.
       
 37327  *
       
 37328  * @-x-less Menu.less
       
 37329  * @class tinymce.ui.Menu
       
 37330  * @extends tinymce.ui.FloatPanel
       
 37331  */
       
 37332 define("tinymce/ui/Menu", [
       
 37333 	"tinymce/ui/FloatPanel",
       
 37334 	"tinymce/ui/MenuItem",
       
 37335 	"tinymce/util/Tools"
       
 37336 ], function(FloatPanel, MenuItem, Tools) {
       
 37337 	"use strict";
       
 37338 
       
 37339 	var Menu = FloatPanel.extend({
       
 37340 		Defaults: {
       
 37341 			defaultType: 'menuitem',
       
 37342 			border: 1,
       
 37343 			layout: 'stack',
       
 37344 			role: 'application',
       
 37345 			bodyRole: 'menu',
       
 37346 			ariaRoot: true
       
 37347 		},
       
 37348 
       
 37349 		/**
       
 37350 		 * Constructs a instance with the specified settings.
       
 37351 		 *
       
 37352 		 * @constructor
       
 37353 		 * @param {Object} settings Name/value object with settings.
       
 37354 		 */
       
 37355 		init: function(settings) {
       
 37356 			var self = this;
       
 37357 
       
 37358 			settings.autohide = true;
       
 37359 			settings.constrainToViewport = true;
       
 37360 
       
 37361 			if (settings.itemDefaults) {
       
 37362 				var items = settings.items, i = items.length;
       
 37363 
       
 37364 				while (i--) {
       
 37365 					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
       
 37366 				}
       
 37367 			}
       
 37368 
       
 37369 			self._super(settings);
       
 37370 			self.addClass('menu');
       
 37371 		},
       
 37372 
       
 37373 		/**
       
 37374 		 * Repaints the control after a layout operation.
       
 37375 		 *
       
 37376 		 * @method repaint
       
 37377 		 */
       
 37378 		repaint: function() {
       
 37379 			this.toggleClass('menu-align', true);
       
 37380 
       
 37381 			this._super();
       
 37382 
       
 37383 			this.getEl().style.height = '';
       
 37384 			this.getEl('body').style.height = '';
       
 37385 
       
 37386 			return this;
       
 37387 		},
       
 37388 
       
 37389 		/**
       
 37390 		 * Hides/closes the menu.
       
 37391 		 *
       
 37392 		 * @method cancel
       
 37393 		 */
       
 37394 		cancel: function() {
       
 37395 			var self = this;
       
 37396 
       
 37397 			self.hideAll();
       
 37398 			self.fire('select');
       
 37399 		},
       
 37400 
       
 37401 		/**
       
 37402 		 * Hide menu and all sub menus.
       
 37403 		 *
       
 37404 		 * @method hideAll
       
 37405 		 */
       
 37406 		hideAll: function() {
       
 37407 			var self = this;
       
 37408 
       
 37409 			this.find('menuitem').exec('hideMenu');
       
 37410 
       
 37411 			return self._super();
       
 37412 		},
       
 37413 /*
       
 37414 		getContainerElm: function() {
       
 37415 			var doc = document, id = this.classPrefix + 'menucontainer';
       
 37416 
       
 37417 			var elm = doc.getElementById(id);
       
 37418 			if (!elm) {
       
 37419 				elm = doc.createElement('div');
       
 37420 				elm.id = id;
       
 37421 				elm.setAttribute('role', 'application');
       
 37422 				elm.className = this.classPrefix + '-reset';
       
 37423 				elm.style.position = 'absolute';
       
 37424 				elm.style.top = elm.style.left = '0';
       
 37425 				elm.style.overflow = 'visible';
       
 37426 				doc.body.appendChild(elm);
       
 37427 			}
       
 37428 
       
 37429 			return elm;
       
 37430 		},
       
 37431 */
       
 37432 		/**
       
 37433 		 * Invoked before the menu is rendered.
       
 37434 		 *
       
 37435 		 * @method preRender
       
 37436 		 */
       
 37437 		preRender: function() {
       
 37438 			var self = this;
       
 37439 
       
 37440 			self.items().each(function(ctrl) {
       
 37441 				var settings = ctrl.settings;
       
 37442 
       
 37443 				if (settings.icon || settings.selectable) {
       
 37444 					self._hasIcons = true;
       
 37445 					return false;
       
 37446 				}
       
 37447 			});
       
 37448 
       
 37449 			return self._super();
       
 37450 		}
       
 37451 	});
       
 37452 
       
 37453 	return Menu;
       
 37454 });
       
 37455 
       
 37456 // Included from: js/tinymce/classes/ui/Radio.js
       
 37457 
       
 37458 /**
       
 37459  * Radio.js
       
 37460  *
       
 37461  * Copyright, Moxiecode Systems AB
       
 37462  * Released under LGPL License.
       
 37463  *
       
 37464  * License: http://www.tinymce.com/license
       
 37465  * Contributing: http://www.tinymce.com/contributing
       
 37466  */
       
 37467 
       
 37468 /**
       
 37469  * Creates a new radio button.
       
 37470  *
       
 37471  * @-x-less Radio.less
       
 37472  * @class tinymce.ui.Radio
       
 37473  * @extends tinymce.ui.Checkbox
       
 37474  */
       
 37475 define("tinymce/ui/Radio", [
       
 37476 	"tinymce/ui/Checkbox"
       
 37477 ], function(Checkbox) {
       
 37478 	"use strict";
       
 37479 
       
 37480 	return Checkbox.extend({
       
 37481 		Defaults: {
       
 37482 			classes: "radio",
       
 37483 			role: "radio"
       
 37484 		}
       
 37485 	});
       
 37486 });
       
 37487 
       
 37488 // Included from: js/tinymce/classes/ui/ResizeHandle.js
       
 37489 
       
 37490 /**
       
 37491  * ResizeHandle.js
       
 37492  *
       
 37493  * Copyright, Moxiecode Systems AB
       
 37494  * Released under LGPL License.
       
 37495  *
       
 37496  * License: http://www.tinymce.com/license
       
 37497  * Contributing: http://www.tinymce.com/contributing
       
 37498  */
       
 37499 
       
 37500 /**
       
 37501  * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events.
       
 37502  *
       
 37503  * @-x-less ResizeHandle.less
       
 37504  * @class tinymce.ui.ResizeHandle
       
 37505  * @extends tinymce.ui.Widget
       
 37506  */
       
 37507 define("tinymce/ui/ResizeHandle", [
       
 37508 	"tinymce/ui/Widget",
       
 37509 	"tinymce/ui/DragHelper"
       
 37510 ], function(Widget, DragHelper) {
       
 37511 	"use strict";
       
 37512 
       
 37513 	return Widget.extend({
       
 37514 		/**
       
 37515 		 * Renders the control as a HTML string.
       
 37516 		 *
       
 37517 		 * @method renderHtml
       
 37518 		 * @return {String} HTML representing the control.
       
 37519 		 */
       
 37520 		renderHtml: function() {
       
 37521 			var self = this, prefix = self.classPrefix;
       
 37522 
       
 37523 			self.addClass('resizehandle');
       
 37524 
       
 37525 			if (self.settings.direction == "both") {
       
 37526 				self.addClass('resizehandle-both');
       
 37527 			}
       
 37528 
       
 37529 			self.canFocus = false;
       
 37530 
       
 37531 			return (
       
 37532 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
       
 37533 					'<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' +
       
 37534 				'</div>'
       
 37535 			);
       
 37536 		},
       
 37537 
       
 37538 		/**
       
 37539 		 * Called after the control has been rendered.
       
 37540 		 *
       
 37541 		 * @method postRender
       
 37542 		 */
       
 37543 		postRender: function() {
       
 37544 			var self = this;
       
 37545 
       
 37546 			self._super();
       
 37547 
       
 37548 			self.resizeDragHelper = new DragHelper(this._id, {
       
 37549 				start: function() {
       
 37550 					self.fire('ResizeStart');
       
 37551 				},
       
 37552 
       
 37553 				drag: function(e) {
       
 37554 					if (self.settings.direction != "both") {
       
 37555 						e.deltaX = 0;
       
 37556 					}
       
 37557 
       
 37558 					self.fire('Resize', e);
       
 37559 				},
       
 37560 
       
 37561 				stop: function() {
       
 37562 					self.fire('ResizeEnd');
       
 37563 				}
       
 37564 			});
       
 37565 		},
       
 37566 
       
 37567 		remove: function() {
       
 37568 			if (this.resizeDragHelper) {
       
 37569 				this.resizeDragHelper.destroy();
       
 37570 			}
       
 37571 
       
 37572 			return this._super();
       
 37573 		}
       
 37574 	});
       
 37575 });
       
 37576 
       
 37577 // Included from: js/tinymce/classes/ui/Spacer.js
       
 37578 
       
 37579 /**
       
 37580  * Spacer.js
       
 37581  *
       
 37582  * Copyright, Moxiecode Systems AB
       
 37583  * Released under LGPL License.
       
 37584  *
       
 37585  * License: http://www.tinymce.com/license
       
 37586  * Contributing: http://www.tinymce.com/contributing
       
 37587  */
       
 37588 
       
 37589 /**
       
 37590  * Creates a spacer. This control is used in flex layouts for example.
       
 37591  *
       
 37592  * @-x-less Spacer.less
       
 37593  * @class tinymce.ui.Spacer
       
 37594  * @extends tinymce.ui.Widget
       
 37595  */
       
 37596 define("tinymce/ui/Spacer", [
       
 37597 	"tinymce/ui/Widget"
       
 37598 ], function(Widget) {
       
 37599 	"use strict";
       
 37600 
       
 37601 	return Widget.extend({
       
 37602 		/**
       
 37603 		 * Renders the control as a HTML string.
       
 37604 		 *
       
 37605 		 * @method renderHtml
       
 37606 		 * @return {String} HTML representing the control.
       
 37607 		 */
       
 37608 		renderHtml: function() {
       
 37609 			var self = this;
       
 37610 
       
 37611 			self.addClass('spacer');
       
 37612 			self.canFocus = false;
       
 37613 
       
 37614 			return '<div id="' + self._id + '" class="' + self.classes() + '"></div>';
       
 37615 		}
       
 37616 	});
       
 37617 });
       
 37618 
       
 37619 // Included from: js/tinymce/classes/ui/SplitButton.js
       
 37620 
       
 37621 /**
       
 37622  * SplitButton.js
       
 37623  *
       
 37624  * Copyright, Moxiecode Systems AB
       
 37625  * Released under LGPL License.
       
 37626  *
       
 37627  * License: http://www.tinymce.com/license
       
 37628  * Contributing: http://www.tinymce.com/contributing
       
 37629  */
       
 37630 
       
 37631 /**
       
 37632  * Creates a split button.
       
 37633  *
       
 37634  * @-x-less SplitButton.less
       
 37635  * @class tinymce.ui.SplitButton
       
 37636  * @extends tinymce.ui.MenuButton
       
 37637  */
       
 37638 define("tinymce/ui/SplitButton", [
       
 37639 	"tinymce/ui/MenuButton",
       
 37640 	"tinymce/ui/DomUtils"
       
 37641 ], function(MenuButton, DomUtils) {
       
 37642 	return MenuButton.extend({
       
 37643 		Defaults: {
       
 37644 			classes: "widget btn splitbtn",
       
 37645 			role: "button"
       
 37646 		},
       
 37647 
       
 37648 		/**
       
 37649 		 * Repaints the control after a layout operation.
       
 37650 		 *
       
 37651 		 * @method repaint
       
 37652 		 */
       
 37653 		repaint: function() {
       
 37654 			var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm;
       
 37655 
       
 37656 			self._super();
       
 37657 
       
 37658 			mainButtonElm = elm.firstChild;
       
 37659 			menuButtonElm = elm.lastChild;
       
 37660 
       
 37661 			DomUtils.css(mainButtonElm, {
       
 37662 				width: rect.w - DomUtils.getSize(menuButtonElm).width,
       
 37663 				height: rect.h - 2
       
 37664 			});
       
 37665 
       
 37666 			DomUtils.css(menuButtonElm, {
       
 37667 				height: rect.h - 2
       
 37668 			});
       
 37669 
       
 37670 			return self;
       
 37671 		},
       
 37672 
       
 37673 		/**
       
 37674 		 * Sets the active menu state.
       
 37675 		 *
       
 37676 		 * @private
       
 37677 		 */
       
 37678 		activeMenu: function(state) {
       
 37679 			var self = this;
       
 37680 
       
 37681 			DomUtils.toggleClass(self.getEl().lastChild, self.classPrefix + 'active', state);
       
 37682 		},
       
 37683 
       
 37684 		/**
       
 37685 		 * Renders the control as a HTML string.
       
 37686 		 *
       
 37687 		 * @method renderHtml
       
 37688 		 * @return {String} HTML representing the control.
       
 37689 		 */
       
 37690 		renderHtml: function() {
       
 37691 			var self = this, id = self._id, prefix = self.classPrefix, image;
       
 37692 			var icon = self.settings.icon;
       
 37693 
       
 37694 			image = self.settings.image;
       
 37695 			if (image) {
       
 37696 				icon = 'none';
       
 37697 
       
 37698 				// Support for [high dpi, low dpi] image sources
       
 37699 				if (typeof image != "string") {
       
 37700 					image = window.getSelection ? image[0] : image[1];
       
 37701 				}
       
 37702 
       
 37703 				image = ' style="background-image: url(\'' + image + '\')"';
       
 37704 			} else {
       
 37705 				image = '';
       
 37706 			}
       
 37707 
       
 37708 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
       
 37709 
       
 37710 			return (
       
 37711 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1">' +
       
 37712 					'<button type="button" hidefocus="1" tabindex="-1">' +
       
 37713 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 37714 						(self._text ? (icon ? ' ' : '') + self._text : '') +
       
 37715 					'</button>' +
       
 37716 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
       
 37717 						//(icon ? '<i class="' + icon + '"></i>' : '') +
       
 37718 						(self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') +
       
 37719 						' <i class="' + prefix + 'caret"></i>' +
       
 37720 					'</button>' +
       
 37721 				'</div>'
       
 37722 			);
       
 37723 		},
       
 37724 
       
 37725 		/**
       
 37726 		 * Called after the control has been rendered.
       
 37727 		 *
       
 37728 		 * @method postRender
       
 37729 		 */
       
 37730 		postRender: function() {
       
 37731 			var self = this, onClickHandler = self.settings.onclick;
       
 37732 
       
 37733 			self.on('click', function(e) {
       
 37734 				var node = e.target;
       
 37735 
       
 37736 				if (e.control == this) {
       
 37737 					// Find clicks that is on the main button
       
 37738 					while (node) {
       
 37739 						if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) {
       
 37740 							e.stopImmediatePropagation();
       
 37741 							onClickHandler.call(this, e);
       
 37742 							return;
       
 37743 						}
       
 37744 
       
 37745 						node = node.parentNode;
       
 37746 					}
       
 37747 				}
       
 37748 			});
       
 37749 
       
 37750 			delete self.settings.onclick;
       
 37751 
       
 37752 			return self._super();
       
 37753 		}
       
 37754 	});
       
 37755 });
       
 37756 
       
 37757 // Included from: js/tinymce/classes/ui/StackLayout.js
       
 37758 
       
 37759 /**
       
 37760  * StackLayout.js
       
 37761  *
       
 37762  * Copyright, Moxiecode Systems AB
       
 37763  * Released under LGPL License.
       
 37764  *
       
 37765  * License: http://www.tinymce.com/license
       
 37766  * Contributing: http://www.tinymce.com/contributing
       
 37767  */
       
 37768 
       
 37769 /**
       
 37770  * This layout uses the browsers layout when the items are blocks.
       
 37771  *
       
 37772  * @-x-less StackLayout.less
       
 37773  * @class tinymce.ui.StackLayout
       
 37774  * @extends tinymce.ui.FlowLayout
       
 37775  */
       
 37776 define("tinymce/ui/StackLayout", [
       
 37777 	"tinymce/ui/FlowLayout"
       
 37778 ], function(FlowLayout) {
       
 37779 	"use strict";
       
 37780 
       
 37781 	return FlowLayout.extend({
       
 37782 		Defaults: {
       
 37783 			containerClass: 'stack-layout',
       
 37784 			controlClass: 'stack-layout-item',
       
 37785 			endClass: 'break'
       
 37786 		}
       
 37787 	});
       
 37788 });
       
 37789 
       
 37790 // Included from: js/tinymce/classes/ui/TabPanel.js
       
 37791 
       
 37792 /**
       
 37793  * TabPanel.js
       
 37794  *
       
 37795  * Copyright, Moxiecode Systems AB
       
 37796  * Released under LGPL License.
       
 37797  *
       
 37798  * License: http://www.tinymce.com/license
       
 37799  * Contributing: http://www.tinymce.com/contributing
       
 37800  */
       
 37801 
       
 37802 /**
       
 37803  * Creates a tab panel control.
       
 37804  *
       
 37805  * @-x-less TabPanel.less
       
 37806  * @class tinymce.ui.TabPanel
       
 37807  * @extends tinymce.ui.Panel
       
 37808  *
       
 37809  * @setting {Number} activeTab Active tab index.
       
 37810  */
       
 37811 define("tinymce/ui/TabPanel", [
       
 37812 	"tinymce/ui/Panel",
       
 37813 	"tinymce/ui/DomUtils"
       
 37814 ], function(Panel, DomUtils) {
       
 37815 	"use strict";
       
 37816 
       
 37817 	return Panel.extend({
       
 37818 		Defaults: {
       
 37819 			layout: 'absolute',
       
 37820 			defaults: {
       
 37821 				type: 'panel'
       
 37822 			}
       
 37823 		},
       
 37824 
       
 37825 		/**
       
 37826 		 * Activates the specified tab by index.
       
 37827 		 *
       
 37828 		 * @method activateTab
       
 37829 		 * @param {Number} idx Index of the tab to activate.
       
 37830 		 */
       
 37831 		activateTab: function(idx) {
       
 37832 			var activeTabElm;
       
 37833 
       
 37834 			if (this.activeTabId) {
       
 37835 				activeTabElm = this.getEl(this.activeTabId);
       
 37836 				DomUtils.removeClass(activeTabElm, this.classPrefix + 'active');
       
 37837 				activeTabElm.setAttribute('aria-selected', "false");
       
 37838 			}
       
 37839 
       
 37840 			this.activeTabId = 't' + idx;
       
 37841 
       
 37842 			activeTabElm = this.getEl('t' + idx);
       
 37843 			activeTabElm.setAttribute('aria-selected', "true");
       
 37844 			DomUtils.addClass(activeTabElm, this.classPrefix + 'active');
       
 37845 
       
 37846 			this.items()[idx].show().fire('showtab');
       
 37847 			this.reflow();
       
 37848 
       
 37849 			this.items().each(function(item, i) {
       
 37850 				if (idx != i) {
       
 37851 					item.hide();
       
 37852 				}
       
 37853 			});
       
 37854 		},
       
 37855 
       
 37856 		/**
       
 37857 		 * Renders the control as a HTML string.
       
 37858 		 *
       
 37859 		 * @method renderHtml
       
 37860 		 * @return {String} HTML representing the control.
       
 37861 		 */
       
 37862 		renderHtml: function() {
       
 37863 			var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix;
       
 37864 
       
 37865 			self.preRender();
       
 37866 			layout.preRender(self);
       
 37867 
       
 37868 			self.items().each(function(ctrl, i) {
       
 37869 				var id = self._id + '-t' + i;
       
 37870 
       
 37871 				ctrl.aria('role', 'tabpanel');
       
 37872 				ctrl.aria('labelledby', id);
       
 37873 
       
 37874 				tabsHtml += (
       
 37875 					'<div id="' + id + '" class="' + prefix + 'tab" ' +
       
 37876 						'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' +
       
 37877 						self.encode(ctrl.settings.title) +
       
 37878 					'</div>'
       
 37879 				);
       
 37880 			});
       
 37881 
       
 37882 			return (
       
 37883 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
       
 37884 					'<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' +
       
 37885 						tabsHtml +
       
 37886 					'</div>' +
       
 37887 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 37888 						layout.renderHtml(self) +
       
 37889 					'</div>' +
       
 37890 				'</div>'
       
 37891 			);
       
 37892 		},
       
 37893 
       
 37894 		/**
       
 37895 		 * Called after the control has been rendered.
       
 37896 		 *
       
 37897 		 * @method postRender
       
 37898 		 */
       
 37899 		postRender: function() {
       
 37900 			var self = this;
       
 37901 
       
 37902 			self._super();
       
 37903 
       
 37904 			self.settings.activeTab = self.settings.activeTab || 0;
       
 37905 			self.activateTab(self.settings.activeTab);
       
 37906 
       
 37907 			this.on('click', function(e) {
       
 37908 				var targetParent = e.target.parentNode;
       
 37909 
       
 37910 				if (e.target.parentNode.id == self._id + '-head') {
       
 37911 					var i = targetParent.childNodes.length;
       
 37912 
       
 37913 					while (i--) {
       
 37914 						if (targetParent.childNodes[i] == e.target) {
       
 37915 							self.activateTab(i);
       
 37916 						}
       
 37917 					}
       
 37918 				}
       
 37919 			});
       
 37920 		},
       
 37921 
       
 37922 		/**
       
 37923 		 * Initializes the current controls layout rect.
       
 37924 		 * This will be executed by the layout managers to determine the
       
 37925 		 * default minWidth/minHeight etc.
       
 37926 		 *
       
 37927 		 * @method initLayoutRect
       
 37928 		 * @return {Object} Layout rect instance.
       
 37929 		 */
       
 37930 		initLayoutRect: function() {
       
 37931 			var self = this, rect, minW, minH;
       
 37932 
       
 37933 			minW = DomUtils.getSize(self.getEl('head')).width;
       
 37934 			minW = minW < 0 ? 0 : minW;
       
 37935 			minH = 0;
       
 37936 
       
 37937 			self.items().each(function(item) {
       
 37938 				minW = Math.max(minW, item.layoutRect().minW);
       
 37939 				minH = Math.max(minH, item.layoutRect().minH);
       
 37940 			});
       
 37941 
       
 37942 			self.items().each(function(ctrl) {
       
 37943 				ctrl.settings.x = 0;
       
 37944 				ctrl.settings.y = 0;
       
 37945 				ctrl.settings.w = minW;
       
 37946 				ctrl.settings.h = minH;
       
 37947 
       
 37948 				ctrl.layoutRect({
       
 37949 					x: 0,
       
 37950 					y: 0,
       
 37951 					w: minW,
       
 37952 					h: minH
       
 37953 				});
       
 37954 			});
       
 37955 
       
 37956 			var headH = DomUtils.getSize(self.getEl('head')).height;
       
 37957 
       
 37958 			self.settings.minWidth = minW;
       
 37959 			self.settings.minHeight = minH + headH;
       
 37960 
       
 37961 			rect = self._super();
       
 37962 			rect.deltaH += headH;
       
 37963 			rect.innerH = rect.h - rect.deltaH;
       
 37964 
       
 37965 			return rect;
       
 37966 		}
       
 37967 	});
       
 37968 });
       
 37969 
       
 37970 // Included from: js/tinymce/classes/ui/TextBox.js
       
 37971 
       
 37972 /**
       
 37973  * TextBox.js
       
 37974  *
       
 37975  * Copyright, Moxiecode Systems AB
       
 37976  * Released under LGPL License.
       
 37977  *
       
 37978  * License: http://www.tinymce.com/license
       
 37979  * Contributing: http://www.tinymce.com/contributing
       
 37980  */
       
 37981 
       
 37982 /**
       
 37983  * Creates a new textbox.
       
 37984  *
       
 37985  * @-x-less TextBox.less
       
 37986  * @class tinymce.ui.TextBox
       
 37987  * @extends tinymce.ui.Widget
       
 37988  */
       
 37989 define("tinymce/ui/TextBox", [
       
 37990 	"tinymce/ui/Widget",
       
 37991 	"tinymce/ui/DomUtils"
       
 37992 ], function(Widget, DomUtils) {
       
 37993 	"use strict";
       
 37994 
       
 37995 	return Widget.extend({
       
 37996 		/**
       
 37997 		 * Constructs a instance with the specified settings.
       
 37998 		 *
       
 37999 		 * @constructor
       
 38000 		 * @param {Object} settings Name/value object with settings.
       
 38001 		 * @setting {Boolean} multiline True if the textbox is a multiline control.
       
 38002 		 * @setting {Number} maxLength Max length for the textbox.
       
 38003 		 * @setting {Number} size Size of the textbox in characters.
       
 38004 		 */
       
 38005 		init: function(settings) {
       
 38006 			var self = this;
       
 38007 
       
 38008 			self._super(settings);
       
 38009 
       
 38010 			self._value = settings.value || '';
       
 38011 			self.addClass('textbox');
       
 38012 
       
 38013 			if (settings.multiline) {
       
 38014 				self.addClass('multiline');
       
 38015 			} else {
       
 38016 				// TODO: Rework this
       
 38017 				self.on('keydown', function(e) {
       
 38018 					if (e.keyCode == 13) {
       
 38019 						self.parents().reverse().each(function(ctrl) {
       
 38020 							e.preventDefault();
       
 38021 
       
 38022 							if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
       
 38023 								ctrl.fire('submit', {data: ctrl.toJSON()});
       
 38024 								return false;
       
 38025 							}
       
 38026 						});
       
 38027 					}
       
 38028 				});
       
 38029 			}
       
 38030 		},
       
 38031 
       
 38032 		/**
       
 38033 		 * Getter/setter function for the disabled state.
       
 38034 		 *
       
 38035 		 * @method value
       
 38036 		 * @param {Boolean} [state] State to be set.
       
 38037 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
       
 38038 		 */
       
 38039 		disabled: function(state) {
       
 38040 			var self = this;
       
 38041 
       
 38042 			if (self._rendered && typeof state != 'undefined') {
       
 38043 				self.getEl().disabled = state;
       
 38044 			}
       
 38045 
       
 38046 			return self._super(state);
       
 38047 		},
       
 38048 
       
 38049 		/**
       
 38050 		 * Getter/setter function for the control value.
       
 38051 		 *
       
 38052 		 * @method value
       
 38053 		 * @param {String} [value] Value to be set.
       
 38054 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
       
 38055 		 */
       
 38056 		value: function(value) {
       
 38057 			var self = this;
       
 38058 
       
 38059 			if (typeof value != "undefined") {
       
 38060 				self._value = value;
       
 38061 
       
 38062 				if (self._rendered) {
       
 38063 					self.getEl().value = value;
       
 38064 				}
       
 38065 
       
 38066 				return self;
       
 38067 			}
       
 38068 
       
 38069 			if (self._rendered) {
       
 38070 				return self.getEl().value;
       
 38071 			}
       
 38072 
       
 38073 			return self._value;
       
 38074 		},
       
 38075 
       
 38076 		/**
       
 38077 		 * Repaints the control after a layout operation.
       
 38078 		 *
       
 38079 		 * @method repaint
       
 38080 		 */
       
 38081 		repaint: function() {
       
 38082 			var self = this, style, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
       
 38083 
       
 38084 			style = self.getEl().style;
       
 38085 			rect = self._layoutRect;
       
 38086 			lastRepaintRect = self._lastRepaintRect || {};
       
 38087 
       
 38088 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
       
 38089 			var doc = document;
       
 38090 			if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
       
 38091 				style.lineHeight = (rect.h - borderH) + 'px';
       
 38092 			}
       
 38093 
       
 38094 			borderBox = self._borderBox;
       
 38095 			borderW = borderBox.left + borderBox.right + 8;
       
 38096 			borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0);
       
 38097 
       
 38098 			if (rect.x !== lastRepaintRect.x) {
       
 38099 				style.left = rect.x + 'px';
       
 38100 				lastRepaintRect.x = rect.x;
       
 38101 			}
       
 38102 
       
 38103 			if (rect.y !== lastRepaintRect.y) {
       
 38104 				style.top = rect.y + 'px';
       
 38105 				lastRepaintRect.y = rect.y;
       
 38106 			}
       
 38107 
       
 38108 			if (rect.w !== lastRepaintRect.w) {
       
 38109 				style.width = (rect.w - borderW) + 'px';
       
 38110 				lastRepaintRect.w = rect.w;
       
 38111 			}
       
 38112 
       
 38113 			if (rect.h !== lastRepaintRect.h) {
       
 38114 				style.height = (rect.h - borderH) + 'px';
       
 38115 				lastRepaintRect.h = rect.h;
       
 38116 			}
       
 38117 
       
 38118 			self._lastRepaintRect = lastRepaintRect;
       
 38119 			self.fire('repaint', {}, false);
       
 38120 
       
 38121 			return self;
       
 38122 		},
       
 38123 
       
 38124 		/**
       
 38125 		 * Renders the control as a HTML string.
       
 38126 		 *
       
 38127 		 * @method renderHtml
       
 38128 		 * @return {String} HTML representing the control.
       
 38129 		 */
       
 38130 		renderHtml: function() {
       
 38131 			var self = this, id = self._id, settings = self.settings, value = self.encode(self._value, false), extraAttrs = '';
       
 38132 
       
 38133 			if ("spellcheck" in settings) {
       
 38134 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
       
 38135 			}
       
 38136 
       
 38137 			if (settings.maxLength) {
       
 38138 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
       
 38139 			}
       
 38140 
       
 38141 			if (settings.size) {
       
 38142 				extraAttrs += ' size="' + settings.size + '"';
       
 38143 			}
       
 38144 
       
 38145 			if (settings.subtype) {
       
 38146 				extraAttrs += ' type="' + settings.subtype + '"';
       
 38147 			}
       
 38148 
       
 38149 			if (self.disabled()) {
       
 38150 				extraAttrs += ' disabled="disabled"';
       
 38151 			}
       
 38152 
       
 38153 			if (settings.multiline) {
       
 38154 				return (
       
 38155 					'<textarea id="' + id + '" class="' + self.classes() + '" ' +
       
 38156 					(settings.rows ? ' rows="' + settings.rows + '"' : '') +
       
 38157 					' hidefocus="1"' + extraAttrs + '>' + value +
       
 38158 					'</textarea>'
       
 38159 				);
       
 38160 			}
       
 38161 
       
 38162 			return '<input id="' + id + '" class="' + self.classes() + '" value="' + value + '" hidefocus="1"' + extraAttrs + ' />';
       
 38163 		},
       
 38164 
       
 38165 		/**
       
 38166 		 * Called after the control has been rendered.
       
 38167 		 *
       
 38168 		 * @method postRender
       
 38169 		 */
       
 38170 		postRender: function() {
       
 38171 			var self = this;
       
 38172 
       
 38173 			DomUtils.on(self.getEl(), 'change', function(e) {
       
 38174 				self.fire('change', e);
       
 38175 			});
       
 38176 
       
 38177 			return self._super();
       
 38178 		},
       
 38179 
       
 38180 		remove: function() {
       
 38181 			DomUtils.off(this.getEl());
       
 38182 			this._super();
       
 38183 		}
       
 38184 	});
       
 38185 });
       
 38186 
       
 38187 // Included from: js/tinymce/classes/ui/Throbber.js
       
 38188 
       
 38189 /**
       
 38190  * Throbber.js
       
 38191  *
       
 38192  * Copyright, Moxiecode Systems AB
       
 38193  * Released under LGPL License.
       
 38194  *
       
 38195  * License: http://www.tinymce.com/license
       
 38196  * Contributing: http://www.tinymce.com/contributing
       
 38197  */
       
 38198 
       
 38199 /**
       
 38200  * This class enables you to display a Throbber for any element.
       
 38201  *
       
 38202  * @-x-less Throbber.less
       
 38203  * @class tinymce.ui.Throbber
       
 38204  */
       
 38205 define("tinymce/ui/Throbber", [
       
 38206 	"tinymce/ui/DomUtils",
       
 38207 	"tinymce/ui/Control"
       
 38208 ], function(DomUtils, Control) {
       
 38209 	"use strict";
       
 38210 
       
 38211 	/**
       
 38212 	 * Constructs a new throbber.
       
 38213 	 *
       
 38214 	 * @constructor
       
 38215 	 * @param {Element} elm DOM Html element to display throbber in.
       
 38216 	 * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll.
       
 38217 	 */
       
 38218 	return function(elm, inline) {
       
 38219 		var self = this, state, classPrefix = Control.classPrefix;
       
 38220 
       
 38221 		/**
       
 38222 		 * Shows the throbber.
       
 38223 		 *
       
 38224 		 * @method show
       
 38225 		 * @param {Number} [time] Time to wait before showing.
       
 38226 		 * @return {tinymce.ui.Throbber} Current throbber instance.
       
 38227 		 */
       
 38228 		self.show = function(time) {
       
 38229 			self.hide();
       
 38230 
       
 38231 			state = true;
       
 38232 
       
 38233 			window.setTimeout(function() {
       
 38234 				if (state) {
       
 38235 					elm.appendChild(DomUtils.createFragment(
       
 38236 						'<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>'
       
 38237 					));
       
 38238 				}
       
 38239 			}, time || 0);
       
 38240 
       
 38241 			return self;
       
 38242 		};
       
 38243 
       
 38244 		/**
       
 38245 		 * Hides the throbber.
       
 38246 		 *
       
 38247 		 * @method hide
       
 38248 		 * @return {tinymce.ui.Throbber} Current throbber instance.
       
 38249 		 */
       
 38250 		self.hide = function() {
       
 38251 			var child = elm.lastChild;
       
 38252 
       
 38253 			if (child && child.className.indexOf('throbber') != -1) {
       
 38254 				child.parentNode.removeChild(child);
       
 38255 			}
       
 38256 
       
 38257 			state = false;
       
 38258 
       
 38259 			return self;
       
 38260 		};
       
 38261 	};
       
 38262 });
       
 38263 
       
 38264 expose(["tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/Env","tinymce/util/Tools","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/dom/Range","tinymce/html/Entities","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/dom/RangeUtils","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/dom/TridentSelection","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/BookmarkManager","tinymce/dom/Selection","tinymce/dom/ElementUtils","tinymce/Formatter","tinymce/UndoManager","tinymce/EnterKey","tinymce/ForceBlocks","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/DomUtils","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/util/Quirks","tinymce/util/Observable","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/LegacyInput","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/ComboBox","tinymce/ui/ColorBox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/util/Color","tinymce/ui/ColorPicker","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/ListBox","tinymce/ui/MenuItem","tinymce/ui/Menu","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox","tinymce/ui/Throbber"]);
       
 38265 })(this);