src/pyams_skin/resources/js/ext/tinymce/dev/tinymce.jquery.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.jQuery.js
       
   652 
       
   653 /**
       
   654  * Sizzle.jQuery.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 
       
   663 /*global jQuery:true */
       
   664 
       
   665 /*
       
   666  * Fake Sizzle using jQuery.
       
   667  */
       
   668 define("tinymce/dom/Sizzle", [], function() {
       
   669 	// Detect if jQuery is loaded
       
   670 	if (!window.jQuery) {
       
   671 		throw new Error("Load jQuery first");
       
   672 	}
       
   673 
       
   674 	return jQuery.find;
       
   675 });
       
   676 
       
   677 // Included from: js/tinymce/classes/Env.js
       
   678 
       
   679 /**
       
   680  * Env.js
       
   681  *
       
   682  * Copyright, Moxiecode Systems AB
       
   683  * Released under LGPL License.
       
   684  *
       
   685  * License: http://www.tinymce.com/license
       
   686  * Contributing: http://www.tinymce.com/contributing
       
   687  */
       
   688 
       
   689 /**
       
   690  * This class contains various environment constants like browser versions etc.
       
   691  * Normally you don't want to sniff specific browser versions but sometimes you have
       
   692  * to when it's impossible to feature detect. So use this with care.
       
   693  *
       
   694  * @class tinymce.Env
       
   695  * @static
       
   696  */
       
   697 define("tinymce/Env", [], function() {
       
   698 	var nav = navigator, userAgent = nav.userAgent;
       
   699 	var opera, webkit, ie, ie11, ie12, gecko, mac, iDevice, android;
       
   700 
       
   701 	opera = window.opera && window.opera.buildNumber;
       
   702 	android = /Android/.test(userAgent);
       
   703 	webkit = /WebKit/.test(userAgent);
       
   704 	ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
       
   705 	ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
       
   706 	ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
       
   707 	ie12 = (document.msElementsFromPoint && !ie && !ie11) ? 12 : false;
       
   708 	ie = ie || ie11 || ie12;
       
   709 	gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
       
   710 	mac = userAgent.indexOf('Mac') != -1;
       
   711 	iDevice = /(iPad|iPhone)/.test(userAgent);
       
   712 
       
   713 	if (ie12) {
       
   714 		webkit = false;
       
   715 	}
       
   716 
       
   717 	// Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
       
   718 	// says it has contentEditable support but there is no visible caret.
       
   719 	var contentEditable = !iDevice || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
       
   720 
       
   721 	return {
       
   722 		/**
       
   723 		 * Constant that is true if the browser is Opera.
       
   724 		 *
       
   725 		 * @property opera
       
   726 		 * @type Boolean
       
   727 		 * @final
       
   728 		 */
       
   729 		opera: opera,
       
   730 
       
   731 		/**
       
   732 		 * Constant that is true if the browser is WebKit (Safari/Chrome).
       
   733 		 *
       
   734 		 * @property webKit
       
   735 		 * @type Boolean
       
   736 		 * @final
       
   737 		 */
       
   738 		webkit: webkit,
       
   739 
       
   740 		/**
       
   741 		 * Constant that is more than zero if the browser is IE.
       
   742 		 *
       
   743 		 * @property ie
       
   744 		 * @type Boolean
       
   745 		 * @final
       
   746 		 */
       
   747 		ie: ie,
       
   748 
       
   749 		/**
       
   750 		 * Constant that is true if the browser is Gecko.
       
   751 		 *
       
   752 		 * @property gecko
       
   753 		 * @type Boolean
       
   754 		 * @final
       
   755 		 */
       
   756 		gecko: gecko,
       
   757 
       
   758 		/**
       
   759 		 * Constant that is true if the os is Mac OS.
       
   760 		 *
       
   761 		 * @property mac
       
   762 		 * @type Boolean
       
   763 		 * @final
       
   764 		 */
       
   765 		mac: mac,
       
   766 
       
   767 		/**
       
   768 		 * Constant that is true if the os is iOS.
       
   769 		 *
       
   770 		 * @property iOS
       
   771 		 * @type Boolean
       
   772 		 * @final
       
   773 		 */
       
   774 		iOS: iDevice,
       
   775 
       
   776 		/**
       
   777 		 * Constant that is true if the os is android.
       
   778 		 *
       
   779 		 * @property android
       
   780 		 * @type Boolean
       
   781 		 * @final
       
   782 		 */
       
   783 		android: android,
       
   784 
       
   785 		/**
       
   786 		 * Constant that is true if the browser supports editing.
       
   787 		 *
       
   788 		 * @property contentEditable
       
   789 		 * @type Boolean
       
   790 		 * @final
       
   791 		 */
       
   792 		contentEditable: contentEditable,
       
   793 
       
   794 		/**
       
   795 		 * Transparent image data url.
       
   796 		 *
       
   797 		 * @property transparentSrc
       
   798 		 * @type Boolean
       
   799 		 * @final
       
   800 		 */
       
   801 		transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
       
   802 
       
   803 		/**
       
   804 		 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
       
   805 		 *
       
   806 		 * @property noCaretAfter
       
   807 		 * @type Boolean
       
   808 		 * @final
       
   809 		 */
       
   810 		caretAfter: ie != 8,
       
   811 
       
   812 		/**
       
   813 		 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
       
   814 		 *
       
   815 		 * @property range
       
   816 		 * @type Boolean
       
   817 		 */
       
   818 		range: window.getSelection && "Range" in window,
       
   819 
       
   820 		/**
       
   821 		 * Returns the IE document mode for non IE browsers this will fake IE 10.
       
   822 		 *
       
   823 		 * @property documentMode
       
   824 		 * @type Number
       
   825 		 */
       
   826 		documentMode: ie && !ie12 ? (document.documentMode || 7) : 10
       
   827 	};
       
   828 });
       
   829 
       
   830 // Included from: js/tinymce/classes/util/Tools.js
       
   831 
       
   832 /**
       
   833  * Tools.js
       
   834  *
       
   835  * Copyright, Moxiecode Systems AB
       
   836  * Released under LGPL License.
       
   837  *
       
   838  * License: http://www.tinymce.com/license
       
   839  * Contributing: http://www.tinymce.com/contributing
       
   840  */
       
   841 
       
   842 /**
       
   843  * This class contains various utlity functions. These are also exposed
       
   844  * directly on the tinymce namespace.
       
   845  *
       
   846  * @class tinymce.util.Tools
       
   847  */
       
   848 define("tinymce/util/Tools", [
       
   849 	"tinymce/Env"
       
   850 ], function(Env) {
       
   851 	/**
       
   852 	 * Removes whitespace from the beginning and end of a string.
       
   853 	 *
       
   854 	 * @method trim
       
   855 	 * @param {String} s String to remove whitespace from.
       
   856 	 * @return {String} New string with removed whitespace.
       
   857 	 */
       
   858 	var whiteSpaceRegExp = /^\s*|\s*$/g;
       
   859 
       
   860 	function trim(str) {
       
   861 		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
       
   862 	}
       
   863 
       
   864 	/**
       
   865 	 * Returns true/false if the object is an array or not.
       
   866 	 *
       
   867 	 * @method isArray
       
   868 	 * @param {Object} obj Object to check.
       
   869 	 * @return {boolean} true/false state if the object is an array or not.
       
   870 	 */
       
   871 	var isArray = Array.isArray || function(obj) {
       
   872 		return Object.prototype.toString.call(obj) === "[object Array]";
       
   873 	};
       
   874 
       
   875 	/**
       
   876 	 * Checks if a object is of a specific type for example an array.
       
   877 	 *
       
   878 	 * @method is
       
   879 	 * @param {Object} obj Object to check type of.
       
   880 	 * @param {string} type Optional type to check for.
       
   881 	 * @return {Boolean} true/false if the object is of the specified type.
       
   882 	 */
       
   883 	function is(obj, type) {
       
   884 		if (!type) {
       
   885 			return obj !== undefined;
       
   886 		}
       
   887 
       
   888 		if (type == 'array' && isArray(obj)) {
       
   889 			return true;
       
   890 		}
       
   891 
       
   892 		return typeof obj == type;
       
   893 	}
       
   894 
       
   895 	/**
       
   896 	 * Converts the specified object into a real JavaScript array.
       
   897 	 *
       
   898 	 * @method toArray
       
   899 	 * @param {Object} obj Object to convert into array.
       
   900 	 * @return {Array} Array object based in input.
       
   901 	 */
       
   902 	function toArray(obj) {
       
   903 		var array = obj, i, l;
       
   904 
       
   905 		if (!isArray(obj)) {
       
   906 			array = [];
       
   907 			for (i = 0, l = obj.length; i < l; i++) {
       
   908 				array[i] = obj[i];
       
   909 			}
       
   910 		}
       
   911 
       
   912 		return array;
       
   913 	}
       
   914 
       
   915 	/**
       
   916 	 * Makes a name/object map out of an array with names.
       
   917 	 *
       
   918 	 * @method makeMap
       
   919 	 * @param {Array/String} items Items to make map out of.
       
   920 	 * @param {String} delim Optional delimiter to split string by.
       
   921 	 * @param {Object} map Optional map to add items to.
       
   922 	 * @return {Object} Name/value map of items.
       
   923 	 */
       
   924 	function makeMap(items, delim, map) {
       
   925 		var i;
       
   926 
       
   927 		items = items || [];
       
   928 		delim = delim || ',';
       
   929 
       
   930 		if (typeof items == "string") {
       
   931 			items = items.split(delim);
       
   932 		}
       
   933 
       
   934 		map = map || {};
       
   935 
       
   936 		i = items.length;
       
   937 		while (i--) {
       
   938 			map[items[i]] = {};
       
   939 		}
       
   940 
       
   941 		return map;
       
   942 	}
       
   943 
       
   944 	/**
       
   945 	 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
       
   946 	 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
       
   947 	 * The callback has the following format: cb(value, key_or_index).
       
   948 	 *
       
   949 	 * @method each
       
   950 	 * @param {Object} o Collection to iterate.
       
   951 	 * @param {function} cb Callback function to execute for each item.
       
   952 	 * @param {Object} s Optional scope to execute the callback in.
       
   953 	 * @example
       
   954 	 * // Iterate an array
       
   955 	 * tinymce.each([1,2,3], function(v, i) {
       
   956 	 *     console.debug("Value: " + v + ", Index: " + i);
       
   957 	 * });
       
   958 	 *
       
   959 	 * // Iterate an object
       
   960 	 * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
       
   961 	 *     console.debug("Value: " + v + ", Key: " + k);
       
   962 	 * });
       
   963 	 */
       
   964 	function each(o, cb, s) {
       
   965 		var n, l;
       
   966 
       
   967 		if (!o) {
       
   968 			return 0;
       
   969 		}
       
   970 
       
   971 		s = s || o;
       
   972 
       
   973 		if (o.length !== undefined) {
       
   974 			// Indexed arrays, needed for Safari
       
   975 			for (n = 0, l = o.length; n < l; n++) {
       
   976 				if (cb.call(s, o[n], n, o) === false) {
       
   977 					return 0;
       
   978 				}
       
   979 			}
       
   980 		} else {
       
   981 			// Hashtables
       
   982 			for (n in o) {
       
   983 				if (o.hasOwnProperty(n)) {
       
   984 					if (cb.call(s, o[n], n, o) === false) {
       
   985 						return 0;
       
   986 					}
       
   987 				}
       
   988 			}
       
   989 		}
       
   990 
       
   991 		return 1;
       
   992 	}
       
   993 
       
   994 	/**
       
   995 	 * Creates a new array by the return value of each iteration function call. This enables you to convert
       
   996 	 * one array list into another.
       
   997 	 *
       
   998 	 * @method map
       
   999 	 * @param {Array} array Array of items to iterate.
       
  1000 	 * @param {function} callback Function to call for each item. It's return value will be the new value.
       
  1001 	 * @return {Array} Array with new values based on function return values.
       
  1002 	 */
       
  1003 	function map(array, callback) {
       
  1004 		var out = [];
       
  1005 
       
  1006 		each(array, function(item) {
       
  1007 			out.push(callback(item));
       
  1008 		});
       
  1009 
       
  1010 		return out;
       
  1011 	}
       
  1012 
       
  1013 	/**
       
  1014 	 * Filters out items from the input array by calling the specified function for each item.
       
  1015 	 * If the function returns false the item will be excluded if it returns true it will be included.
       
  1016 	 *
       
  1017 	 * @method grep
       
  1018 	 * @param {Array} a Array of items to loop though.
       
  1019 	 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
       
  1020 	 * @return {Array} New array with values imported and filtered based in input.
       
  1021 	 * @example
       
  1022 	 * // Filter out some items, this will return an array with 4 and 5
       
  1023 	 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
       
  1024 	 */
       
  1025 	function grep(a, f) {
       
  1026 		var o = [];
       
  1027 
       
  1028 		each(a, function(v) {
       
  1029 			if (!f || f(v)) {
       
  1030 				o.push(v);
       
  1031 			}
       
  1032 		});
       
  1033 
       
  1034 		return o;
       
  1035 	}
       
  1036 
       
  1037 	/**
       
  1038 	 * Creates a class, subclass or static singleton.
       
  1039 	 * More details on this method can be found in the Wiki.
       
  1040 	 *
       
  1041 	 * @method create
       
  1042 	 * @param {String} s Class name, inheritage and prefix.
       
  1043 	 * @param {Object} p Collection of methods to add to the class.
       
  1044 	 * @param {Object} root Optional root object defaults to the global window object.
       
  1045 	 * @example
       
  1046 	 * // Creates a basic class
       
  1047 	 * tinymce.create('tinymce.somepackage.SomeClass', {
       
  1048 	 *     SomeClass: function() {
       
  1049 	 *         // Class constructor
       
  1050 	 *     },
       
  1051 	 *
       
  1052 	 *     method: function() {
       
  1053 	 *         // Some method
       
  1054 	 *     }
       
  1055 	 * });
       
  1056 	 *
       
  1057 	 * // Creates a basic subclass class
       
  1058 	 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
       
  1059 	 *     SomeSubClass: function() {
       
  1060 	 *         // Class constructor
       
  1061 	 *         this.parent(); // Call parent constructor
       
  1062 	 *     },
       
  1063 	 *
       
  1064 	 *     method: function() {
       
  1065 	 *         // Some method
       
  1066 	 *         this.parent(); // Call parent method
       
  1067 	 *     },
       
  1068 	 *
       
  1069 	 *     'static': {
       
  1070 	 *         staticMethod: function() {
       
  1071 	 *             // Static method
       
  1072 	 *         }
       
  1073 	 *     }
       
  1074 	 * });
       
  1075 	 *
       
  1076 	 * // Creates a singleton/static class
       
  1077 	 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
       
  1078 	 *     method: function() {
       
  1079 	 *         // Some method
       
  1080 	 *     }
       
  1081 	 * });
       
  1082 	 */
       
  1083 	function create(s, p, root) {
       
  1084 		var self = this, sp, ns, cn, scn, c, de = 0;
       
  1085 
       
  1086 		// Parse : <prefix> <class>:<super class>
       
  1087 		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
       
  1088 		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
       
  1089 
       
  1090 		// Create namespace for new class
       
  1091 		ns = self.createNS(s[3].replace(/\.\w+$/, ''), root);
       
  1092 
       
  1093 		// Class already exists
       
  1094 		if (ns[cn]) {
       
  1095 			return;
       
  1096 		}
       
  1097 
       
  1098 		// Make pure static class
       
  1099 		if (s[2] == 'static') {
       
  1100 			ns[cn] = p;
       
  1101 
       
  1102 			if (this.onCreate) {
       
  1103 				this.onCreate(s[2], s[3], ns[cn]);
       
  1104 			}
       
  1105 
       
  1106 			return;
       
  1107 		}
       
  1108 
       
  1109 		// Create default constructor
       
  1110 		if (!p[cn]) {
       
  1111 			p[cn] = function() {};
       
  1112 			de = 1;
       
  1113 		}
       
  1114 
       
  1115 		// Add constructor and methods
       
  1116 		ns[cn] = p[cn];
       
  1117 		self.extend(ns[cn].prototype, p);
       
  1118 
       
  1119 		// Extend
       
  1120 		if (s[5]) {
       
  1121 			sp = self.resolve(s[5]).prototype;
       
  1122 			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
       
  1123 
       
  1124 			// Extend constructor
       
  1125 			c = ns[cn];
       
  1126 			if (de) {
       
  1127 				// Add passthrough constructor
       
  1128 				ns[cn] = function() {
       
  1129 					return sp[scn].apply(this, arguments);
       
  1130 				};
       
  1131 			} else {
       
  1132 				// Add inherit constructor
       
  1133 				ns[cn] = function() {
       
  1134 					this.parent = sp[scn];
       
  1135 					return c.apply(this, arguments);
       
  1136 				};
       
  1137 			}
       
  1138 			ns[cn].prototype[cn] = ns[cn];
       
  1139 
       
  1140 			// Add super methods
       
  1141 			self.each(sp, function(f, n) {
       
  1142 				ns[cn].prototype[n] = sp[n];
       
  1143 			});
       
  1144 
       
  1145 			// Add overridden methods
       
  1146 			self.each(p, function(f, n) {
       
  1147 				// Extend methods if needed
       
  1148 				if (sp[n]) {
       
  1149 					ns[cn].prototype[n] = function() {
       
  1150 						this.parent = sp[n];
       
  1151 						return f.apply(this, arguments);
       
  1152 					};
       
  1153 				} else {
       
  1154 					if (n != cn) {
       
  1155 						ns[cn].prototype[n] = f;
       
  1156 					}
       
  1157 				}
       
  1158 			});
       
  1159 		}
       
  1160 
       
  1161 		// Add static methods
       
  1162 		/*jshint sub:true*/
       
  1163 		/*eslint dot-notation:0*/
       
  1164 		self.each(p['static'], function(f, n) {
       
  1165 			ns[cn][n] = f;
       
  1166 		});
       
  1167 	}
       
  1168 
       
  1169 	/**
       
  1170 	 * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
       
  1171 	 *
       
  1172 	 * @method inArray
       
  1173 	 * @param {Array} a Array/Object to search for value in.
       
  1174 	 * @param {Object} v Value to check for inside the array.
       
  1175 	 * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
       
  1176 	 * @example
       
  1177 	 * // Get index of value in array this will alert 1 since 2 is at that index
       
  1178 	 * alert(tinymce.inArray([1,2,3], 2));
       
  1179 	 */
       
  1180 	function inArray(a, v) {
       
  1181 		var i, l;
       
  1182 
       
  1183 		if (a) {
       
  1184 			for (i = 0, l = a.length; i < l; i++) {
       
  1185 				if (a[i] === v) {
       
  1186 					return i;
       
  1187 				}
       
  1188 			}
       
  1189 		}
       
  1190 
       
  1191 		return -1;
       
  1192 	}
       
  1193 
       
  1194 	function extend(obj, ext) {
       
  1195 		var i, l, name, args = arguments, value;
       
  1196 
       
  1197 		for (i = 1, l = args.length; i < l; i++) {
       
  1198 			ext = args[i];
       
  1199 			for (name in ext) {
       
  1200 				if (ext.hasOwnProperty(name)) {
       
  1201 					value = ext[name];
       
  1202 
       
  1203 					if (value !== undefined) {
       
  1204 						obj[name] = value;
       
  1205 					}
       
  1206 				}
       
  1207 			}
       
  1208 		}
       
  1209 
       
  1210 		return obj;
       
  1211 	}
       
  1212 
       
  1213 	/**
       
  1214 	 * Executed the specified function for each item in a object tree.
       
  1215 	 *
       
  1216 	 * @method walk
       
  1217 	 * @param {Object} o Object tree to walk though.
       
  1218 	 * @param {function} f Function to call for each item.
       
  1219 	 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
       
  1220 	 * @param {String} s Optional scope to execute the function in.
       
  1221 	 */
       
  1222 	function walk(o, f, n, s) {
       
  1223 		s = s || this;
       
  1224 
       
  1225 		if (o) {
       
  1226 			if (n) {
       
  1227 				o = o[n];
       
  1228 			}
       
  1229 
       
  1230 			each(o, function(o, i) {
       
  1231 				if (f.call(s, o, i, n) === false) {
       
  1232 					return false;
       
  1233 				}
       
  1234 
       
  1235 				walk(o, f, n, s);
       
  1236 			});
       
  1237 		}
       
  1238 	}
       
  1239 
       
  1240 	/**
       
  1241 	 * Creates a namespace on a specific object.
       
  1242 	 *
       
  1243 	 * @method createNS
       
  1244 	 * @param {String} n Namespace to create for example a.b.c.d.
       
  1245 	 * @param {Object} o Optional object to add namespace to, defaults to window.
       
  1246 	 * @return {Object} New namespace object the last item in path.
       
  1247 	 * @example
       
  1248 	 * // Create some namespace
       
  1249 	 * tinymce.createNS('tinymce.somepackage.subpackage');
       
  1250 	 *
       
  1251 	 * // Add a singleton
       
  1252 	 * var tinymce.somepackage.subpackage.SomeSingleton = {
       
  1253 	 *     method: function() {
       
  1254 	 *         // Some method
       
  1255 	 *     }
       
  1256 	 * };
       
  1257 	 */
       
  1258 	function createNS(n, o) {
       
  1259 		var i, v;
       
  1260 
       
  1261 		o = o || window;
       
  1262 
       
  1263 		n = n.split('.');
       
  1264 		for (i = 0; i < n.length; i++) {
       
  1265 			v = n[i];
       
  1266 
       
  1267 			if (!o[v]) {
       
  1268 				o[v] = {};
       
  1269 			}
       
  1270 
       
  1271 			o = o[v];
       
  1272 		}
       
  1273 
       
  1274 		return o;
       
  1275 	}
       
  1276 
       
  1277 	/**
       
  1278 	 * Resolves a string and returns the object from a specific structure.
       
  1279 	 *
       
  1280 	 * @method resolve
       
  1281 	 * @param {String} n Path to resolve for example a.b.c.d.
       
  1282 	 * @param {Object} o Optional object to search though, defaults to window.
       
  1283 	 * @return {Object} Last object in path or null if it couldn't be resolved.
       
  1284 	 * @example
       
  1285 	 * // Resolve a path into an object reference
       
  1286 	 * var obj = tinymce.resolve('a.b.c.d');
       
  1287 	 */
       
  1288 	function resolve(n, o) {
       
  1289 		var i, l;
       
  1290 
       
  1291 		o = o || window;
       
  1292 
       
  1293 		n = n.split('.');
       
  1294 		for (i = 0, l = n.length; i < l; i++) {
       
  1295 			o = o[n[i]];
       
  1296 
       
  1297 			if (!o) {
       
  1298 				break;
       
  1299 			}
       
  1300 		}
       
  1301 
       
  1302 		return o;
       
  1303 	}
       
  1304 
       
  1305 	/**
       
  1306 	 * Splits a string but removes the whitespace before and after each value.
       
  1307 	 *
       
  1308 	 * @method explode
       
  1309 	 * @param {string} s String to split.
       
  1310 	 * @param {string} d Delimiter to split by.
       
  1311 	 * @example
       
  1312 	 * // Split a string into an array with a,b,c
       
  1313 	 * var arr = tinymce.explode('a, b,   c');
       
  1314 	 */
       
  1315 	function explode(s, d) {
       
  1316 		if (!s || is(s, 'array')) {
       
  1317 			return s;
       
  1318 		}
       
  1319 
       
  1320 		return map(s.split(d || ','), trim);
       
  1321 	}
       
  1322 
       
  1323 	function _addCacheSuffix(url) {
       
  1324 		var cacheSuffix = Env.cacheSuffix;
       
  1325 
       
  1326 		if (cacheSuffix) {
       
  1327 			url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix;
       
  1328 		}
       
  1329 
       
  1330 		return url;
       
  1331 	}
       
  1332 
       
  1333 	return {
       
  1334 		trim: trim,
       
  1335 		isArray: isArray,
       
  1336 		is: is,
       
  1337 		toArray: toArray,
       
  1338 		makeMap: makeMap,
       
  1339 		each: each,
       
  1340 		map: map,
       
  1341 		grep: grep,
       
  1342 		inArray: inArray,
       
  1343 		extend: extend,
       
  1344 		create: create,
       
  1345 		walk: walk,
       
  1346 		createNS: createNS,
       
  1347 		resolve: resolve,
       
  1348 		explode: explode,
       
  1349 		_addCacheSuffix: _addCacheSuffix
       
  1350 	};
       
  1351 });
       
  1352 
       
  1353 // Included from: js/tinymce/classes/dom/DomQuery.js
       
  1354 
       
  1355 /**
       
  1356  * DomQuery.js
       
  1357  *
       
  1358  * Copyright, Moxiecode Systems AB
       
  1359  * Released under LGPL License.
       
  1360  *
       
  1361  * License: http://www.tinymce.com/license
       
  1362  * Contributing: http://www.tinymce.com/contributing
       
  1363  */
       
  1364 
       
  1365 /**
       
  1366  * This class mimics most of the jQuery API:
       
  1367  *
       
  1368  * This is whats currently implemented:
       
  1369  * - Utility functions
       
  1370  * - DOM traversial
       
  1371  * - DOM manipulation
       
  1372  * - Event binding
       
  1373  *
       
  1374  * This is not currently implemented:
       
  1375  * - Dimension
       
  1376  * - Ajax
       
  1377  * - Animation
       
  1378  * - Advanced chaining
       
  1379  *
       
  1380  * @example
       
  1381  * var $ = tinymce.dom.DomQuery;
       
  1382  * $('p').attr('attr', 'value').addClass('class');
       
  1383  *
       
  1384  * @class tinymce.dom.DomQuery
       
  1385  */
       
  1386 define("tinymce/dom/DomQuery", [
       
  1387 	"tinymce/dom/EventUtils",
       
  1388 	"tinymce/dom/Sizzle",
       
  1389 	"tinymce/util/Tools",
       
  1390 	"tinymce/Env"
       
  1391 ], function(EventUtils, Sizzle, Tools, Env) {
       
  1392 	var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
       
  1393 	var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
       
  1394 	var Event = EventUtils.Event, undef;
       
  1395 
       
  1396 	function isDefined(obj) {
       
  1397 		return typeof obj !== 'undefined';
       
  1398 	}
       
  1399 
       
  1400 	function isString(obj) {
       
  1401 		return typeof obj === 'string';
       
  1402 	}
       
  1403 
       
  1404 	function isWindow(obj) {
       
  1405 		return obj && obj == obj.window;
       
  1406 	}
       
  1407 
       
  1408 	function createFragment(html, fragDoc) {
       
  1409 		var frag, node, container;
       
  1410 
       
  1411 		fragDoc = fragDoc || doc;
       
  1412 		container = fragDoc.createElement('div');
       
  1413 		frag = fragDoc.createDocumentFragment();
       
  1414 		container.innerHTML = html;
       
  1415 
       
  1416 		while ((node = container.firstChild)) {
       
  1417 			frag.appendChild(node);
       
  1418 		}
       
  1419 
       
  1420 		return frag;
       
  1421 	}
       
  1422 
       
  1423 	function domManipulate(targetNodes, sourceItem, callback, reverse) {
       
  1424 		var i;
       
  1425 
       
  1426 		if (isString(sourceItem)) {
       
  1427 			sourceItem = createFragment(sourceItem, getElementDocument(targetNodes[0]));
       
  1428 		} else if (sourceItem.length && !sourceItem.nodeType) {
       
  1429 			sourceItem = DomQuery.makeArray(sourceItem);
       
  1430 
       
  1431 			if (reverse) {
       
  1432 				for (i = sourceItem.length - 1; i >= 0; i--) {
       
  1433 					domManipulate(targetNodes, sourceItem[i], callback, reverse);
       
  1434 				}
       
  1435 			} else {
       
  1436 				for (i = 0; i < sourceItem.length; i++) {
       
  1437 					domManipulate(targetNodes, sourceItem[i], callback, reverse);
       
  1438 				}
       
  1439 			}
       
  1440 
       
  1441 			return targetNodes;
       
  1442 		}
       
  1443 
       
  1444 		if (sourceItem.nodeType) {
       
  1445 			i = targetNodes.length;
       
  1446 			while (i--) {
       
  1447 				callback.call(targetNodes[i], sourceItem);
       
  1448 			}
       
  1449 		}
       
  1450 
       
  1451 		return targetNodes;
       
  1452 	}
       
  1453 
       
  1454 	function hasClass(node, className) {
       
  1455 		return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
       
  1456 	}
       
  1457 
       
  1458 	function wrap(elements, wrapper, all) {
       
  1459 		var lastParent, newWrapper;
       
  1460 
       
  1461 		wrapper = DomQuery(wrapper)[0];
       
  1462 
       
  1463 		elements.each(function() {
       
  1464 			var self = this;
       
  1465 
       
  1466 			if (!all || lastParent != self.parentNode) {
       
  1467 				lastParent = self.parentNode;
       
  1468 				newWrapper = wrapper.cloneNode(false);
       
  1469 				self.parentNode.insertBefore(newWrapper, self);
       
  1470 				newWrapper.appendChild(self);
       
  1471 			} else {
       
  1472 				newWrapper.appendChild(self);
       
  1473 			}
       
  1474 		});
       
  1475 
       
  1476 		return elements;
       
  1477 	}
       
  1478 
       
  1479 	var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
       
  1480 	var booleanMap = Tools.makeMap('checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected', ' ');
       
  1481 	var propFix = {
       
  1482 		'for': 'htmlFor',
       
  1483 		'class': 'className',
       
  1484 		'readonly': 'readOnly'
       
  1485 	};
       
  1486 	var cssFix = {
       
  1487 		'float': 'cssFloat'
       
  1488 	};
       
  1489 
       
  1490 	var attrHooks = {}, cssHooks = {};
       
  1491 
       
  1492 	function DomQuery(selector, context) {
       
  1493 		/*eslint new-cap:0 */
       
  1494 		return new DomQuery.fn.init(selector, context);
       
  1495 	}
       
  1496 
       
  1497 	function inArray(item, array) {
       
  1498 		var i;
       
  1499 
       
  1500 		if (array.indexOf) {
       
  1501 			return array.indexOf(item);
       
  1502 		}
       
  1503 
       
  1504 		i = array.length;
       
  1505 		while (i--) {
       
  1506 			if (array[i] === item) {
       
  1507 				return i;
       
  1508 			}
       
  1509 		}
       
  1510 
       
  1511 		return -1;
       
  1512 	}
       
  1513 
       
  1514 	var whiteSpaceRegExp = /^\s*|\s*$/g;
       
  1515 
       
  1516 	function trim(str) {
       
  1517 		return (str === null || str === undef) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
       
  1518 	}
       
  1519 
       
  1520 	function each(obj, callback) {
       
  1521 		var length, key, i, undef, value;
       
  1522 
       
  1523 		if (obj) {
       
  1524 			length = obj.length;
       
  1525 
       
  1526 			if (length === undef) {
       
  1527 				// Loop object items
       
  1528 				for (key in obj) {
       
  1529 					if (obj.hasOwnProperty(key)) {
       
  1530 						value = obj[key];
       
  1531 						if (callback.call(value, key, value) === false) {
       
  1532 							break;
       
  1533 						}
       
  1534 					}
       
  1535 				}
       
  1536 			} else {
       
  1537 				// Loop array items
       
  1538 				for (i = 0; i < length; i++) {
       
  1539 					value = obj[i];
       
  1540 					if (callback.call(value, i, value) === false) {
       
  1541 						break;
       
  1542 					}
       
  1543 				}
       
  1544 			}
       
  1545 		}
       
  1546 
       
  1547 		return obj;
       
  1548 	}
       
  1549 
       
  1550 	function grep(array, callback) {
       
  1551 		var out = [];
       
  1552 
       
  1553 		each(array, function(i, item) {
       
  1554 			if (callback(item, i)) {
       
  1555 				out.push(item);
       
  1556 			}
       
  1557 		});
       
  1558 
       
  1559 		return out;
       
  1560 	}
       
  1561 
       
  1562 	function getElementDocument(element) {
       
  1563 		if (!element) {
       
  1564 			return doc;
       
  1565 		}
       
  1566 
       
  1567 		if (element.nodeType == 9) {
       
  1568 			return element;
       
  1569 		}
       
  1570 
       
  1571 		return element.ownerDocument;
       
  1572 	}
       
  1573 
       
  1574 	DomQuery.fn = DomQuery.prototype = {
       
  1575 		constructor: DomQuery,
       
  1576 
       
  1577 		/**
       
  1578 		 * Selector for the current set.
       
  1579 		 *
       
  1580 		 * @property selector
       
  1581 		 * @type String
       
  1582 		 */
       
  1583 		selector: "",
       
  1584 
       
  1585 		/**
       
  1586 		 * Context used to create the set.
       
  1587 		 *
       
  1588 		 * @property context
       
  1589 		 * @type Element
       
  1590 		 */
       
  1591 		context: null,
       
  1592 
       
  1593 		/**
       
  1594 		 * Number of items in the current set.
       
  1595 		 *
       
  1596 		 * @property length
       
  1597 		 * @type Number
       
  1598 		 */
       
  1599 		length: 0,
       
  1600 
       
  1601 		/**
       
  1602 		 * Constructs a new DomQuery instance with the specified selector or context.
       
  1603 		 *
       
  1604 		 * @constructor
       
  1605 		 * @method init
       
  1606 		 * @param {String/Array/DomQuery} selector Optional CSS selector/Array or array like object or HTML string.
       
  1607 		 * @param {Document/Element} context Optional context to search in.
       
  1608 		 */
       
  1609 		init: function(selector, context) {
       
  1610 			var self = this, match, node;
       
  1611 
       
  1612 			if (!selector) {
       
  1613 				return self;
       
  1614 			}
       
  1615 
       
  1616 			if (selector.nodeType) {
       
  1617 				self.context = self[0] = selector;
       
  1618 				self.length = 1;
       
  1619 
       
  1620 				return self;
       
  1621 			}
       
  1622 
       
  1623 			if (context && context.nodeType) {
       
  1624 				self.context = context;
       
  1625 			} else {
       
  1626 				if (context) {
       
  1627 					return DomQuery(selector).attr(context);
       
  1628 				} else {
       
  1629 					self.context = context = document;
       
  1630 				}
       
  1631 			}
       
  1632 
       
  1633 			if (isString(selector)) {
       
  1634 				self.selector = selector;
       
  1635 
       
  1636 				if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
       
  1637 					match = [null, selector, null];
       
  1638 				} else {
       
  1639 					match = rquickExpr.exec(selector);
       
  1640 				}
       
  1641 
       
  1642 				if (match) {
       
  1643 					if (match[1]) {
       
  1644 						node = createFragment(selector, getElementDocument(context)).firstChild;
       
  1645 
       
  1646 						while (node) {
       
  1647 							push.call(self, node);
       
  1648 							node = node.nextSibling;
       
  1649 						}
       
  1650 					} else {
       
  1651 						node = getElementDocument(context).getElementById(match[2]);
       
  1652 
       
  1653 						if (!node) {
       
  1654 							return self;
       
  1655 						}
       
  1656 
       
  1657 						if (node.id !== match[2]) {
       
  1658 							return self.find(selector);
       
  1659 						}
       
  1660 
       
  1661 						self.length = 1;
       
  1662 						self[0] = node;
       
  1663 					}
       
  1664 				} else {
       
  1665 					return DomQuery(context).find(selector);
       
  1666 				}
       
  1667 			} else {
       
  1668 				this.add(selector, false);
       
  1669 			}
       
  1670 
       
  1671 			return self;
       
  1672 		},
       
  1673 
       
  1674 		/**
       
  1675 		 * Converts the current set to an array.
       
  1676 		 *
       
  1677 		 * @method toArray
       
  1678 		 * @param {Array} Array of all nodes in set.
       
  1679 		 */
       
  1680 		toArray: function() {
       
  1681 			return Tools.toArray(this);
       
  1682 		},
       
  1683 
       
  1684 		/**
       
  1685 		 * Adds new nodes to the set.
       
  1686 		 *
       
  1687 		 * @method add
       
  1688 		 * @param {Array/tinymce.dom.DomQuery} items Array of all nodes to add to set.
       
  1689 		 * @return {tinymce.dom.DomQuery} New instance with nodes added.
       
  1690 		 */
       
  1691 		add: function(items, sort) {
       
  1692 			var self = this, nodes, i;
       
  1693 
       
  1694 			if (isString(items)) {
       
  1695 				return self.add(DomQuery(items));
       
  1696 			}
       
  1697 
       
  1698 			if (sort !== false) {
       
  1699 				nodes = DomQuery.unique(self.toArray().concat(DomQuery.makeArray(items)));
       
  1700 				self.length = nodes.length;
       
  1701 				for (i = 0; i < nodes.length; i++) {
       
  1702 					self[i] = nodes[i];
       
  1703 				}
       
  1704 			} else {
       
  1705 				push.apply(self, DomQuery.makeArray(items));
       
  1706 			}
       
  1707 
       
  1708 			return self;
       
  1709 		},
       
  1710 
       
  1711 		/**
       
  1712 		 * Sets/gets attributes on the elements in the current set.
       
  1713 		 *
       
  1714 		 * @method attr
       
  1715 		 * @param {String/Object} name Name of attribute to get or an object with attributes to set.
       
  1716 		 * @param {String} value Optional value to set.
       
  1717 		 * @return {tinymce.dom.DomQuery/String} Current set or the specified attribute when only the name is specified.
       
  1718 		 */
       
  1719 		attr: function(name, value) {
       
  1720 			var self = this, hook;
       
  1721 
       
  1722 			if (typeof name === "object") {
       
  1723 				each(name, function(name, value) {
       
  1724 					self.attr(name, value);
       
  1725 				});
       
  1726 			} else if (isDefined(value)) {
       
  1727 				this.each(function() {
       
  1728 					var hook;
       
  1729 
       
  1730 					if (this.nodeType === 1) {
       
  1731 						hook = attrHooks[name];
       
  1732 						if (hook && hook.set) {
       
  1733 							hook.set(this, value);
       
  1734 							return;
       
  1735 						}
       
  1736 
       
  1737 						if (value === null) {
       
  1738 							this.removeAttribute(name, 2);
       
  1739 						} else {
       
  1740 							this.setAttribute(name, value, 2);
       
  1741 						}
       
  1742 					}
       
  1743 				});
       
  1744 			} else {
       
  1745 				if (self[0] && self[0].nodeType === 1) {
       
  1746 					hook = attrHooks[name];
       
  1747 					if (hook && hook.get) {
       
  1748 						return hook.get(self[0], name);
       
  1749 					}
       
  1750 
       
  1751 					if (booleanMap[name]) {
       
  1752 						return self.prop(name) ? name : undef;
       
  1753 					}
       
  1754 
       
  1755 					value = self[0].getAttribute(name, 2);
       
  1756 
       
  1757 					if (value === null) {
       
  1758 						value = undef;
       
  1759 					}
       
  1760 				}
       
  1761 
       
  1762 				return value;
       
  1763 			}
       
  1764 
       
  1765 			return self;
       
  1766 		},
       
  1767 
       
  1768 		/**
       
  1769 		 * Removes attributse on the elements in the current set.
       
  1770 		 *
       
  1771 		 * @method removeAttr
       
  1772 		 * @param {String/Object} name Name of attribute to remove.
       
  1773 		 * @return {tinymce.dom.DomQuery/String} Current set.
       
  1774 		 */
       
  1775 		removeAttr: function(name) {
       
  1776 			return this.attr(name, null);
       
  1777 		},
       
  1778 
       
  1779 		/**
       
  1780 		 * Sets/gets properties on the elements in the current set.
       
  1781 		 *
       
  1782 		 * @method attr
       
  1783 		 * @param {String/Object} name Name of property to get or an object with properties to set.
       
  1784 		 * @param {String} value Optional value to set.
       
  1785 		 * @return {tinymce.dom.DomQuery/String} Current set or the specified property when only the name is specified.
       
  1786 		 */
       
  1787 		prop: function(name, value) {
       
  1788 			var self = this;
       
  1789 
       
  1790 			name = propFix[name] || name;
       
  1791 
       
  1792 			if (typeof name === "object") {
       
  1793 				each(name, function(name, value) {
       
  1794 					self.prop(name, value);
       
  1795 				});
       
  1796 			} else if (isDefined(value)) {
       
  1797 				this.each(function() {
       
  1798 					if (this.nodeType == 1) {
       
  1799 						this[name] = value;
       
  1800 					}
       
  1801 				});
       
  1802 			} else {
       
  1803 				if (self[0] && self[0].nodeType && name in self[0]) {
       
  1804 					return self[0][name];
       
  1805 				}
       
  1806 
       
  1807 				return value;
       
  1808 			}
       
  1809 
       
  1810 			return self;
       
  1811 		},
       
  1812 
       
  1813 		/**
       
  1814 		 * Sets/gets styles on the elements in the current set.
       
  1815 		 *
       
  1816 		 * @method css
       
  1817 		 * @param {String/Object} name Name of style to get or an object with styles to set.
       
  1818 		 * @param {String} value Optional value to set.
       
  1819 		 * @return {tinymce.dom.DomQuery/String} Current set or the specified style when only the name is specified.
       
  1820 		 */
       
  1821 		css: function(name, value) {
       
  1822 			var self = this, elm, hook;
       
  1823 
       
  1824 			function camel(name) {
       
  1825 				return name.replace(/-(\D)/g, function(a, b) {
       
  1826 					return b.toUpperCase();
       
  1827 				});
       
  1828 			}
       
  1829 
       
  1830 			function dashed(name) {
       
  1831 				return name.replace(/[A-Z]/g, function(a) {
       
  1832 					return '-' + a;
       
  1833 				});
       
  1834 			}
       
  1835 
       
  1836 			if (typeof name === "object") {
       
  1837 				each(name, function(name, value) {
       
  1838 					self.css(name, value);
       
  1839 				});
       
  1840 			} else {
       
  1841 				if (isDefined(value)) {
       
  1842 					name = camel(name);
       
  1843 
       
  1844 					// Default px suffix on these
       
  1845 					if (typeof value === 'number' && !numericCssMap[name]) {
       
  1846 						value += 'px';
       
  1847 					}
       
  1848 
       
  1849 					self.each(function() {
       
  1850 						var style = this.style;
       
  1851 
       
  1852 						hook = cssHooks[name];
       
  1853 						if (hook && hook.set) {
       
  1854 							hook.set(this, value);
       
  1855 							return;
       
  1856 						}
       
  1857 
       
  1858 						try {
       
  1859 							this.style[cssFix[name] || name] = value;
       
  1860 						} catch (ex) {
       
  1861 							// Ignore
       
  1862 						}
       
  1863 
       
  1864 						if (value === null || value === '') {
       
  1865 							if (style.removeProperty) {
       
  1866 								style.removeProperty(dashed(name));
       
  1867 							} else {
       
  1868 								style.removeAttribute(name);
       
  1869 							}
       
  1870 						}
       
  1871 					});
       
  1872 				} else {
       
  1873 					elm = self[0];
       
  1874 
       
  1875 					hook = cssHooks[name];
       
  1876 					if (hook && hook.get) {
       
  1877 						return hook.get(elm);
       
  1878 					}
       
  1879 
       
  1880 					if (elm.ownerDocument.defaultView) {
       
  1881 						try {
       
  1882 							return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(dashed(name));
       
  1883 						} catch (ex) {
       
  1884 							return undef;
       
  1885 						}
       
  1886 					} else if (elm.currentStyle) {
       
  1887 						return elm.currentStyle[camel(name)];
       
  1888 					}
       
  1889 				}
       
  1890 			}
       
  1891 
       
  1892 			return self;
       
  1893 		},
       
  1894 
       
  1895 		/**
       
  1896 		 * Removes all nodes in set from the document.
       
  1897 		 *
       
  1898 		 * @method remove
       
  1899 		 * @return {tinymce.dom.DomQuery} Current set with the removed nodes.
       
  1900 		 */
       
  1901 		remove: function() {
       
  1902 			var self = this, node, i = this.length;
       
  1903 
       
  1904 			while (i--) {
       
  1905 				node = self[i];
       
  1906 				Event.clean(node);
       
  1907 
       
  1908 				if (node.parentNode) {
       
  1909 					node.parentNode.removeChild(node);
       
  1910 				}
       
  1911 			}
       
  1912 
       
  1913 			return this;
       
  1914 		},
       
  1915 
       
  1916 		/**
       
  1917 		 * Empties all elements in set.
       
  1918 		 *
       
  1919 		 * @method empty
       
  1920 		 * @return {tinymce.dom.DomQuery} Current set with the empty nodes.
       
  1921 		 */
       
  1922 		empty: function() {
       
  1923 			var self = this, node, i = this.length;
       
  1924 
       
  1925 			while (i--) {
       
  1926 				node = self[i];
       
  1927 				while (node.firstChild) {
       
  1928 					node.removeChild(node.firstChild);
       
  1929 				}
       
  1930 			}
       
  1931 
       
  1932 			return this;
       
  1933 		},
       
  1934 
       
  1935 		/**
       
  1936 		 * Sets or gets the HTML of the current set or first set node.
       
  1937 		 *
       
  1938 		 * @method html
       
  1939 		 * @param {String} value Optional innerHTML value to set on each element.
       
  1940 		 * @return {tinymce.dom.DomQuery/String} Current set or the innerHTML of the first element.
       
  1941 		 */
       
  1942 		html: function(value) {
       
  1943 			var self = this, i;
       
  1944 
       
  1945 			if (isDefined(value)) {
       
  1946 				i = self.length;
       
  1947 
       
  1948 				try {
       
  1949 					while (i--) {
       
  1950 						self[i].innerHTML = value;
       
  1951 					}
       
  1952 				} catch (ex) {
       
  1953 					// Workaround for "Unknown runtime error" when DIV is added to P on IE
       
  1954 					DomQuery(self[i]).empty().append(value);
       
  1955 				}
       
  1956 
       
  1957 				return self;
       
  1958 			}
       
  1959 
       
  1960 			return self[0] ? self[0].innerHTML : '';
       
  1961 		},
       
  1962 
       
  1963 		/**
       
  1964 		 * Sets or gets the text of the current set or first set node.
       
  1965 		 *
       
  1966 		 * @method text
       
  1967 		 * @param {String} value Optional innerText value to set on each element.
       
  1968 		 * @return {tinymce.dom.DomQuery/String} Current set or the innerText of the first element.
       
  1969 		 */
       
  1970 		text: function(value) {
       
  1971 			var self = this, i;
       
  1972 
       
  1973 			if (isDefined(value)) {
       
  1974 				i = self.length;
       
  1975 				while (i--) {
       
  1976 					if ("innerText" in self[i]) {
       
  1977 						self[i].innerText = value;
       
  1978 					} else {
       
  1979 						self[0].textContent = value;
       
  1980 					}
       
  1981 				}
       
  1982 
       
  1983 				return self;
       
  1984 			}
       
  1985 
       
  1986 			return self[0] ? (self[0].innerText || self[0].textContent) : '';
       
  1987 		},
       
  1988 
       
  1989 		/**
       
  1990 		 * Appends the specified node/html or node set to the current set nodes.
       
  1991 		 *
       
  1992 		 * @method append
       
  1993 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to append to each element in set.
       
  1994 		 * @return {tinymce.dom.DomQuery} Current set.
       
  1995 		 */
       
  1996 		append: function() {
       
  1997 			return domManipulate(this, arguments, function(node) {
       
  1998 				if (this.nodeType === 1) {
       
  1999 					this.appendChild(node);
       
  2000 				}
       
  2001 			});
       
  2002 		},
       
  2003 
       
  2004 		/**
       
  2005 		 * Prepends the specified node/html or node set to the current set nodes.
       
  2006 		 *
       
  2007 		 * @method prepend
       
  2008 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to prepend to each element in set.
       
  2009 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2010 		 */
       
  2011 		prepend: function() {
       
  2012 			return domManipulate(this, arguments, function(node) {
       
  2013 				if (this.nodeType === 1) {
       
  2014 					this.insertBefore(node, this.firstChild);
       
  2015 				}
       
  2016 			}, true);
       
  2017 		},
       
  2018 
       
  2019 		/**
       
  2020 		 * Adds the specified elements before current set nodes.
       
  2021 		 *
       
  2022 		 * @method before
       
  2023 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add before to each element in set.
       
  2024 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2025 		 */
       
  2026 		before: function() {
       
  2027 			var self = this;
       
  2028 
       
  2029 			if (self[0] && self[0].parentNode) {
       
  2030 				return domManipulate(self, arguments, function(node) {
       
  2031 					this.parentNode.insertBefore(node, this);
       
  2032 				});
       
  2033 			}
       
  2034 
       
  2035 			return self;
       
  2036 		},
       
  2037 
       
  2038 		/**
       
  2039 		 * Adds the specified elements after current set nodes.
       
  2040 		 *
       
  2041 		 * @method after
       
  2042 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add after to each element in set.
       
  2043 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2044 		 */
       
  2045 		after: function() {
       
  2046 			var self = this;
       
  2047 
       
  2048 			if (self[0] && self[0].parentNode) {
       
  2049 				return domManipulate(self, arguments, function(node) {
       
  2050 					this.parentNode.insertBefore(node, this.nextSibling);
       
  2051 				}, true);
       
  2052 			}
       
  2053 
       
  2054 			return self;
       
  2055 		},
       
  2056 
       
  2057 		/**
       
  2058 		 * Appends the specified set nodes to the specified selector/instance.
       
  2059 		 *
       
  2060 		 * @method appendTo
       
  2061 		 * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to append the current set to.
       
  2062 		 * @return {tinymce.dom.DomQuery} Current set with the appended nodes.
       
  2063 		 */
       
  2064 		appendTo: function(val) {
       
  2065 			DomQuery(val).append(this);
       
  2066 
       
  2067 			return this;
       
  2068 		},
       
  2069 
       
  2070 		/**
       
  2071 		 * Prepends the specified set nodes to the specified selector/instance.
       
  2072 		 *
       
  2073 		 * @method prependTo
       
  2074 		 * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to prepend the current set to.
       
  2075 		 * @return {tinymce.dom.DomQuery} Current set with the prepended nodes.
       
  2076 		 */
       
  2077 		prependTo: function(val) {
       
  2078 			DomQuery(val).prepend(this);
       
  2079 
       
  2080 			return this;
       
  2081 		},
       
  2082 
       
  2083 		/**
       
  2084 		 * Replaces the nodes in set with the specified content.
       
  2085 		 *
       
  2086 		 * @method replaceWith
       
  2087 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to replace nodes with.
       
  2088 		 * @return {tinymce.dom.DomQuery} Set with replaced nodes.
       
  2089 		 */
       
  2090 		replaceWith: function(content) {
       
  2091 			return this.before(content).remove();
       
  2092 		},
       
  2093 
       
  2094 		/**
       
  2095 		 * Wraps all elements in set with the specified wrapper.
       
  2096 		 *
       
  2097 		 * @method wrap
       
  2098 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
       
  2099 		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
       
  2100 		 */
       
  2101 		wrap: function(wrapper) {
       
  2102 			return wrap(this, wrapper);
       
  2103 		},
       
  2104 
       
  2105 		/**
       
  2106 		 * Wraps all nodes in set with the specified wrapper. If the nodes are siblings all of them
       
  2107 		 * will be wrapped in the same wrapper.
       
  2108 		 *
       
  2109 		 * @method wrapAll
       
  2110 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
       
  2111 		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
       
  2112 		 */
       
  2113 		wrapAll: function(wrapper) {
       
  2114 			return wrap(this, wrapper, true);
       
  2115 		},
       
  2116 
       
  2117 		/**
       
  2118 		 * Wraps all elements inner contents in set with the specified wrapper.
       
  2119 		 *
       
  2120 		 * @method wrapInner
       
  2121 		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
       
  2122 		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
       
  2123 		 */
       
  2124 		wrapInner: function(wrapper) {
       
  2125 			this.each(function() {
       
  2126 				DomQuery(this).contents().wrapAll(wrapper);
       
  2127 			});
       
  2128 
       
  2129 			return this;
       
  2130 		},
       
  2131 
       
  2132 		/**
       
  2133 		 * Unwraps all elements by removing the parent element of each item in set.
       
  2134 		 *
       
  2135 		 * @method unwrap
       
  2136 		 * @return {tinymce.dom.DomQuery} Set with unwrapped nodes.
       
  2137 		 */
       
  2138 		unwrap: function() {
       
  2139 			return this.parent().each(function() {
       
  2140 				DomQuery(this).replaceWith(this.childNodes);
       
  2141 			});
       
  2142 		},
       
  2143 
       
  2144 		/**
       
  2145 		 * Clones all nodes in set.
       
  2146 		 *
       
  2147 		 * @method clone
       
  2148 		 * @return {tinymce.dom.DomQuery} Set with cloned nodes.
       
  2149 		 */
       
  2150 		clone: function() {
       
  2151 			var result = [];
       
  2152 
       
  2153 			this.each(function() {
       
  2154 				result.push(this.cloneNode(true));
       
  2155 			});
       
  2156 
       
  2157 			return DomQuery(result);
       
  2158 		},
       
  2159 
       
  2160 		/**
       
  2161 		 * Adds the specified class name to the current set elements.
       
  2162 		 *
       
  2163 		 * @method addClass
       
  2164 		 * @param {String} className Class name to add.
       
  2165 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2166 		 */
       
  2167 		addClass: function(className) {
       
  2168 			return this.toggleClass(className, true);
       
  2169 		},
       
  2170 
       
  2171 		/**
       
  2172 		 * Removes the specified class name to the current set elements.
       
  2173 		 *
       
  2174 		 * @method removeClass
       
  2175 		 * @param {String} className Class name to remove.
       
  2176 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2177 		 */
       
  2178 		removeClass: function(className) {
       
  2179 			return this.toggleClass(className, false);
       
  2180 		},
       
  2181 
       
  2182 		/**
       
  2183 		 * Toggles the specified class name on the current set elements.
       
  2184 		 *
       
  2185 		 * @method toggleClass
       
  2186 		 * @param {String} className Class name to add/remove.
       
  2187 		 * @param {Boolean} state Optional state to toggle on/off.
       
  2188 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2189 		 */
       
  2190 		toggleClass: function(className, state) {
       
  2191 			var self = this;
       
  2192 
       
  2193 			// Functions are not supported
       
  2194 			if (typeof className != 'string') {
       
  2195 				return self;
       
  2196 			}
       
  2197 
       
  2198 			if (className.indexOf(' ') !== -1) {
       
  2199 				each(className.split(' '), function() {
       
  2200 					self.toggleClass(this, state);
       
  2201 				});
       
  2202 			} else {
       
  2203 				self.each(function(index, node) {
       
  2204 					var existingClassName, classState;
       
  2205 
       
  2206 					classState = hasClass(node, className);
       
  2207 					if (classState !== state) {
       
  2208 						existingClassName = node.className;
       
  2209 
       
  2210 						if (classState) {
       
  2211 							node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
       
  2212 						} else {
       
  2213 							node.className += existingClassName ? ' ' + className : className;
       
  2214 						}
       
  2215 					}
       
  2216 				});
       
  2217 			}
       
  2218 
       
  2219 			return self;
       
  2220 		},
       
  2221 
       
  2222 		/**
       
  2223 		 * Returns true/false if the first item in set has the specified class.
       
  2224 		 *
       
  2225 		 * @method hasClass
       
  2226 		 * @param {String} className Class name to check for.
       
  2227 		 * @return {Boolean} True/false if the set has the specified class.
       
  2228 		 */
       
  2229 		hasClass: function(className) {
       
  2230 			return hasClass(this[0], className);
       
  2231 		},
       
  2232 
       
  2233 		/**
       
  2234 		 * Executes the callback function for each item DomQuery collection. If you return false in the
       
  2235 		 * callback it will break the loop.
       
  2236 		 *
       
  2237 		 * @method each
       
  2238 		 * @param {function} callback Callback function to execute for each item.
       
  2239 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2240 		 */
       
  2241 		each: function(callback) {
       
  2242 			return each(this, callback);
       
  2243 		},
       
  2244 
       
  2245 		/**
       
  2246 		 * Binds an event with callback function to the elements in set.
       
  2247 		 *
       
  2248 		 * @method on
       
  2249 		 * @param {String} name Name of the event to bind.
       
  2250 		 * @param {function} callback Callback function to execute when the event occurs.
       
  2251 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2252 		 */
       
  2253 		on: function(name, callback) {
       
  2254 			return this.each(function() {
       
  2255 				Event.bind(this, name, callback);
       
  2256 			});
       
  2257 		},
       
  2258 
       
  2259 		/**
       
  2260 		 * Unbinds an event with callback function to the elements in set.
       
  2261 		 *
       
  2262 		 * @method off
       
  2263 		 * @param {String} name Optional name of the event to bind.
       
  2264 		 * @param {function} callback Optional callback function to execute when the event occurs.
       
  2265 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2266 		 */
       
  2267 		off: function(name, callback) {
       
  2268 			return this.each(function() {
       
  2269 				Event.unbind(this, name, callback);
       
  2270 			});
       
  2271 		},
       
  2272 
       
  2273 		/**
       
  2274 		 * Triggers the specified event by name or event object.
       
  2275 		 *
       
  2276 		 * @method trigger
       
  2277 		 * @param {String/Object} name Name of the event to trigger or event object.
       
  2278 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2279 		 */
       
  2280 		trigger: function(name) {
       
  2281 			return this.each(function() {
       
  2282 				if (typeof name == 'object') {
       
  2283 					Event.fire(this, name.type, name);
       
  2284 				} else {
       
  2285 					Event.fire(this, name);
       
  2286 				}
       
  2287 			});
       
  2288 		},
       
  2289 
       
  2290 		/**
       
  2291 		 * Shows all elements in set.
       
  2292 		 *
       
  2293 		 * @method show
       
  2294 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2295 		 */
       
  2296 		show: function() {
       
  2297 			return this.css('display', '');
       
  2298 		},
       
  2299 
       
  2300 		/**
       
  2301 		 * Hides all elements in set.
       
  2302 		 *
       
  2303 		 * @method hide
       
  2304 		 * @return {tinymce.dom.DomQuery} Current set.
       
  2305 		 */
       
  2306 		hide: function() {
       
  2307 			return this.css('display', 'none');
       
  2308 		},
       
  2309 
       
  2310 		/**
       
  2311 		 * Slices the current set.
       
  2312 		 *
       
  2313 		 * @method slice
       
  2314 		 * @param {Number} start Start index to slice at.
       
  2315 		 * @param {Number} end Optional ened index to end slice at.
       
  2316 		 * @return {tinymce.dom.DomQuery} Sliced set.
       
  2317 		 */
       
  2318 		slice: function() {
       
  2319 			return new DomQuery(slice.apply(this, arguments));
       
  2320 		},
       
  2321 
       
  2322 		/**
       
  2323 		 * Makes the set equal to the specified index.
       
  2324 		 *
       
  2325 		 * @method eq
       
  2326 		 * @param {Number} index Index to set it equal to.
       
  2327 		 * @return {tinymce.dom.DomQuery} Single item set.
       
  2328 		 */
       
  2329 		eq: function(index) {
       
  2330 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
       
  2331 		},
       
  2332 
       
  2333 		/**
       
  2334 		 * Makes the set equal to first element in set.
       
  2335 		 *
       
  2336 		 * @method first
       
  2337 		 * @return {tinymce.dom.DomQuery} Single item set.
       
  2338 		 */
       
  2339 		first: function() {
       
  2340 			return this.eq(0);
       
  2341 		},
       
  2342 
       
  2343 		/**
       
  2344 		 * Makes the set equal to last element in set.
       
  2345 		 *
       
  2346 		 * @method last
       
  2347 		 * @return {tinymce.dom.DomQuery} Single item set.
       
  2348 		 */
       
  2349 		last: function() {
       
  2350 			return this.eq(-1);
       
  2351 		},
       
  2352 
       
  2353 		/**
       
  2354 		 * Finds elements by the specified selector for each element in set.
       
  2355 		 *
       
  2356 		 * @method find
       
  2357 		 * @param {String} selector Selector to find elements by.
       
  2358 		 * @return {tinymce.dom.DomQuery} Set with matches elements.
       
  2359 		 */
       
  2360 		find: function(selector) {
       
  2361 			var i, l, ret = [];
       
  2362 
       
  2363 			for (i = 0, l = this.length; i < l; i++) {
       
  2364 				DomQuery.find(selector, this[i], ret);
       
  2365 			}
       
  2366 
       
  2367 			return DomQuery(ret);
       
  2368 		},
       
  2369 
       
  2370 		/**
       
  2371 		 * Filters the current set with the specified selector.
       
  2372 		 *
       
  2373 		 * @method filter
       
  2374 		 * @param {String/function} selector Selector to filter elements by.
       
  2375 		 * @return {tinymce.dom.DomQuery} Set with filtered elements.
       
  2376 		 */
       
  2377 		filter: function(selector) {
       
  2378 			if (typeof selector == 'function') {
       
  2379 				return DomQuery(grep(this.toArray(), function(item, i) {
       
  2380 					return selector(i, item);
       
  2381 				}));
       
  2382 			}
       
  2383 
       
  2384 			return DomQuery(DomQuery.filter(selector, this.toArray()));
       
  2385 		},
       
  2386 
       
  2387 		/**
       
  2388 		 * Gets the current node or any partent matching the specified selector.
       
  2389 		 *
       
  2390 		 * @method closest
       
  2391 		 * @param {String/Element/tinymce.dom.DomQuery} selector Selector or element to find.
       
  2392 		 * @return {tinymce.dom.DomQuery} Set with closest elements.
       
  2393 		 */
       
  2394 		closest: function(selector) {
       
  2395 			var result = [];
       
  2396 
       
  2397 			if (selector instanceof DomQuery) {
       
  2398 				selector = selector[0];
       
  2399 			}
       
  2400 
       
  2401 			this.each(function(i, node) {
       
  2402 				while (node) {
       
  2403 					if (typeof selector == 'string' && DomQuery(node).is(selector)) {
       
  2404 						result.push(node);
       
  2405 						break;
       
  2406 					} else if (node == selector) {
       
  2407 						result.push(node);
       
  2408 						break;
       
  2409 					}
       
  2410 
       
  2411 					node = node.parentNode;
       
  2412 				}
       
  2413 			});
       
  2414 
       
  2415 			return DomQuery(result);
       
  2416 		},
       
  2417 
       
  2418 		/**
       
  2419 		 * Returns the offset of the first element in set or sets the top/left css properties of all elements in set.
       
  2420 		 *
       
  2421 		 * @method offset
       
  2422 		 * @param {Object} offset Optional offset object to set on each item.
       
  2423 		 * @return {Object/tinymce.dom.DomQuery} Returns the first element offset or the current set if you specified an offset.
       
  2424 		 */
       
  2425 		offset: function(offset) {
       
  2426 			var elm, doc, docElm;
       
  2427 			var x = 0, y = 0, pos;
       
  2428 
       
  2429 			if (!offset) {
       
  2430 				elm = this[0];
       
  2431 
       
  2432 				if (elm) {
       
  2433 					doc = elm.ownerDocument;
       
  2434 					docElm = doc.documentElement;
       
  2435 
       
  2436 					if (elm.getBoundingClientRect) {
       
  2437 						pos = elm.getBoundingClientRect();
       
  2438 						x = pos.left + (docElm.scrollLeft || doc.body.scrollLeft) - docElm.clientLeft;
       
  2439 						y = pos.top + (docElm.scrollTop || doc.body.scrollTop) - docElm.clientTop;
       
  2440 					}
       
  2441 				}
       
  2442 
       
  2443 				return {
       
  2444 					left: x,
       
  2445 					top: y
       
  2446 				};
       
  2447 			}
       
  2448 
       
  2449 			return this.css(offset);
       
  2450 		},
       
  2451 
       
  2452 		push: push,
       
  2453 		sort: [].sort,
       
  2454 		splice: [].splice
       
  2455 	};
       
  2456 
       
  2457 	// Static members
       
  2458 	Tools.extend(DomQuery, {
       
  2459 		/**
       
  2460 		 * Extends the specified object with one or more objects.
       
  2461 		 *
       
  2462 		 * @static
       
  2463 		 * @method extend
       
  2464 		 * @param {Object} target Target object to extend with new items.
       
  2465 		 * @param {Object..} object Object to extend the target with.
       
  2466 		 * @return {Object} Extended input object.
       
  2467 		 */
       
  2468 		extend: Tools.extend,
       
  2469 
       
  2470 		/**
       
  2471 		 * Creates an array out of an array like object.
       
  2472 		 *
       
  2473 		 * @static
       
  2474 		 * @method makeArray
       
  2475 		 * @param {Object} object Object to convert to array.
       
  2476 		 * @return {Arrau} Array produced from object.
       
  2477 		 */
       
  2478 		makeArray: function(array) {
       
  2479 			if (isWindow(array) || array.nodeType) {
       
  2480 				return [array];
       
  2481 			}
       
  2482 
       
  2483 			return Tools.toArray(array);
       
  2484 		},
       
  2485 
       
  2486 		/**
       
  2487 		 * Returns the index of the specified item inside the array.
       
  2488 		 *
       
  2489 		 * @static
       
  2490 		 * @method inArray
       
  2491 		 * @param {Object} item Item to look for.
       
  2492 		 * @param {Array} array Array to look for item in.
       
  2493 		 * @return {Number} Index of the item or -1.
       
  2494 		 */
       
  2495 		inArray: inArray,
       
  2496 
       
  2497 		/**
       
  2498 		 * Returns true/false if the specified object is an array or not.
       
  2499 		 *
       
  2500 		 * @static
       
  2501 		 * @method isArray
       
  2502 		 * @param {Object} array Object to check if it's an array or not.
       
  2503 		 * @return {Boolean} True/false if the object is an array.
       
  2504 		 */
       
  2505 		isArray: Tools.isArray,
       
  2506 
       
  2507 		/**
       
  2508 		 * Executes the callback function for each item in array/object. If you return false in the
       
  2509 		 * callback it will break the loop.
       
  2510 		 *
       
  2511 		 * @static
       
  2512 		 * @method each
       
  2513 		 * @param {Object} obj Object to iterate.
       
  2514 		 * @param {function} callback Callback function to execute for each item.
       
  2515 		 */
       
  2516 		each: each,
       
  2517 
       
  2518 		/**
       
  2519 		 * Removes whitespace from the beginning and end of a string.
       
  2520 		 *
       
  2521 		 * @static
       
  2522 		 * @method trim
       
  2523 		 * @param {String} str String to remove whitespace from.
       
  2524 		 * @return {String} New string with removed whitespace.
       
  2525 		 */
       
  2526 		trim: trim,
       
  2527 
       
  2528 		/**
       
  2529 		 * Filters out items from the input array by calling the specified function for each item.
       
  2530 		 * If the function returns false the item will be excluded if it returns true it will be included.
       
  2531 		 *
       
  2532 		 * @static
       
  2533 		 * @method grep
       
  2534 		 * @param {Array} array Array of items to loop though.
       
  2535 		 * @param {function} callback Function to call for each item. Include/exclude depends on it's return value.
       
  2536 		 * @return {Array} New array with values imported and filtered based in input.
       
  2537 		 * @example
       
  2538 		 * // Filter out some items, this will return an array with 4 and 5
       
  2539 		 * var items = DomQuery.grep([1, 2, 3, 4, 5], function(v) {return v > 3;});
       
  2540 		 */
       
  2541 		grep: grep,
       
  2542 
       
  2543 		// Sizzle
       
  2544 		find: Sizzle,
       
  2545 		expr: Sizzle.selectors,
       
  2546 		unique: Sizzle.uniqueSort,
       
  2547 		text: Sizzle.getText,
       
  2548 		contains: Sizzle.contains,
       
  2549 		filter: function(expr, elems, not) {
       
  2550 			var i = elems.length;
       
  2551 
       
  2552 			if (not) {
       
  2553 				expr = ":not(" + expr + ")";
       
  2554 			}
       
  2555 
       
  2556 			while (i--) {
       
  2557 				if (elems[i].nodeType != 1) {
       
  2558 					elems.splice(i, 1);
       
  2559 				}
       
  2560 			}
       
  2561 
       
  2562 			if (elems.length === 1) {
       
  2563 				elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [];
       
  2564 			} else {
       
  2565 				elems = DomQuery.find.matches(expr, elems);
       
  2566 			}
       
  2567 
       
  2568 			return elems;
       
  2569 		}
       
  2570 	});
       
  2571 
       
  2572 	function dir(el, prop, until) {
       
  2573 		var matched = [], cur = el[prop];
       
  2574 
       
  2575 		if (typeof until != 'string' && until instanceof DomQuery) {
       
  2576 			until = until[0];
       
  2577 		}
       
  2578 
       
  2579 		while (cur && cur.nodeType !== 9) {
       
  2580 			if (until !== undefined) {
       
  2581 				if (cur === until) {
       
  2582 					break;
       
  2583 				}
       
  2584 
       
  2585 				if (typeof until == 'string' && DomQuery(cur).is(until)) {
       
  2586 					break;
       
  2587 				}
       
  2588 			}
       
  2589 
       
  2590 			if (cur.nodeType === 1) {
       
  2591 				matched.push(cur);
       
  2592 			}
       
  2593 
       
  2594 			cur = cur[prop];
       
  2595 		}
       
  2596 
       
  2597 		return matched;
       
  2598 	}
       
  2599 
       
  2600 	function sibling(node, siblingName, nodeType, until) {
       
  2601 		var result = [];
       
  2602 
       
  2603 		if (until instanceof DomQuery) {
       
  2604 			until = until[0];
       
  2605 		}
       
  2606 
       
  2607 		for (; node; node = node[siblingName]) {
       
  2608 			if (nodeType && node.nodeType !== nodeType) {
       
  2609 				continue;
       
  2610 			}
       
  2611 
       
  2612 			if (until !== undefined) {
       
  2613 				if (node === until) {
       
  2614 					break;
       
  2615 				}
       
  2616 
       
  2617 				if (typeof until == 'string' && DomQuery(node).is(until)) {
       
  2618 					break;
       
  2619 				}
       
  2620 			}
       
  2621 
       
  2622 			result.push(node);
       
  2623 		}
       
  2624 
       
  2625 		return result;
       
  2626 	}
       
  2627 
       
  2628 	function firstSibling(node, siblingName, nodeType) {
       
  2629 		for (node = node[siblingName]; node; node = node[siblingName]) {
       
  2630 			if (node.nodeType == nodeType) {
       
  2631 				return node;
       
  2632 			}
       
  2633 		}
       
  2634 
       
  2635 		return null;
       
  2636 	}
       
  2637 
       
  2638 	each({
       
  2639 		/**
       
  2640 		 * Returns a new collection with the parent of each item in current collection matching the optional selector.
       
  2641 		 *
       
  2642 		 * @method parent
       
  2643 		 * @param {String} selector Selector to match parents agains.
       
  2644 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
       
  2645 		 */
       
  2646 		parent: function(node) {
       
  2647 			var parent = node.parentNode;
       
  2648 
       
  2649 			return parent && parent.nodeType !== 11 ? parent : null;
       
  2650 		},
       
  2651 
       
  2652 		/**
       
  2653 		 * Returns a new collection with the all the parents of each item in current collection matching the optional selector.
       
  2654 		 *
       
  2655 		 * @method parents
       
  2656 		 * @param {String} selector Selector to match parents agains.
       
  2657 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
       
  2658 		 */
       
  2659 		parents: function(node) {
       
  2660 			return dir(node, "parentNode");
       
  2661 		},
       
  2662 
       
  2663 		/**
       
  2664 		 * Returns a new collection with next sibling of each item in current collection matching the optional selector.
       
  2665 		 *
       
  2666 		 * @method next
       
  2667 		 * @param {String} selector Selector to match the next element against.
       
  2668 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  2669 		 */
       
  2670 		next: function(node) {
       
  2671 			return firstSibling(node, 'nextSibling', 1);
       
  2672 		},
       
  2673 
       
  2674 		/**
       
  2675 		 * Returns a new collection with previous sibling of each item in current collection matching the optional selector.
       
  2676 		 *
       
  2677 		 * @method prev
       
  2678 		 * @param {String} selector Selector to match the previous element against.
       
  2679 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  2680 		 */
       
  2681 		prev: function(node) {
       
  2682 			return firstSibling(node, 'previousSibling', 1);
       
  2683 		},
       
  2684 
       
  2685 		/**
       
  2686 		 * Returns all child elements matching the optional selector.
       
  2687 		 *
       
  2688 		 * @method children
       
  2689 		 * @param {String} selector Selector to match the elements against.
       
  2690 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  2691 		 */
       
  2692 		children: function(node) {
       
  2693 			return sibling(node.firstChild, 'nextSibling', 1);
       
  2694 		},
       
  2695 
       
  2696 		/**
       
  2697 		 * Returns all child nodes matching the optional selector.
       
  2698 		 *
       
  2699 		 * @method contents
       
  2700 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  2701 		 */
       
  2702 		contents: function(node) {
       
  2703 			return Tools.toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
       
  2704 		}
       
  2705 	}, function(name, fn) {
       
  2706 		DomQuery.fn[name] = function(selector) {
       
  2707 			var self = this, result = [];
       
  2708 
       
  2709 			self.each(function() {
       
  2710 				var nodes = fn.call(result, this, selector, result);
       
  2711 
       
  2712 				if (nodes) {
       
  2713 					if (DomQuery.isArray(nodes)) {
       
  2714 						result.push.apply(result, nodes);
       
  2715 					} else {
       
  2716 						result.push(nodes);
       
  2717 					}
       
  2718 				}
       
  2719 			});
       
  2720 
       
  2721 			// If traversing on multiple elements we might get the same elements twice
       
  2722 			if (this.length > 1) {
       
  2723 				result = DomQuery.unique(result);
       
  2724 
       
  2725 				if (name.indexOf('parents') === 0) {
       
  2726 					result = result.reverse();
       
  2727 				}
       
  2728 			}
       
  2729 
       
  2730 			result = DomQuery(result);
       
  2731 
       
  2732 			if (selector) {
       
  2733 				return result.filter(selector);
       
  2734 			}
       
  2735 
       
  2736 			return result;
       
  2737 		};
       
  2738 	});
       
  2739 
       
  2740 	each({
       
  2741 		/**
       
  2742 		 * Returns a new collection with the all the parents until the matching selector/element
       
  2743 		 * of each item in current collection matching the optional selector.
       
  2744 		 *
       
  2745 		 * @method parentsUntil
       
  2746 		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
       
  2747 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
       
  2748 		 */
       
  2749 		parentsUntil: function(node, until) {
       
  2750 			return dir(node, "parentNode", until);
       
  2751 		},
       
  2752 
       
  2753 		/**
       
  2754 		 * Returns a new collection with all next siblings of each item in current collection matching the optional selector.
       
  2755 		 *
       
  2756 		 * @method nextUntil
       
  2757 		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
       
  2758 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  2759 		 */
       
  2760 		nextUntil: function(node, until) {
       
  2761 			return sibling(node, 'nextSibling', 1, until).slice(1);
       
  2762 		},
       
  2763 
       
  2764 		/**
       
  2765 		 * Returns a new collection with all previous siblings of each item in current collection matching the optional selector.
       
  2766 		 *
       
  2767 		 * @method prevUntil
       
  2768 		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
       
  2769 		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
       
  2770 		 */
       
  2771 		prevUntil: function(node, until) {
       
  2772 			return sibling(node, 'previousSibling', 1, until).slice(1);
       
  2773 		}
       
  2774 	}, function(name, fn) {
       
  2775 		DomQuery.fn[name] = function(selector, filter) {
       
  2776 			var self = this, result = [];
       
  2777 
       
  2778 			self.each(function() {
       
  2779 				var nodes = fn.call(result, this, selector, result);
       
  2780 
       
  2781 				if (nodes) {
       
  2782 					if (DomQuery.isArray(nodes)) {
       
  2783 						result.push.apply(result, nodes);
       
  2784 					} else {
       
  2785 						result.push(nodes);
       
  2786 					}
       
  2787 				}
       
  2788 			});
       
  2789 
       
  2790 			// If traversing on multiple elements we might get the same elements twice
       
  2791 			if (this.length > 1) {
       
  2792 				result = DomQuery.unique(result);
       
  2793 
       
  2794 				if (name.indexOf('parents') === 0 || name === 'prevUntil') {
       
  2795 					result = result.reverse();
       
  2796 				}
       
  2797 			}
       
  2798 
       
  2799 			result = DomQuery(result);
       
  2800 
       
  2801 			if (filter) {
       
  2802 				return result.filter(filter);
       
  2803 			}
       
  2804 
       
  2805 			return result;
       
  2806 		};
       
  2807 	});
       
  2808 
       
  2809 	/**
       
  2810 	 * Returns true/false if the current set items matches the selector.
       
  2811 	 *
       
  2812 	 * @method is
       
  2813 	 * @param {String} selector Selector to match the elements against.
       
  2814 	 * @return {Boolean} True/false if the current set matches the selector.
       
  2815 	 */
       
  2816 	DomQuery.fn.is = function(selector) {
       
  2817 		return !!selector && this.filter(selector).length > 0;
       
  2818 	};
       
  2819 
       
  2820 	DomQuery.fn.init.prototype = DomQuery.fn;
       
  2821 
       
  2822 	DomQuery.overrideDefaults = function(callback) {
       
  2823 		var defaults;
       
  2824 
       
  2825 		function sub(selector, context) {
       
  2826 			defaults = defaults || callback();
       
  2827 
       
  2828 			if (arguments.length === 0) {
       
  2829 				selector = defaults.element;
       
  2830 			}
       
  2831 
       
  2832 			if (!context) {
       
  2833 				context = defaults.context;
       
  2834 			}
       
  2835 
       
  2836 			return new sub.fn.init(selector, context);
       
  2837 		}
       
  2838 
       
  2839 		DomQuery.extend(sub, this);
       
  2840 
       
  2841 		return sub;
       
  2842 	};
       
  2843 
       
  2844 	function appendHooks(targetHooks, prop, hooks) {
       
  2845 		each(hooks, function(name, func) {
       
  2846 			targetHooks[name] = targetHooks[name] || {};
       
  2847 			targetHooks[name][prop] = func;
       
  2848 		});
       
  2849 	}
       
  2850 
       
  2851 	if (Env.ie && Env.ie < 8) {
       
  2852 		appendHooks(attrHooks, 'get', {
       
  2853 			maxlength: function(elm) {
       
  2854 				var value = elm.maxLength;
       
  2855 
       
  2856 				if (value === 0x7fffffff) {
       
  2857 					return undef;
       
  2858 				}
       
  2859 
       
  2860 				return value;
       
  2861 			},
       
  2862 
       
  2863 			size: function(elm) {
       
  2864 				var value = elm.size;
       
  2865 
       
  2866 				if (value === 20) {
       
  2867 					return undef;
       
  2868 				}
       
  2869 
       
  2870 				return value;
       
  2871 			},
       
  2872 
       
  2873 			'class': function(elm) {
       
  2874 				return elm.className;
       
  2875 			},
       
  2876 
       
  2877 			style: function(elm) {
       
  2878 				var value = elm.style.cssText;
       
  2879 
       
  2880 				if (value.length === 0) {
       
  2881 					return undef;
       
  2882 				}
       
  2883 
       
  2884 				return value;
       
  2885 			}
       
  2886 		});
       
  2887 
       
  2888 		appendHooks(attrHooks, 'set', {
       
  2889 			'class': function(elm, value) {
       
  2890 				elm.className = value;
       
  2891 			},
       
  2892 
       
  2893 			style: function(elm, value) {
       
  2894 				elm.style.cssText = value;
       
  2895 			}
       
  2896 		});
       
  2897 	}
       
  2898 
       
  2899 	if (Env.ie && Env.ie < 9) {
       
  2900 		/*jshint sub:true */
       
  2901 		/*eslint dot-notation: 0*/
       
  2902 		cssFix['float'] = 'styleFloat';
       
  2903 
       
  2904 		appendHooks(cssHooks, 'set', {
       
  2905 			opacity: function(elm, value) {
       
  2906 				var style = elm.style;
       
  2907 
       
  2908 				if (value === null || value === '') {
       
  2909 					style.removeAttribute('filter');
       
  2910 				} else {
       
  2911 					style.zoom = 1;
       
  2912 					style.filter = 'alpha(opacity=' + (value * 100) + ')';
       
  2913 				}
       
  2914 			}
       
  2915 		});
       
  2916 	}
       
  2917 
       
  2918 	DomQuery.attrHooks = attrHooks;
       
  2919 	DomQuery.cssHooks = cssHooks;
       
  2920 
       
  2921 	return DomQuery;
       
  2922 });
       
  2923 
       
  2924 // Included from: js/tinymce/classes/html/Styles.js
       
  2925 
       
  2926 /**
       
  2927  * Styles.js
       
  2928  *
       
  2929  * Copyright, Moxiecode Systems AB
       
  2930  * Released under LGPL License.
       
  2931  *
       
  2932  * License: http://www.tinymce.com/license
       
  2933  * Contributing: http://www.tinymce.com/contributing
       
  2934  */
       
  2935 
       
  2936 /**
       
  2937  * This class is used to parse CSS styles it also compresses styles to reduce the output size.
       
  2938  *
       
  2939  * @example
       
  2940  * var Styles = new tinymce.html.Styles({
       
  2941  *    url_converter: function(url) {
       
  2942  *       return url;
       
  2943  *    }
       
  2944  * });
       
  2945  *
       
  2946  * styles = Styles.parse('border: 1px solid red');
       
  2947  * styles.color = 'red';
       
  2948  *
       
  2949  * console.log(new tinymce.html.StyleSerializer().serialize(styles));
       
  2950  *
       
  2951  * @class tinymce.html.Styles
       
  2952  * @version 3.4
       
  2953  */
       
  2954 define("tinymce/html/Styles", [], function() {
       
  2955 	return function(settings, schema) {
       
  2956 		/*jshint maxlen:255 */
       
  2957 		/*eslint max-len:0 */
       
  2958 		var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
       
  2959 			urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
       
  2960 			styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
       
  2961 			trimRightRegExp = /\s+$/,
       
  2962 			undef, i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
       
  2963 
       
  2964 		settings = settings || {};
       
  2965 
       
  2966 		if (schema) {
       
  2967 			validStyles = schema.getValidStyles();
       
  2968 			invalidStyles = schema.getInvalidStyles();
       
  2969 		}
       
  2970 
       
  2971 		encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
       
  2972 		for (i = 0; i < encodingItems.length; i++) {
       
  2973 			encodingLookup[encodingItems[i]] = invisibleChar + i;
       
  2974 			encodingLookup[invisibleChar + i] = encodingItems[i];
       
  2975 		}
       
  2976 
       
  2977 		function toHex(match, r, g, b) {
       
  2978 			function hex(val) {
       
  2979 				val = parseInt(val, 10).toString(16);
       
  2980 
       
  2981 				return val.length > 1 ? val : '0' + val; // 0 -> 00
       
  2982 			}
       
  2983 
       
  2984 			return '#' + hex(r) + hex(g) + hex(b);
       
  2985 		}
       
  2986 
       
  2987 		return {
       
  2988 			/**
       
  2989 			 * Parses the specified RGB color value and returns a hex version of that color.
       
  2990 			 *
       
  2991 			 * @method toHex
       
  2992 			 * @param {String} color RGB string value like rgb(1,2,3)
       
  2993 			 * @return {String} Hex version of that RGB value like #FF00FF.
       
  2994 			 */
       
  2995 			toHex: function(color) {
       
  2996 				return color.replace(rgbRegExp, toHex);
       
  2997 			},
       
  2998 
       
  2999 			/**
       
  3000 			 * Parses the specified style value into an object collection. This parser will also
       
  3001 			 * merge and remove any redundant items that browsers might have added. It will also convert non hex
       
  3002 			 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
       
  3003 			 *
       
  3004 			 * @method parse
       
  3005 			 * @param {String} css Style value to parse for example: border:1px solid red;.
       
  3006 			 * @return {Object} Object representation of that style like {border: '1px solid red'}
       
  3007 			 */
       
  3008 			parse: function(css) {
       
  3009 				var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
       
  3010 				var urlConverterScope = settings.url_converter_scope || this;
       
  3011 
       
  3012 				function compress(prefix, suffix, noJoin) {
       
  3013 					var top, right, bottom, left;
       
  3014 
       
  3015 					top = styles[prefix + '-top' + suffix];
       
  3016 					if (!top) {
       
  3017 						return;
       
  3018 					}
       
  3019 
       
  3020 					right = styles[prefix + '-right' + suffix];
       
  3021 					if (!right) {
       
  3022 						return;
       
  3023 					}
       
  3024 
       
  3025 					bottom = styles[prefix + '-bottom' + suffix];
       
  3026 					if (!bottom) {
       
  3027 						return;
       
  3028 					}
       
  3029 
       
  3030 					left = styles[prefix + '-left' + suffix];
       
  3031 					if (!left) {
       
  3032 						return;
       
  3033 					}
       
  3034 
       
  3035 					var box = [top, right, bottom, left];
       
  3036 					i = box.length - 1;
       
  3037 					while (i--) {
       
  3038 						if (box[i] !== box[i + 1]) {
       
  3039 							break;
       
  3040 						}
       
  3041 					}
       
  3042 
       
  3043 					if (i > -1 && noJoin) {
       
  3044 						return;
       
  3045 					}
       
  3046 
       
  3047 					styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
       
  3048 					delete styles[prefix + '-top' + suffix];
       
  3049 					delete styles[prefix + '-right' + suffix];
       
  3050 					delete styles[prefix + '-bottom' + suffix];
       
  3051 					delete styles[prefix + '-left' + suffix];
       
  3052 				}
       
  3053 
       
  3054 				/**
       
  3055 				 * Checks if the specific style can be compressed in other words if all border-width are equal.
       
  3056 				 */
       
  3057 				function canCompress(key) {
       
  3058 					var value = styles[key], i;
       
  3059 
       
  3060 					if (!value) {
       
  3061 						return;
       
  3062 					}
       
  3063 
       
  3064 					value = value.split(' ');
       
  3065 					i = value.length;
       
  3066 					while (i--) {
       
  3067 						if (value[i] !== value[0]) {
       
  3068 							return false;
       
  3069 						}
       
  3070 					}
       
  3071 
       
  3072 					styles[key] = value[0];
       
  3073 
       
  3074 					return true;
       
  3075 				}
       
  3076 
       
  3077 				/**
       
  3078 				 * Compresses multiple styles into one style.
       
  3079 				 */
       
  3080 				function compress2(target, a, b, c) {
       
  3081 					if (!canCompress(a)) {
       
  3082 						return;
       
  3083 					}
       
  3084 
       
  3085 					if (!canCompress(b)) {
       
  3086 						return;
       
  3087 					}
       
  3088 
       
  3089 					if (!canCompress(c)) {
       
  3090 						return;
       
  3091 					}
       
  3092 
       
  3093 					// Compress
       
  3094 					styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
       
  3095 					delete styles[a];
       
  3096 					delete styles[b];
       
  3097 					delete styles[c];
       
  3098 				}
       
  3099 
       
  3100 				// Encodes the specified string by replacing all \" \' ; : with _<num>
       
  3101 				function encode(str) {
       
  3102 					isEncoded = true;
       
  3103 
       
  3104 					return encodingLookup[str];
       
  3105 				}
       
  3106 
       
  3107 				// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
       
  3108 				// It will also decode the \" \' if keep_slashes is set to fale or omitted
       
  3109 				function decode(str, keep_slashes) {
       
  3110 					if (isEncoded) {
       
  3111 						str = str.replace(/\uFEFF[0-9]/g, function(str) {
       
  3112 							return encodingLookup[str];
       
  3113 						});
       
  3114 					}
       
  3115 
       
  3116 					if (!keep_slashes) {
       
  3117 						str = str.replace(/\\([\'\";:])/g, "$1");
       
  3118 					}
       
  3119 
       
  3120 					return str;
       
  3121 				}
       
  3122 
       
  3123 				function processUrl(match, url, url2, url3, str, str2) {
       
  3124 					str = str || str2;
       
  3125 
       
  3126 					if (str) {
       
  3127 						str = decode(str);
       
  3128 
       
  3129 						// Force strings into single quote format
       
  3130 						return "'" + str.replace(/\'/g, "\\'") + "'";
       
  3131 					}
       
  3132 
       
  3133 					url = decode(url || url2 || url3);
       
  3134 
       
  3135 					if (!settings.allow_script_urls) {
       
  3136 						var scriptUrl = url.replace(/[\s\r\n]+/, '');
       
  3137 
       
  3138 						if (/(java|vb)script:/i.test(scriptUrl)) {
       
  3139 							return "";
       
  3140 						}
       
  3141 
       
  3142 						if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
       
  3143 							return "";
       
  3144 						}
       
  3145 					}
       
  3146 
       
  3147 					// Convert the URL to relative/absolute depending on config
       
  3148 					if (urlConverter) {
       
  3149 						url = urlConverter.call(urlConverterScope, url, 'style');
       
  3150 					}
       
  3151 
       
  3152 					// Output new URL format
       
  3153 					return "url('" + url.replace(/\'/g, "\\'") + "')";
       
  3154 				}
       
  3155 
       
  3156 				if (css) {
       
  3157 					css = css.replace(/[\u0000-\u001F]/g, '');
       
  3158 
       
  3159 					// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
       
  3160 					css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
       
  3161 						return str.replace(/[;:]/g, encode);
       
  3162 					});
       
  3163 
       
  3164 					// Parse styles
       
  3165 					while ((matches = styleRegExp.exec(css))) {
       
  3166 						name = matches[1].replace(trimRightRegExp, '').toLowerCase();
       
  3167 						value = matches[2].replace(trimRightRegExp, '');
       
  3168 
       
  3169 						// Decode escaped sequences like \65 -> e
       
  3170 						/*jshint loopfunc:true*/
       
  3171 						/*eslint no-loop-func:0 */
       
  3172 						value = value.replace(/\\[0-9a-f]+/g, function(e) {
       
  3173 							return String.fromCharCode(parseInt(e.substr(1), 16));
       
  3174 						});
       
  3175 
       
  3176 						if (name && value.length > 0) {
       
  3177 							// Don't allow behavior name or expression/comments within the values
       
  3178 							if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
       
  3179 								continue;
       
  3180 							}
       
  3181 
       
  3182 							// Opera will produce 700 instead of bold in their style values
       
  3183 							if (name === 'font-weight' && value === '700') {
       
  3184 								value = 'bold';
       
  3185 							} else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
       
  3186 								value = value.toLowerCase();
       
  3187 							}
       
  3188 
       
  3189 							// Convert RGB colors to HEX
       
  3190 							value = value.replace(rgbRegExp, toHex);
       
  3191 
       
  3192 							// Convert URLs and force them into url('value') format
       
  3193 							value = value.replace(urlOrStrRegExp, processUrl);
       
  3194 							styles[name] = isEncoded ? decode(value, true) : value;
       
  3195 						}
       
  3196 
       
  3197 						styleRegExp.lastIndex = matches.index + matches[0].length;
       
  3198 					}
       
  3199 					// Compress the styles to reduce it's size for example IE will expand styles
       
  3200 					compress("border", "", true);
       
  3201 					compress("border", "-width");
       
  3202 					compress("border", "-color");
       
  3203 					compress("border", "-style");
       
  3204 					compress("padding", "");
       
  3205 					compress("margin", "");
       
  3206 					compress2('border', 'border-width', 'border-style', 'border-color');
       
  3207 
       
  3208 					// Remove pointless border, IE produces these
       
  3209 					if (styles.border === 'medium none') {
       
  3210 						delete styles.border;
       
  3211 					}
       
  3212 
       
  3213 					// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
       
  3214 					// So lets asume it shouldn't be there
       
  3215 					if (styles['border-image'] === 'none') {
       
  3216 						delete styles['border-image'];
       
  3217 					}
       
  3218 				}
       
  3219 
       
  3220 				return styles;
       
  3221 			},
       
  3222 
       
  3223 			/**
       
  3224 			 * Serializes the specified style object into a string.
       
  3225 			 *
       
  3226 			 * @method serialize
       
  3227 			 * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
       
  3228 			 * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
       
  3229 			 * @return {String} String representation of the style object for example: border: 1px solid red.
       
  3230 			 */
       
  3231 			serialize: function(styles, elementName) {
       
  3232 				var css = '', name, value;
       
  3233 
       
  3234 				function serializeStyles(name) {
       
  3235 					var styleList, i, l, value;
       
  3236 
       
  3237 					styleList = validStyles[name];
       
  3238 					if (styleList) {
       
  3239 						for (i = 0, l = styleList.length; i < l; i++) {
       
  3240 							name = styleList[i];
       
  3241 							value = styles[name];
       
  3242 
       
  3243 							if (value !== undef && value.length > 0) {
       
  3244 								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
       
  3245 							}
       
  3246 						}
       
  3247 					}
       
  3248 				}
       
  3249 
       
  3250 				function isValid(name, elementName) {
       
  3251 					var styleMap;
       
  3252 
       
  3253 					styleMap = invalidStyles['*'];
       
  3254 					if (styleMap && styleMap[name]) {
       
  3255 						return false;
       
  3256 					}
       
  3257 
       
  3258 					styleMap = invalidStyles[elementName];
       
  3259 					if (styleMap && styleMap[name]) {
       
  3260 						return false;
       
  3261 					}
       
  3262 
       
  3263 					return true;
       
  3264 				}
       
  3265 
       
  3266 				// Serialize styles according to schema
       
  3267 				if (elementName && validStyles) {
       
  3268 					// Serialize global styles and element specific styles
       
  3269 					serializeStyles('*');
       
  3270 					serializeStyles(elementName);
       
  3271 				} else {
       
  3272 					// Output the styles in the order they are inside the object
       
  3273 					for (name in styles) {
       
  3274 						value = styles[name];
       
  3275 
       
  3276 						if (value !== undef && value.length > 0) {
       
  3277 							if (!invalidStyles || isValid(name, elementName)) {
       
  3278 								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
       
  3279 							}
       
  3280 						}
       
  3281 					}
       
  3282 				}
       
  3283 
       
  3284 				return css;
       
  3285 			}
       
  3286 		};
       
  3287 	};
       
  3288 });
       
  3289 
       
  3290 // Included from: js/tinymce/classes/dom/TreeWalker.js
       
  3291 
       
  3292 /**
       
  3293  * TreeWalker.js
       
  3294  *
       
  3295  * Copyright, Moxiecode Systems AB
       
  3296  * Released under LGPL License.
       
  3297  *
       
  3298  * License: http://www.tinymce.com/license
       
  3299  * Contributing: http://www.tinymce.com/contributing
       
  3300  */
       
  3301 
       
  3302 /**
       
  3303  * TreeWalker class enables you to walk the DOM in a linear manner.
       
  3304  *
       
  3305  * @class tinymce.dom.TreeWalker
       
  3306  * @example
       
  3307  * var walker = new tinymce.dom.TreeWalker(startNode);
       
  3308  *
       
  3309  * do {
       
  3310  *     console.log(walker.current());
       
  3311  * } while (walker.next());
       
  3312  */
       
  3313 define("tinymce/dom/TreeWalker", [], function() {
       
  3314 	/**
       
  3315 	 * Constructs a new TreeWalker instance.
       
  3316 	 *
       
  3317 	 * @constructor
       
  3318 	 * @method TreeWalker
       
  3319 	 * @param {Node} startNode Node to start walking from.
       
  3320 	 * @param {node} rootNode Optional root node to never walk out of.
       
  3321 	 */
       
  3322 	return function(startNode, rootNode) {
       
  3323 		var node = startNode;
       
  3324 
       
  3325 		function findSibling(node, startName, siblingName, shallow) {
       
  3326 			var sibling, parent;
       
  3327 
       
  3328 			if (node) {
       
  3329 				// Walk into nodes if it has a start
       
  3330 				if (!shallow && node[startName]) {
       
  3331 					return node[startName];
       
  3332 				}
       
  3333 
       
  3334 				// Return the sibling if it has one
       
  3335 				if (node != rootNode) {
       
  3336 					sibling = node[siblingName];
       
  3337 					if (sibling) {
       
  3338 						return sibling;
       
  3339 					}
       
  3340 
       
  3341 					// Walk up the parents to look for siblings
       
  3342 					for (parent = node.parentNode; parent && parent != rootNode; parent = parent.parentNode) {
       
  3343 						sibling = parent[siblingName];
       
  3344 						if (sibling) {
       
  3345 							return sibling;
       
  3346 						}
       
  3347 					}
       
  3348 				}
       
  3349 			}
       
  3350 		}
       
  3351 
       
  3352 		/**
       
  3353 		 * Returns the current node.
       
  3354 		 *
       
  3355 		 * @method current
       
  3356 		 * @return {Node} Current node where the walker is.
       
  3357 		 */
       
  3358 		this.current = function() {
       
  3359 			return node;
       
  3360 		};
       
  3361 
       
  3362 		/**
       
  3363 		 * Walks to the next node in tree.
       
  3364 		 *
       
  3365 		 * @method next
       
  3366 		 * @return {Node} Current node where the walker is after moving to the next node.
       
  3367 		 */
       
  3368 		this.next = function(shallow) {
       
  3369 			node = findSibling(node, 'firstChild', 'nextSibling', shallow);
       
  3370 			return node;
       
  3371 		};
       
  3372 
       
  3373 		/**
       
  3374 		 * Walks to the previous node in tree.
       
  3375 		 *
       
  3376 		 * @method prev
       
  3377 		 * @return {Node} Current node where the walker is after moving to the previous node.
       
  3378 		 */
       
  3379 		this.prev = function(shallow) {
       
  3380 			node = findSibling(node, 'lastChild', 'previousSibling', shallow);
       
  3381 			return node;
       
  3382 		};
       
  3383 	};
       
  3384 });
       
  3385 
       
  3386 // Included from: js/tinymce/classes/dom/Range.js
       
  3387 
       
  3388 /**
       
  3389  * Range.js
       
  3390  *
       
  3391  * Copyright, Moxiecode Systems AB
       
  3392  * Released under LGPL License.
       
  3393  *
       
  3394  * License: http://www.tinymce.com/license
       
  3395  * Contributing: http://www.tinymce.com/contributing
       
  3396  */
       
  3397 
       
  3398 define("tinymce/dom/Range", [
       
  3399 	"tinymce/util/Tools"
       
  3400 ], function(Tools) {
       
  3401 	// Range constructor
       
  3402 	function Range(dom) {
       
  3403 		var self = this,
       
  3404 			doc = dom.doc,
       
  3405 			EXTRACT = 0,
       
  3406 			CLONE = 1,
       
  3407 			DELETE = 2,
       
  3408 			TRUE = true,
       
  3409 			FALSE = false,
       
  3410 			START_OFFSET = 'startOffset',
       
  3411 			START_CONTAINER = 'startContainer',
       
  3412 			END_CONTAINER = 'endContainer',
       
  3413 			END_OFFSET = 'endOffset',
       
  3414 			extend = Tools.extend,
       
  3415 			nodeIndex = dom.nodeIndex;
       
  3416 
       
  3417 		function createDocumentFragment() {
       
  3418 			return doc.createDocumentFragment();
       
  3419 		}
       
  3420 
       
  3421 		function setStart(n, o) {
       
  3422 			_setEndPoint(TRUE, n, o);
       
  3423 		}
       
  3424 
       
  3425 		function setEnd(n, o) {
       
  3426 			_setEndPoint(FALSE, n, o);
       
  3427 		}
       
  3428 
       
  3429 		function setStartBefore(n) {
       
  3430 			setStart(n.parentNode, nodeIndex(n));
       
  3431 		}
       
  3432 
       
  3433 		function setStartAfter(n) {
       
  3434 			setStart(n.parentNode, nodeIndex(n) + 1);
       
  3435 		}
       
  3436 
       
  3437 		function setEndBefore(n) {
       
  3438 			setEnd(n.parentNode, nodeIndex(n));
       
  3439 		}
       
  3440 
       
  3441 		function setEndAfter(n) {
       
  3442 			setEnd(n.parentNode, nodeIndex(n) + 1);
       
  3443 		}
       
  3444 
       
  3445 		function collapse(ts) {
       
  3446 			if (ts) {
       
  3447 				self[END_CONTAINER] = self[START_CONTAINER];
       
  3448 				self[END_OFFSET] = self[START_OFFSET];
       
  3449 			} else {
       
  3450 				self[START_CONTAINER] = self[END_CONTAINER];
       
  3451 				self[START_OFFSET] = self[END_OFFSET];
       
  3452 			}
       
  3453 
       
  3454 			self.collapsed = TRUE;
       
  3455 		}
       
  3456 
       
  3457 		function selectNode(n) {
       
  3458 			setStartBefore(n);
       
  3459 			setEndAfter(n);
       
  3460 		}
       
  3461 
       
  3462 		function selectNodeContents(n) {
       
  3463 			setStart(n, 0);
       
  3464 			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
       
  3465 		}
       
  3466 
       
  3467 		function compareBoundaryPoints(h, r) {
       
  3468 			var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
       
  3469 			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
       
  3470 
       
  3471 			// Check START_TO_START
       
  3472 			if (h === 0) {
       
  3473 				return _compareBoundaryPoints(sc, so, rsc, rso);
       
  3474 			}
       
  3475 
       
  3476 			// Check START_TO_END
       
  3477 			if (h === 1) {
       
  3478 				return _compareBoundaryPoints(ec, eo, rsc, rso);
       
  3479 			}
       
  3480 
       
  3481 			// Check END_TO_END
       
  3482 			if (h === 2) {
       
  3483 				return _compareBoundaryPoints(ec, eo, rec, reo);
       
  3484 			}
       
  3485 
       
  3486 			// Check END_TO_START
       
  3487 			if (h === 3) {
       
  3488 				return _compareBoundaryPoints(sc, so, rec, reo);
       
  3489 			}
       
  3490 		}
       
  3491 
       
  3492 		function deleteContents() {
       
  3493 			_traverse(DELETE);
       
  3494 		}
       
  3495 
       
  3496 		function extractContents() {
       
  3497 			return _traverse(EXTRACT);
       
  3498 		}
       
  3499 
       
  3500 		function cloneContents() {
       
  3501 			return _traverse(CLONE);
       
  3502 		}
       
  3503 
       
  3504 		function insertNode(n) {
       
  3505 			var startContainer = this[START_CONTAINER],
       
  3506 				startOffset = this[START_OFFSET], nn, o;
       
  3507 
       
  3508 			// Node is TEXT_NODE or CDATA
       
  3509 			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
       
  3510 				if (!startOffset) {
       
  3511 					// At the start of text
       
  3512 					startContainer.parentNode.insertBefore(n, startContainer);
       
  3513 				} else if (startOffset >= startContainer.nodeValue.length) {
       
  3514 					// At the end of text
       
  3515 					dom.insertAfter(n, startContainer);
       
  3516 				} else {
       
  3517 					// Middle, need to split
       
  3518 					nn = startContainer.splitText(startOffset);
       
  3519 					startContainer.parentNode.insertBefore(n, nn);
       
  3520 				}
       
  3521 			} else {
       
  3522 				// Insert element node
       
  3523 				if (startContainer.childNodes.length > 0) {
       
  3524 					o = startContainer.childNodes[startOffset];
       
  3525 				}
       
  3526 
       
  3527 				if (o) {
       
  3528 					startContainer.insertBefore(n, o);
       
  3529 				} else {
       
  3530 					if (startContainer.nodeType == 3) {
       
  3531 						dom.insertAfter(n, startContainer);
       
  3532 					} else {
       
  3533 						startContainer.appendChild(n);
       
  3534 					}
       
  3535 				}
       
  3536 			}
       
  3537 		}
       
  3538 
       
  3539 		function surroundContents(n) {
       
  3540 			var f = self.extractContents();
       
  3541 
       
  3542 			self.insertNode(n);
       
  3543 			n.appendChild(f);
       
  3544 			self.selectNode(n);
       
  3545 		}
       
  3546 
       
  3547 		function cloneRange() {
       
  3548 			return extend(new Range(dom), {
       
  3549 				startContainer: self[START_CONTAINER],
       
  3550 				startOffset: self[START_OFFSET],
       
  3551 				endContainer: self[END_CONTAINER],
       
  3552 				endOffset: self[END_OFFSET],
       
  3553 				collapsed: self.collapsed,
       
  3554 				commonAncestorContainer: self.commonAncestorContainer
       
  3555 			});
       
  3556 		}
       
  3557 
       
  3558 		// Private methods
       
  3559 
       
  3560 		function _getSelectedNode(container, offset) {
       
  3561 			var child;
       
  3562 
       
  3563 			if (container.nodeType == 3 /* TEXT_NODE */) {
       
  3564 				return container;
       
  3565 			}
       
  3566 
       
  3567 			if (offset < 0) {
       
  3568 				return container;
       
  3569 			}
       
  3570 
       
  3571 			child = container.firstChild;
       
  3572 			while (child && offset > 0) {
       
  3573 				--offset;
       
  3574 				child = child.nextSibling;
       
  3575 			}
       
  3576 
       
  3577 			if (child) {
       
  3578 				return child;
       
  3579 			}
       
  3580 
       
  3581 			return container;
       
  3582 		}
       
  3583 
       
  3584 		function _isCollapsed() {
       
  3585 			return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
       
  3586 		}
       
  3587 
       
  3588 		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
       
  3589 			var c, offsetC, n, cmnRoot, childA, childB;
       
  3590 
       
  3591 			// In the first case the boundary-points have the same container. A is before B
       
  3592 			// if its offset is less than the offset of B, A is equal to B if its offset is
       
  3593 			// equal to the offset of B, and A is after B if its offset is greater than the
       
  3594 			// offset of B.
       
  3595 			if (containerA == containerB) {
       
  3596 				if (offsetA == offsetB) {
       
  3597 					return 0; // equal
       
  3598 				}
       
  3599 
       
  3600 				if (offsetA < offsetB) {
       
  3601 					return -1; // before
       
  3602 				}
       
  3603 
       
  3604 				return 1; // after
       
  3605 			}
       
  3606 
       
  3607 			// In the second case a child node C of the container of A is an ancestor
       
  3608 			// container of B. In this case, A is before B if the offset of A is less than or
       
  3609 			// equal to the index of the child node C and A is after B otherwise.
       
  3610 			c = containerB;
       
  3611 			while (c && c.parentNode != containerA) {
       
  3612 				c = c.parentNode;
       
  3613 			}
       
  3614 
       
  3615 			if (c) {
       
  3616 				offsetC = 0;
       
  3617 				n = containerA.firstChild;
       
  3618 
       
  3619 				while (n != c && offsetC < offsetA) {
       
  3620 					offsetC++;
       
  3621 					n = n.nextSibling;
       
  3622 				}
       
  3623 
       
  3624 				if (offsetA <= offsetC) {
       
  3625 					return -1; // before
       
  3626 				}
       
  3627 
       
  3628 				return 1; // after
       
  3629 			}
       
  3630 
       
  3631 			// In the third case a child node C of the container of B is an ancestor container
       
  3632 			// of A. In this case, A is before B if the index of the child node C is less than
       
  3633 			// the offset of B and A is after B otherwise.
       
  3634 			c = containerA;
       
  3635 			while (c && c.parentNode != containerB) {
       
  3636 				c = c.parentNode;
       
  3637 			}
       
  3638 
       
  3639 			if (c) {
       
  3640 				offsetC = 0;
       
  3641 				n = containerB.firstChild;
       
  3642 
       
  3643 				while (n != c && offsetC < offsetB) {
       
  3644 					offsetC++;
       
  3645 					n = n.nextSibling;
       
  3646 				}
       
  3647 
       
  3648 				if (offsetC < offsetB) {
       
  3649 					return -1; // before
       
  3650 				}
       
  3651 
       
  3652 				return 1; // after
       
  3653 			}
       
  3654 
       
  3655 			// In the fourth case, none of three other cases hold: the containers of A and B
       
  3656 			// are siblings or descendants of sibling nodes. In this case, A is before B if
       
  3657 			// the container of A is before the container of B in a pre-order traversal of the
       
  3658 			// Ranges' context tree and A is after B otherwise.
       
  3659 			cmnRoot = dom.findCommonAncestor(containerA, containerB);
       
  3660 			childA = containerA;
       
  3661 
       
  3662 			while (childA && childA.parentNode != cmnRoot) {
       
  3663 				childA = childA.parentNode;
       
  3664 			}
       
  3665 
       
  3666 			if (!childA) {
       
  3667 				childA = cmnRoot;
       
  3668 			}
       
  3669 
       
  3670 			childB = containerB;
       
  3671 			while (childB && childB.parentNode != cmnRoot) {
       
  3672 				childB = childB.parentNode;
       
  3673 			}
       
  3674 
       
  3675 			if (!childB) {
       
  3676 				childB = cmnRoot;
       
  3677 			}
       
  3678 
       
  3679 			if (childA == childB) {
       
  3680 				return 0; // equal
       
  3681 			}
       
  3682 
       
  3683 			n = cmnRoot.firstChild;
       
  3684 			while (n) {
       
  3685 				if (n == childA) {
       
  3686 					return -1; // before
       
  3687 				}
       
  3688 
       
  3689 				if (n == childB) {
       
  3690 					return 1; // after
       
  3691 				}
       
  3692 
       
  3693 				n = n.nextSibling;
       
  3694 			}
       
  3695 		}
       
  3696 
       
  3697 		function _setEndPoint(st, n, o) {
       
  3698 			var ec, sc;
       
  3699 
       
  3700 			if (st) {
       
  3701 				self[START_CONTAINER] = n;
       
  3702 				self[START_OFFSET] = o;
       
  3703 			} else {
       
  3704 				self[END_CONTAINER] = n;
       
  3705 				self[END_OFFSET] = o;
       
  3706 			}
       
  3707 
       
  3708 			// If one boundary-point of a Range is set to have a root container
       
  3709 			// other than the current one for the Range, the Range is collapsed to
       
  3710 			// the new position. This enforces the restriction that both boundary-
       
  3711 			// points of a Range must have the same root container.
       
  3712 			ec = self[END_CONTAINER];
       
  3713 			while (ec.parentNode) {
       
  3714 				ec = ec.parentNode;
       
  3715 			}
       
  3716 
       
  3717 			sc = self[START_CONTAINER];
       
  3718 			while (sc.parentNode) {
       
  3719 				sc = sc.parentNode;
       
  3720 			}
       
  3721 
       
  3722 			if (sc == ec) {
       
  3723 				// The start position of a Range is guaranteed to never be after the
       
  3724 				// end position. To enforce this restriction, if the start is set to
       
  3725 				// be at a position after the end, the Range is collapsed to that
       
  3726 				// position.
       
  3727 				if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
       
  3728 					self.collapse(st);
       
  3729 				}
       
  3730 			} else {
       
  3731 				self.collapse(st);
       
  3732 			}
       
  3733 
       
  3734 			self.collapsed = _isCollapsed();
       
  3735 			self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
       
  3736 		}
       
  3737 
       
  3738 		function _traverse(how) {
       
  3739 			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
       
  3740 
       
  3741 			if (self[START_CONTAINER] == self[END_CONTAINER]) {
       
  3742 				return _traverseSameContainer(how);
       
  3743 			}
       
  3744 
       
  3745 			for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
       
  3746 				if (p == self[START_CONTAINER]) {
       
  3747 					return _traverseCommonStartContainer(c, how);
       
  3748 				}
       
  3749 
       
  3750 				++endContainerDepth;
       
  3751 			}
       
  3752 
       
  3753 			for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
       
  3754 				if (p == self[END_CONTAINER]) {
       
  3755 					return _traverseCommonEndContainer(c, how);
       
  3756 				}
       
  3757 
       
  3758 				++startContainerDepth;
       
  3759 			}
       
  3760 
       
  3761 			depthDiff = startContainerDepth - endContainerDepth;
       
  3762 
       
  3763 			startNode = self[START_CONTAINER];
       
  3764 			while (depthDiff > 0) {
       
  3765 				startNode = startNode.parentNode;
       
  3766 				depthDiff--;
       
  3767 			}
       
  3768 
       
  3769 			endNode = self[END_CONTAINER];
       
  3770 			while (depthDiff < 0) {
       
  3771 				endNode = endNode.parentNode;
       
  3772 				depthDiff++;
       
  3773 			}
       
  3774 
       
  3775 			// ascend the ancestor hierarchy until we have a common parent.
       
  3776 			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
       
  3777 				startNode = sp;
       
  3778 				endNode = ep;
       
  3779 			}
       
  3780 
       
  3781 			return _traverseCommonAncestors(startNode, endNode, how);
       
  3782 		}
       
  3783 
       
  3784 		function _traverseSameContainer(how) {
       
  3785 			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
       
  3786 
       
  3787 			if (how != DELETE) {
       
  3788 				frag = createDocumentFragment();
       
  3789 			}
       
  3790 
       
  3791 			// If selection is empty, just return the fragment
       
  3792 			if (self[START_OFFSET] == self[END_OFFSET]) {
       
  3793 				return frag;
       
  3794 			}
       
  3795 
       
  3796 			// Text node needs special case handling
       
  3797 			if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
       
  3798 				// get the substring
       
  3799 				s = self[START_CONTAINER].nodeValue;
       
  3800 				sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
       
  3801 
       
  3802 				// set the original text node to its new value
       
  3803 				if (how != CLONE) {
       
  3804 					n = self[START_CONTAINER];
       
  3805 					start = self[START_OFFSET];
       
  3806 					len = self[END_OFFSET] - self[START_OFFSET];
       
  3807 
       
  3808 					if (start === 0 && len >= n.nodeValue.length - 1) {
       
  3809 						n.parentNode.removeChild(n);
       
  3810 					} else {
       
  3811 						n.deleteData(start, len);
       
  3812 					}
       
  3813 
       
  3814 					// Nothing is partially selected, so collapse to start point
       
  3815 					self.collapse(TRUE);
       
  3816 				}
       
  3817 
       
  3818 				if (how == DELETE) {
       
  3819 					return;
       
  3820 				}
       
  3821 
       
  3822 				if (sub.length > 0) {
       
  3823 					frag.appendChild(doc.createTextNode(sub));
       
  3824 				}
       
  3825 
       
  3826 				return frag;
       
  3827 			}
       
  3828 
       
  3829 			// Copy nodes between the start/end offsets.
       
  3830 			n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
       
  3831 			cnt = self[END_OFFSET] - self[START_OFFSET];
       
  3832 
       
  3833 			while (n && cnt > 0) {
       
  3834 				sibling = n.nextSibling;
       
  3835 				xferNode = _traverseFullySelected(n, how);
       
  3836 
       
  3837 				if (frag) {
       
  3838 					frag.appendChild(xferNode);
       
  3839 				}
       
  3840 
       
  3841 				--cnt;
       
  3842 				n = sibling;
       
  3843 			}
       
  3844 
       
  3845 			// Nothing is partially selected, so collapse to start point
       
  3846 			if (how != CLONE) {
       
  3847 				self.collapse(TRUE);
       
  3848 			}
       
  3849 
       
  3850 			return frag;
       
  3851 		}
       
  3852 
       
  3853 		function _traverseCommonStartContainer(endAncestor, how) {
       
  3854 			var frag, n, endIdx, cnt, sibling, xferNode;
       
  3855 
       
  3856 			if (how != DELETE) {
       
  3857 				frag = createDocumentFragment();
       
  3858 			}
       
  3859 
       
  3860 			n = _traverseRightBoundary(endAncestor, how);
       
  3861 
       
  3862 			if (frag) {
       
  3863 				frag.appendChild(n);
       
  3864 			}
       
  3865 
       
  3866 			endIdx = nodeIndex(endAncestor);
       
  3867 			cnt = endIdx - self[START_OFFSET];
       
  3868 
       
  3869 			if (cnt <= 0) {
       
  3870 				// Collapse to just before the endAncestor, which
       
  3871 				// is partially selected.
       
  3872 				if (how != CLONE) {
       
  3873 					self.setEndBefore(endAncestor);
       
  3874 					self.collapse(FALSE);
       
  3875 				}
       
  3876 
       
  3877 				return frag;
       
  3878 			}
       
  3879 
       
  3880 			n = endAncestor.previousSibling;
       
  3881 			while (cnt > 0) {
       
  3882 				sibling = n.previousSibling;
       
  3883 				xferNode = _traverseFullySelected(n, how);
       
  3884 
       
  3885 				if (frag) {
       
  3886 					frag.insertBefore(xferNode, frag.firstChild);
       
  3887 				}
       
  3888 
       
  3889 				--cnt;
       
  3890 				n = sibling;
       
  3891 			}
       
  3892 
       
  3893 			// Collapse to just before the endAncestor, which
       
  3894 			// is partially selected.
       
  3895 			if (how != CLONE) {
       
  3896 				self.setEndBefore(endAncestor);
       
  3897 				self.collapse(FALSE);
       
  3898 			}
       
  3899 
       
  3900 			return frag;
       
  3901 		}
       
  3902 
       
  3903 		function _traverseCommonEndContainer(startAncestor, how) {
       
  3904 			var frag, startIdx, n, cnt, sibling, xferNode;
       
  3905 
       
  3906 			if (how != DELETE) {
       
  3907 				frag = createDocumentFragment();
       
  3908 			}
       
  3909 
       
  3910 			n = _traverseLeftBoundary(startAncestor, how);
       
  3911 			if (frag) {
       
  3912 				frag.appendChild(n);
       
  3913 			}
       
  3914 
       
  3915 			startIdx = nodeIndex(startAncestor);
       
  3916 			++startIdx; // Because we already traversed it
       
  3917 
       
  3918 			cnt = self[END_OFFSET] - startIdx;
       
  3919 			n = startAncestor.nextSibling;
       
  3920 			while (n && cnt > 0) {
       
  3921 				sibling = n.nextSibling;
       
  3922 				xferNode = _traverseFullySelected(n, how);
       
  3923 
       
  3924 				if (frag) {
       
  3925 					frag.appendChild(xferNode);
       
  3926 				}
       
  3927 
       
  3928 				--cnt;
       
  3929 				n = sibling;
       
  3930 			}
       
  3931 
       
  3932 			if (how != CLONE) {
       
  3933 				self.setStartAfter(startAncestor);
       
  3934 				self.collapse(TRUE);
       
  3935 			}
       
  3936 
       
  3937 			return frag;
       
  3938 		}
       
  3939 
       
  3940 		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
       
  3941 			var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
       
  3942 
       
  3943 			if (how != DELETE) {
       
  3944 				frag = createDocumentFragment();
       
  3945 			}
       
  3946 
       
  3947 			n = _traverseLeftBoundary(startAncestor, how);
       
  3948 			if (frag) {
       
  3949 				frag.appendChild(n);
       
  3950 			}
       
  3951 
       
  3952 			startOffset = nodeIndex(startAncestor);
       
  3953 			endOffset = nodeIndex(endAncestor);
       
  3954 			++startOffset;
       
  3955 
       
  3956 			cnt = endOffset - startOffset;
       
  3957 			sibling = startAncestor.nextSibling;
       
  3958 
       
  3959 			while (cnt > 0) {
       
  3960 				nextSibling = sibling.nextSibling;
       
  3961 				n = _traverseFullySelected(sibling, how);
       
  3962 
       
  3963 				if (frag) {
       
  3964 					frag.appendChild(n);
       
  3965 				}
       
  3966 
       
  3967 				sibling = nextSibling;
       
  3968 				--cnt;
       
  3969 			}
       
  3970 
       
  3971 			n = _traverseRightBoundary(endAncestor, how);
       
  3972 
       
  3973 			if (frag) {
       
  3974 				frag.appendChild(n);
       
  3975 			}
       
  3976 
       
  3977 			if (how != CLONE) {
       
  3978 				self.setStartAfter(startAncestor);
       
  3979 				self.collapse(TRUE);
       
  3980 			}
       
  3981 
       
  3982 			return frag;
       
  3983 		}
       
  3984 
       
  3985 		function _traverseRightBoundary(root, how) {
       
  3986 			var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
       
  3987 			var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
       
  3988 
       
  3989 			if (next == root) {
       
  3990 				return _traverseNode(next, isFullySelected, FALSE, how);
       
  3991 			}
       
  3992 
       
  3993 			parent = next.parentNode;
       
  3994 			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
       
  3995 
       
  3996 			while (parent) {
       
  3997 				while (next) {
       
  3998 					prevSibling = next.previousSibling;
       
  3999 					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
       
  4000 
       
  4001 					if (how != DELETE) {
       
  4002 						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
       
  4003 					}
       
  4004 
       
  4005 					isFullySelected = TRUE;
       
  4006 					next = prevSibling;
       
  4007 				}
       
  4008 
       
  4009 				if (parent == root) {
       
  4010 					return clonedParent;
       
  4011 				}
       
  4012 
       
  4013 				next = parent.previousSibling;
       
  4014 				parent = parent.parentNode;
       
  4015 
       
  4016 				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
       
  4017 
       
  4018 				if (how != DELETE) {
       
  4019 					clonedGrandParent.appendChild(clonedParent);
       
  4020 				}
       
  4021 
       
  4022 				clonedParent = clonedGrandParent;
       
  4023 			}
       
  4024 		}
       
  4025 
       
  4026 		function _traverseLeftBoundary(root, how) {
       
  4027 			var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
       
  4028 			var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
       
  4029 
       
  4030 			if (next == root) {
       
  4031 				return _traverseNode(next, isFullySelected, TRUE, how);
       
  4032 			}
       
  4033 
       
  4034 			parent = next.parentNode;
       
  4035 			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
       
  4036 
       
  4037 			while (parent) {
       
  4038 				while (next) {
       
  4039 					nextSibling = next.nextSibling;
       
  4040 					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
       
  4041 
       
  4042 					if (how != DELETE) {
       
  4043 						clonedParent.appendChild(clonedChild);
       
  4044 					}
       
  4045 
       
  4046 					isFullySelected = TRUE;
       
  4047 					next = nextSibling;
       
  4048 				}
       
  4049 
       
  4050 				if (parent == root) {
       
  4051 					return clonedParent;
       
  4052 				}
       
  4053 
       
  4054 				next = parent.nextSibling;
       
  4055 				parent = parent.parentNode;
       
  4056 
       
  4057 				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
       
  4058 
       
  4059 				if (how != DELETE) {
       
  4060 					clonedGrandParent.appendChild(clonedParent);
       
  4061 				}
       
  4062 
       
  4063 				clonedParent = clonedGrandParent;
       
  4064 			}
       
  4065 		}
       
  4066 
       
  4067 		function _traverseNode(n, isFullySelected, isLeft, how) {
       
  4068 			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
       
  4069 
       
  4070 			if (isFullySelected) {
       
  4071 				return _traverseFullySelected(n, how);
       
  4072 			}
       
  4073 
       
  4074 			if (n.nodeType == 3 /* TEXT_NODE */) {
       
  4075 				txtValue = n.nodeValue;
       
  4076 
       
  4077 				if (isLeft) {
       
  4078 					offset = self[START_OFFSET];
       
  4079 					newNodeValue = txtValue.substring(offset);
       
  4080 					oldNodeValue = txtValue.substring(0, offset);
       
  4081 				} else {
       
  4082 					offset = self[END_OFFSET];
       
  4083 					newNodeValue = txtValue.substring(0, offset);
       
  4084 					oldNodeValue = txtValue.substring(offset);
       
  4085 				}
       
  4086 
       
  4087 				if (how != CLONE) {
       
  4088 					n.nodeValue = oldNodeValue;
       
  4089 				}
       
  4090 
       
  4091 				if (how == DELETE) {
       
  4092 					return;
       
  4093 				}
       
  4094 
       
  4095 				newNode = dom.clone(n, FALSE);
       
  4096 				newNode.nodeValue = newNodeValue;
       
  4097 
       
  4098 				return newNode;
       
  4099 			}
       
  4100 
       
  4101 			if (how == DELETE) {
       
  4102 				return;
       
  4103 			}
       
  4104 
       
  4105 			return dom.clone(n, FALSE);
       
  4106 		}
       
  4107 
       
  4108 		function _traverseFullySelected(n, how) {
       
  4109 			if (how != DELETE) {
       
  4110 				return how == CLONE ? dom.clone(n, TRUE) : n;
       
  4111 			}
       
  4112 
       
  4113 			n.parentNode.removeChild(n);
       
  4114 		}
       
  4115 
       
  4116 		function toStringIE() {
       
  4117 			return dom.create('body', null, cloneContents()).outerText;
       
  4118 		}
       
  4119 
       
  4120 		extend(self, {
       
  4121 			// Inital states
       
  4122 			startContainer: doc,
       
  4123 			startOffset: 0,
       
  4124 			endContainer: doc,
       
  4125 			endOffset: 0,
       
  4126 			collapsed: TRUE,
       
  4127 			commonAncestorContainer: doc,
       
  4128 
       
  4129 			// Range constants
       
  4130 			START_TO_START: 0,
       
  4131 			START_TO_END: 1,
       
  4132 			END_TO_END: 2,
       
  4133 			END_TO_START: 3,
       
  4134 
       
  4135 			// Public methods
       
  4136 			setStart: setStart,
       
  4137 			setEnd: setEnd,
       
  4138 			setStartBefore: setStartBefore,
       
  4139 			setStartAfter: setStartAfter,
       
  4140 			setEndBefore: setEndBefore,
       
  4141 			setEndAfter: setEndAfter,
       
  4142 			collapse: collapse,
       
  4143 			selectNode: selectNode,
       
  4144 			selectNodeContents: selectNodeContents,
       
  4145 			compareBoundaryPoints: compareBoundaryPoints,
       
  4146 			deleteContents: deleteContents,
       
  4147 			extractContents: extractContents,
       
  4148 			cloneContents: cloneContents,
       
  4149 			insertNode: insertNode,
       
  4150 			surroundContents: surroundContents,
       
  4151 			cloneRange: cloneRange,
       
  4152 			toStringIE: toStringIE
       
  4153 		});
       
  4154 
       
  4155 		return self;
       
  4156 	}
       
  4157 
       
  4158 	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
       
  4159 	Range.prototype.toString = function() {
       
  4160 		return this.toStringIE();
       
  4161 	};
       
  4162 
       
  4163 	return Range;
       
  4164 });
       
  4165 
       
  4166 // Included from: js/tinymce/classes/html/Entities.js
       
  4167 
       
  4168 /**
       
  4169  * Entities.js
       
  4170  *
       
  4171  * Copyright, Moxiecode Systems AB
       
  4172  * Released under LGPL License.
       
  4173  *
       
  4174  * License: http://www.tinymce.com/license
       
  4175  * Contributing: http://www.tinymce.com/contributing
       
  4176  */
       
  4177 
       
  4178 /*jshint bitwise:false */
       
  4179 /*eslint no-bitwise:0 */
       
  4180 
       
  4181 /**
       
  4182  * Entity encoder class.
       
  4183  *
       
  4184  * @class tinymce.html.Entities
       
  4185  * @static
       
  4186  * @version 3.4
       
  4187  */
       
  4188 define("tinymce/html/Entities", [
       
  4189 	"tinymce/util/Tools"
       
  4190 ], function(Tools) {
       
  4191 	var makeMap = Tools.makeMap;
       
  4192 
       
  4193 	var namedEntities, baseEntities, reverseEntities,
       
  4194 		attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
       
  4195 		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
       
  4196 		rawCharsRegExp = /[<>&\"\']/g,
       
  4197 		entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
       
  4198 		asciiMap = {
       
  4199 			128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
       
  4200 			135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
       
  4201 			142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
       
  4202 			150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
       
  4203 			156: "\u0153", 158: "\u017E", 159: "\u0178"
       
  4204 		};
       
  4205 
       
  4206 	// Raw entities
       
  4207 	baseEntities = {
       
  4208 		'\"': '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code
       
  4209 		"'": '&#39;',
       
  4210 		'<': '&lt;',
       
  4211 		'>': '&gt;',
       
  4212 		'&': '&amp;',
       
  4213 		'\u0060': '&#96;'
       
  4214 	};
       
  4215 
       
  4216 	// Reverse lookup table for raw entities
       
  4217 	reverseEntities = {
       
  4218 		'&lt;': '<',
       
  4219 		'&gt;': '>',
       
  4220 		'&amp;': '&',
       
  4221 		'&quot;': '"',
       
  4222 		'&apos;': "'"
       
  4223 	};
       
  4224 
       
  4225 	// Decodes text by using the browser
       
  4226 	function nativeDecode(text) {
       
  4227 		var elm;
       
  4228 
       
  4229 		elm = document.createElement("div");
       
  4230 		elm.innerHTML = text;
       
  4231 
       
  4232 		return elm.textContent || elm.innerText || text;
       
  4233 	}
       
  4234 
       
  4235 	// Build a two way lookup table for the entities
       
  4236 	function buildEntitiesLookup(items, radix) {
       
  4237 		var i, chr, entity, lookup = {};
       
  4238 
       
  4239 		if (items) {
       
  4240 			items = items.split(',');
       
  4241 			radix = radix || 10;
       
  4242 
       
  4243 			// Build entities lookup table
       
  4244 			for (i = 0; i < items.length; i += 2) {
       
  4245 				chr = String.fromCharCode(parseInt(items[i], radix));
       
  4246 
       
  4247 				// Only add non base entities
       
  4248 				if (!baseEntities[chr]) {
       
  4249 					entity = '&' + items[i + 1] + ';';
       
  4250 					lookup[chr] = entity;
       
  4251 					lookup[entity] = chr;
       
  4252 				}
       
  4253 			}
       
  4254 
       
  4255 			return lookup;
       
  4256 		}
       
  4257 	}
       
  4258 
       
  4259 	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
       
  4260 	namedEntities = buildEntitiesLookup(
       
  4261 		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
       
  4262 		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
       
  4263 		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
       
  4264 		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
       
  4265 		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
       
  4266 		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
       
  4267 		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
       
  4268 		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
       
  4269 		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
       
  4270 		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
       
  4271 		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
       
  4272 		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
       
  4273 		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
       
  4274 		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
       
  4275 		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
       
  4276 		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
       
  4277 		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
       
  4278 		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
       
  4279 		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
       
  4280 		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
       
  4281 		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
       
  4282 		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
       
  4283 		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
       
  4284 		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
       
  4285 		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
       
  4286 
       
  4287 	var Entities = {
       
  4288 		/**
       
  4289 		 * Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
       
  4290 		 *
       
  4291 		 * @method encodeRaw
       
  4292 		 * @param {String} text Text to encode.
       
  4293 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
       
  4294 		 * @return {String} Entity encoded text.
       
  4295 		 */
       
  4296 		encodeRaw: function(text, attr) {
       
  4297 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  4298 				return baseEntities[chr] || chr;
       
  4299 			});
       
  4300 		},
       
  4301 
       
  4302 		/**
       
  4303 		 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
       
  4304 		 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
       
  4305 		 * and is exposed as the DOMUtils.encode function.
       
  4306 		 *
       
  4307 		 * @method encodeAllRaw
       
  4308 		 * @param {String} text Text to encode.
       
  4309 		 * @return {String} Entity encoded text.
       
  4310 		 */
       
  4311 		encodeAllRaw: function(text) {
       
  4312 			return ('' + text).replace(rawCharsRegExp, function(chr) {
       
  4313 				return baseEntities[chr] || chr;
       
  4314 			});
       
  4315 		},
       
  4316 
       
  4317 		/**
       
  4318 		 * Encodes the specified string using numeric entities. The core entities will be
       
  4319 		 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
       
  4320 		 *
       
  4321 		 * @method encodeNumeric
       
  4322 		 * @param {String} text Text to encode.
       
  4323 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
       
  4324 		 * @return {String} Entity encoded text.
       
  4325 		 */
       
  4326 		encodeNumeric: function(text, attr) {
       
  4327 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  4328 				// Multi byte sequence convert it to a single entity
       
  4329 				if (chr.length > 1) {
       
  4330 					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
       
  4331 				}
       
  4332 
       
  4333 				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
       
  4334 			});
       
  4335 		},
       
  4336 
       
  4337 		/**
       
  4338 		 * Encodes the specified string using named entities. The core entities will be encoded
       
  4339 		 * as named ones but all non lower ascii characters will be encoded into named entities.
       
  4340 		 *
       
  4341 		 * @method encodeNamed
       
  4342 		 * @param {String} text Text to encode.
       
  4343 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
       
  4344 		 * @param {Object} entities Optional parameter with entities to use.
       
  4345 		 * @return {String} Entity encoded text.
       
  4346 		 */
       
  4347 		encodeNamed: function(text, attr, entities) {
       
  4348 			entities = entities || namedEntities;
       
  4349 
       
  4350 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  4351 				return baseEntities[chr] || entities[chr] || chr;
       
  4352 			});
       
  4353 		},
       
  4354 
       
  4355 		/**
       
  4356 		 * Returns an encode function based on the name(s) and it's optional entities.
       
  4357 		 *
       
  4358 		 * @method getEncodeFunc
       
  4359 		 * @param {String} name Comma separated list of encoders for example named,numeric.
       
  4360 		 * @param {String} entities Optional parameter with entities to use instead of the built in set.
       
  4361 		 * @return {function} Encode function to be used.
       
  4362 		 */
       
  4363 		getEncodeFunc: function(name, entities) {
       
  4364 			entities = buildEntitiesLookup(entities) || namedEntities;
       
  4365 
       
  4366 			function encodeNamedAndNumeric(text, attr) {
       
  4367 				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
       
  4368 					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
       
  4369 				});
       
  4370 			}
       
  4371 
       
  4372 			function encodeCustomNamed(text, attr) {
       
  4373 				return Entities.encodeNamed(text, attr, entities);
       
  4374 			}
       
  4375 
       
  4376 			// Replace + with , to be compatible with previous TinyMCE versions
       
  4377 			name = makeMap(name.replace(/\+/g, ','));
       
  4378 
       
  4379 			// Named and numeric encoder
       
  4380 			if (name.named && name.numeric) {
       
  4381 				return encodeNamedAndNumeric;
       
  4382 			}
       
  4383 
       
  4384 			// Named encoder
       
  4385 			if (name.named) {
       
  4386 				// Custom names
       
  4387 				if (entities) {
       
  4388 					return encodeCustomNamed;
       
  4389 				}
       
  4390 
       
  4391 				return Entities.encodeNamed;
       
  4392 			}
       
  4393 
       
  4394 			// Numeric
       
  4395 			if (name.numeric) {
       
  4396 				return Entities.encodeNumeric;
       
  4397 			}
       
  4398 
       
  4399 			// Raw encoder
       
  4400 			return Entities.encodeRaw;
       
  4401 		},
       
  4402 
       
  4403 		/**
       
  4404 		 * Decodes the specified string, this will replace entities with raw UTF characters.
       
  4405 		 *
       
  4406 		 * @method decode
       
  4407 		 * @param {String} text Text to entity decode.
       
  4408 		 * @return {String} Entity decoded string.
       
  4409 		 */
       
  4410 		decode: function(text) {
       
  4411 			return text.replace(entityRegExp, function(all, numeric) {
       
  4412 				if (numeric) {
       
  4413 					if (numeric.charAt(0).toLowerCase() === 'x') {
       
  4414 						numeric = parseInt(numeric.substr(1), 16);
       
  4415 					} else {
       
  4416 						numeric = parseInt(numeric, 10);
       
  4417 					}
       
  4418 
       
  4419 					// Support upper UTF
       
  4420 					if (numeric > 0xFFFF) {
       
  4421 						numeric -= 0x10000;
       
  4422 
       
  4423 						return String.fromCharCode(0xD800 + (numeric >> 10), 0xDC00 + (numeric & 0x3FF));
       
  4424 					} else {
       
  4425 						return asciiMap[numeric] || String.fromCharCode(numeric);
       
  4426 					}
       
  4427 				}
       
  4428 
       
  4429 				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
       
  4430 			});
       
  4431 		}
       
  4432 	};
       
  4433 
       
  4434 	return Entities;
       
  4435 });
       
  4436 
       
  4437 // Included from: js/tinymce/classes/dom/StyleSheetLoader.js
       
  4438 
       
  4439 /**
       
  4440  * StyleSheetLoader.js
       
  4441  *
       
  4442  * Copyright, Moxiecode Systems AB
       
  4443  * Released under LGPL License.
       
  4444  *
       
  4445  * License: http://www.tinymce.com/license
       
  4446  * Contributing: http://www.tinymce.com/contributing
       
  4447  */
       
  4448 
       
  4449 /**
       
  4450  * This class handles loading of external stylesheets and fires events when these are loaded.
       
  4451  *
       
  4452  * @class tinymce.dom.StyleSheetLoader
       
  4453  * @private
       
  4454  */
       
  4455 define("tinymce/dom/StyleSheetLoader", [
       
  4456 	"tinymce/util/Tools"
       
  4457 ], function(Tools) {
       
  4458 	"use strict";
       
  4459 
       
  4460 	return function(document, settings) {
       
  4461 		var idCount = 0, loadedStates = {}, maxLoadTime;
       
  4462 
       
  4463 		settings = settings || {};
       
  4464 		maxLoadTime = settings.maxLoadTime || 5000;
       
  4465 
       
  4466 		function appendToHead(node) {
       
  4467 			document.getElementsByTagName('head')[0].appendChild(node);
       
  4468 		}
       
  4469 
       
  4470 		/**
       
  4471 		 * Loads the specified css style sheet file and call the loadedCallback once it's finished loading.
       
  4472 		 *
       
  4473 		 * @method load
       
  4474 		 * @param {String} url Url to be loaded.
       
  4475 		 * @param {Function} loadedCallback Callback to be executed when loaded.
       
  4476 		 * @param {Function} errorCallback Callback to be executed when failed loading.
       
  4477 		 */
       
  4478 		function load(url, loadedCallback, errorCallback) {
       
  4479 			var link, style, startTime, state;
       
  4480 
       
  4481 			function passed() {
       
  4482 				var callbacks = state.passed, i = callbacks.length;
       
  4483 
       
  4484 				while (i--) {
       
  4485 					callbacks[i]();
       
  4486 				}
       
  4487 
       
  4488 				state.status = 2;
       
  4489 				state.passed = [];
       
  4490 				state.failed = [];
       
  4491 			}
       
  4492 
       
  4493 			function failed() {
       
  4494 				var callbacks = state.failed, i = callbacks.length;
       
  4495 
       
  4496 				while (i--) {
       
  4497 					callbacks[i]();
       
  4498 				}
       
  4499 
       
  4500 				state.status = 3;
       
  4501 				state.passed = [];
       
  4502 				state.failed = [];
       
  4503 			}
       
  4504 
       
  4505 			// Sniffs for older WebKit versions that have the link.onload but a broken one
       
  4506 			function isOldWebKit() {
       
  4507 				var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/);
       
  4508 				return !!(webKitChunks && webKitChunks[1] < 536);
       
  4509 			}
       
  4510 
       
  4511 			// Calls the waitCallback until the test returns true or the timeout occurs
       
  4512 			function wait(testCallback, waitCallback) {
       
  4513 				if (!testCallback()) {
       
  4514 					// Wait for timeout
       
  4515 					if ((new Date().getTime()) - startTime < maxLoadTime) {
       
  4516 						window.setTimeout(waitCallback, 0);
       
  4517 					} else {
       
  4518 						failed();
       
  4519 					}
       
  4520 				}
       
  4521 			}
       
  4522 
       
  4523 			// Workaround for WebKit that doesn't properly support the onload event for link elements
       
  4524 			// Or WebKit that fires the onload event before the StyleSheet is added to the document
       
  4525 			function waitForWebKitLinkLoaded() {
       
  4526 				wait(function() {
       
  4527 					var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner;
       
  4528 
       
  4529 					while (i--) {
       
  4530 						styleSheet = styleSheets[i];
       
  4531 						owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement;
       
  4532 						if (owner && owner.id === link.id) {
       
  4533 							passed();
       
  4534 							return true;
       
  4535 						}
       
  4536 					}
       
  4537 				}, waitForWebKitLinkLoaded);
       
  4538 			}
       
  4539 
       
  4540 			// Workaround for older Geckos that doesn't have any onload event for StyleSheets
       
  4541 			function waitForGeckoLinkLoaded() {
       
  4542 				wait(function() {
       
  4543 					try {
       
  4544 						// Accessing the cssRules will throw an exception until the CSS file is loaded
       
  4545 						var cssRules = style.sheet.cssRules;
       
  4546 						passed();
       
  4547 						return !!cssRules;
       
  4548 					} catch (ex) {
       
  4549 						// Ignore
       
  4550 					}
       
  4551 				}, waitForGeckoLinkLoaded);
       
  4552 			}
       
  4553 
       
  4554 			url = Tools._addCacheSuffix(url);
       
  4555 
       
  4556 			if (!loadedStates[url]) {
       
  4557 				state = {
       
  4558 					passed: [],
       
  4559 					failed: []
       
  4560 				};
       
  4561 
       
  4562 				loadedStates[url] = state;
       
  4563 			} else {
       
  4564 				state = loadedStates[url];
       
  4565 			}
       
  4566 
       
  4567 			if (loadedCallback) {
       
  4568 				state.passed.push(loadedCallback);
       
  4569 			}
       
  4570 
       
  4571 			if (errorCallback) {
       
  4572 				state.failed.push(errorCallback);
       
  4573 			}
       
  4574 
       
  4575 			// Is loading wait for it to pass
       
  4576 			if (state.status == 1) {
       
  4577 				return;
       
  4578 			}
       
  4579 
       
  4580 			// Has finished loading and was success
       
  4581 			if (state.status == 2) {
       
  4582 				passed();
       
  4583 				return;
       
  4584 			}
       
  4585 
       
  4586 			// Has finished loading and was a failure
       
  4587 			if (state.status == 3) {
       
  4588 				failed();
       
  4589 				return;
       
  4590 			}
       
  4591 
       
  4592 			// Start loading
       
  4593 			state.status = 1;
       
  4594 			link = document.createElement('link');
       
  4595 			link.rel = 'stylesheet';
       
  4596 			link.type = 'text/css';
       
  4597 			link.id = 'u' + (idCount++);
       
  4598 			link.async = false;
       
  4599 			link.defer = false;
       
  4600 			startTime = new Date().getTime();
       
  4601 
       
  4602 			// Feature detect onload on link element and sniff older webkits since it has an broken onload event
       
  4603 			if ("onload" in link && !isOldWebKit()) {
       
  4604 				link.onload = waitForWebKitLinkLoaded;
       
  4605 				link.onerror = failed;
       
  4606 			} else {
       
  4607 				// Sniff for old Firefox that doesn't support the onload event on link elements
       
  4608 				// TODO: Remove this in the future when everyone uses modern browsers
       
  4609 				if (navigator.userAgent.indexOf("Firefox") > 0) {
       
  4610 					style = document.createElement('style');
       
  4611 					style.textContent = '@import "' + url + '"';
       
  4612 					waitForGeckoLinkLoaded();
       
  4613 					appendToHead(style);
       
  4614 					return;
       
  4615 				} else {
       
  4616 					// Use the id owner on older webkits
       
  4617 					waitForWebKitLinkLoaded();
       
  4618 				}
       
  4619 			}
       
  4620 
       
  4621 			appendToHead(link);
       
  4622 			link.href = url;
       
  4623 		}
       
  4624 
       
  4625 		this.load = load;
       
  4626 	};
       
  4627 });
       
  4628 
       
  4629 // Included from: js/tinymce/classes/dom/DOMUtils.js
       
  4630 
       
  4631 /**
       
  4632  * DOMUtils.js
       
  4633  *
       
  4634  * Copyright, Moxiecode Systems AB
       
  4635  * Released under LGPL License.
       
  4636  *
       
  4637  * License: http://www.tinymce.com/license
       
  4638  * Contributing: http://www.tinymce.com/contributing
       
  4639  */
       
  4640 
       
  4641 /**
       
  4642  * Utility class for various DOM manipulation and retrieval functions.
       
  4643  *
       
  4644  * @class tinymce.dom.DOMUtils
       
  4645  * @example
       
  4646  * // Add a class to an element by id in the page
       
  4647  * tinymce.DOM.addClass('someid', 'someclass');
       
  4648  *
       
  4649  * // Add a class to an element by id inside the editor
       
  4650  * tinymce.activeEditor.dom.addClass('someid', 'someclass');
       
  4651  */
       
  4652 define("tinymce/dom/DOMUtils", [
       
  4653 	"tinymce/dom/Sizzle",
       
  4654 	"tinymce/dom/DomQuery",
       
  4655 	"tinymce/html/Styles",
       
  4656 	"tinymce/dom/EventUtils",
       
  4657 	"tinymce/dom/TreeWalker",
       
  4658 	"tinymce/dom/Range",
       
  4659 	"tinymce/html/Entities",
       
  4660 	"tinymce/Env",
       
  4661 	"tinymce/util/Tools",
       
  4662 	"tinymce/dom/StyleSheetLoader"
       
  4663 ], function(Sizzle, $, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools, StyleSheetLoader) {
       
  4664 	// Shorten names
       
  4665 	var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim;
       
  4666 	var isIE = Env.ie;
       
  4667 	var simpleSelectorRe = /^([a-z0-9],?)+$/i;
       
  4668 	var whiteSpaceRegExp = /^[ \t\r\n]*$/;
       
  4669 
       
  4670 	function setupAttrHooks(domUtils, settings) {
       
  4671 		var attrHooks = {}, keepValues = settings.keep_values, keepUrlHook;
       
  4672 
       
  4673 		keepUrlHook = {
       
  4674 			set: function($elm, value, name) {
       
  4675 				if (settings.url_converter) {
       
  4676 					value = settings.url_converter.call(settings.url_converter_scope || domUtils, value, name, $elm[0]);
       
  4677 				}
       
  4678 
       
  4679 				$elm.attr('data-mce-' + name, value).attr(name, value);
       
  4680 			},
       
  4681 
       
  4682 			get: function($elm, name) {
       
  4683 				return $elm.attr('data-mce-' + name) || $elm.attr(name);
       
  4684 			}
       
  4685 		};
       
  4686 
       
  4687 		attrHooks = {
       
  4688 			style: {
       
  4689 				set: function($elm, value) {
       
  4690 					if (value !== null && typeof value === 'object') {
       
  4691 						$elm.css(value);
       
  4692 						return;
       
  4693 					}
       
  4694 
       
  4695 					if (keepValues) {
       
  4696 						$elm.attr('data-mce-style', value);
       
  4697 					}
       
  4698 
       
  4699 					$elm.attr('style', value);
       
  4700 				},
       
  4701 
       
  4702 				get: function($elm) {
       
  4703 					var value = $elm.attr('data-mce-style') || $elm.attr('style');
       
  4704 
       
  4705 					value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
       
  4706 
       
  4707 					return value;
       
  4708 				}
       
  4709 			}
       
  4710 		};
       
  4711 
       
  4712 		if (keepValues) {
       
  4713 			attrHooks.href = attrHooks.src = keepUrlHook;
       
  4714 		}
       
  4715 
       
  4716 		return attrHooks;
       
  4717 	}
       
  4718 
       
  4719 	/**
       
  4720 	 * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
       
  4721 	 *
       
  4722 	 * @constructor
       
  4723 	 * @method DOMUtils
       
  4724 	 * @param {Document} d Document reference to bind the utility class to.
       
  4725 	 * @param {settings} s Optional settings collection.
       
  4726 	 */
       
  4727 	function DOMUtils(doc, settings) {
       
  4728 		var self = this, blockElementsMap;
       
  4729 
       
  4730 		self.doc = doc;
       
  4731 		self.win = window;
       
  4732 		self.files = {};
       
  4733 		self.counter = 0;
       
  4734 		self.stdMode = !isIE || doc.documentMode >= 8;
       
  4735 		self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
       
  4736 		self.styleSheetLoader = new StyleSheetLoader(doc);
       
  4737 		self.boundEvents = [];
       
  4738 		self.settings = settings = settings || {};
       
  4739 		self.schema = settings.schema;
       
  4740 		self.styles = new Styles({
       
  4741 			url_converter: settings.url_converter,
       
  4742 			url_converter_scope: settings.url_converter_scope
       
  4743 		}, settings.schema);
       
  4744 
       
  4745 		self.fixDoc(doc);
       
  4746 		self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
       
  4747 		self.attrHooks = setupAttrHooks(self, settings);
       
  4748 		blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
       
  4749 		self.$ = $.overrideDefaults(function() {
       
  4750 			return {
       
  4751 				context: doc,
       
  4752 				element: self.getRoot()
       
  4753 			};
       
  4754 		});
       
  4755 
       
  4756 		/**
       
  4757 		 * Returns true/false if the specified element is a block element or not.
       
  4758 		 *
       
  4759 		 * @method isBlock
       
  4760 		 * @param {Node/String} node Element/Node to check.
       
  4761 		 * @return {Boolean} True/False state if the node is a block element or not.
       
  4762 		 */
       
  4763 		self.isBlock = function(node) {
       
  4764 			// Fix for #5446
       
  4765 			if (!node) {
       
  4766 				return false;
       
  4767 			}
       
  4768 
       
  4769 			// This function is called in module pattern style since it might be executed with the wrong this scope
       
  4770 			var type = node.nodeType;
       
  4771 
       
  4772 			// If it's a node then check the type and use the nodeName
       
  4773 			if (type) {
       
  4774 				return !!(type === 1 && blockElementsMap[node.nodeName]);
       
  4775 			}
       
  4776 
       
  4777 			return !!blockElementsMap[node];
       
  4778 		};
       
  4779 	}
       
  4780 
       
  4781 	DOMUtils.prototype = {
       
  4782 		$$: function(elm) {
       
  4783 			if (typeof elm == 'string') {
       
  4784 				elm = this.get(elm);
       
  4785 			}
       
  4786 
       
  4787 			return this.$(elm);
       
  4788 		},
       
  4789 
       
  4790 		root: null,
       
  4791 
       
  4792 		fixDoc: function(doc) {
       
  4793 			var settings = this.settings, name;
       
  4794 
       
  4795 			if (isIE && settings.schema) {
       
  4796 				// Add missing HTML 4/5 elements to IE
       
  4797 				('abbr article aside audio canvas ' +
       
  4798 				'details figcaption figure footer ' +
       
  4799 				'header hgroup mark menu meter nav ' +
       
  4800 				'output progress section summary ' +
       
  4801 				'time video').replace(/\w+/g, function(name) {
       
  4802 					doc.createElement(name);
       
  4803 				});
       
  4804 
       
  4805 				// Create all custom elements
       
  4806 				for (name in settings.schema.getCustomElements()) {
       
  4807 					doc.createElement(name);
       
  4808 				}
       
  4809 			}
       
  4810 		},
       
  4811 
       
  4812 		clone: function(node, deep) {
       
  4813 			var self = this, clone, doc;
       
  4814 
       
  4815 			// TODO: Add feature detection here in the future
       
  4816 			if (!isIE || node.nodeType !== 1 || deep) {
       
  4817 				return node.cloneNode(deep);
       
  4818 			}
       
  4819 
       
  4820 			doc = self.doc;
       
  4821 
       
  4822 			// Make a HTML5 safe shallow copy
       
  4823 			if (!deep) {
       
  4824 				clone = doc.createElement(node.nodeName);
       
  4825 
       
  4826 				// Copy attribs
       
  4827 				each(self.getAttribs(node), function(attr) {
       
  4828 					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
       
  4829 				});
       
  4830 
       
  4831 				return clone;
       
  4832 			}
       
  4833 
       
  4834 			return clone.firstChild;
       
  4835 		},
       
  4836 
       
  4837 		/**
       
  4838 		 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
       
  4839 		 * go above the point of this root node.
       
  4840 		 *
       
  4841 		 * @method getRoot
       
  4842 		 * @return {Element} Root element for the utility class.
       
  4843 		 */
       
  4844 		getRoot: function() {
       
  4845 			var self = this;
       
  4846 
       
  4847 			return self.settings.root_element || self.doc.body;
       
  4848 		},
       
  4849 
       
  4850 		/**
       
  4851 		 * Returns the viewport of the window.
       
  4852 		 *
       
  4853 		 * @method getViewPort
       
  4854 		 * @param {Window} win Optional window to get viewport of.
       
  4855 		 * @return {Object} Viewport object with fields x, y, w and h.
       
  4856 		 */
       
  4857 		getViewPort: function(win) {
       
  4858 			var doc, rootElm;
       
  4859 
       
  4860 			win = !win ? this.win : win;
       
  4861 			doc = win.document;
       
  4862 			rootElm = this.boxModel ? doc.documentElement : doc.body;
       
  4863 
       
  4864 			// Returns viewport size excluding scrollbars
       
  4865 			return {
       
  4866 				x: win.pageXOffset || rootElm.scrollLeft,
       
  4867 				y: win.pageYOffset || rootElm.scrollTop,
       
  4868 				w: win.innerWidth || rootElm.clientWidth,
       
  4869 				h: win.innerHeight || rootElm.clientHeight
       
  4870 			};
       
  4871 		},
       
  4872 
       
  4873 		/**
       
  4874 		 * Returns the rectangle for a specific element.
       
  4875 		 *
       
  4876 		 * @method getRect
       
  4877 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
       
  4878 		 * @return {object} Rectangle for specified element object with x, y, w, h fields.
       
  4879 		 */
       
  4880 		getRect: function(elm) {
       
  4881 			var self = this, pos, size;
       
  4882 
       
  4883 			elm = self.get(elm);
       
  4884 			pos = self.getPos(elm);
       
  4885 			size = self.getSize(elm);
       
  4886 
       
  4887 			return {
       
  4888 				x: pos.x, y: pos.y,
       
  4889 				w: size.w, h: size.h
       
  4890 			};
       
  4891 		},
       
  4892 
       
  4893 		/**
       
  4894 		 * Returns the size dimensions of the specified element.
       
  4895 		 *
       
  4896 		 * @method getSize
       
  4897 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
       
  4898 		 * @return {object} Rectangle for specified element object with w, h fields.
       
  4899 		 */
       
  4900 		getSize: function(elm) {
       
  4901 			var self = this, w, h;
       
  4902 
       
  4903 			elm = self.get(elm);
       
  4904 			w = self.getStyle(elm, 'width');
       
  4905 			h = self.getStyle(elm, 'height');
       
  4906 
       
  4907 			// Non pixel value, then force offset/clientWidth
       
  4908 			if (w.indexOf('px') === -1) {
       
  4909 				w = 0;
       
  4910 			}
       
  4911 
       
  4912 			// Non pixel value, then force offset/clientWidth
       
  4913 			if (h.indexOf('px') === -1) {
       
  4914 				h = 0;
       
  4915 			}
       
  4916 
       
  4917 			return {
       
  4918 				w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
       
  4919 				h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
       
  4920 			};
       
  4921 		},
       
  4922 
       
  4923 		/**
       
  4924 		 * Returns a node by the specified selector function. This function will
       
  4925 		 * loop through all parent nodes and call the specified function for each node.
       
  4926 		 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
       
  4927 		 * and the node it found will be returned.
       
  4928 		 *
       
  4929 		 * @method getParent
       
  4930 		 * @param {Node/String} node DOM node to search parents on or ID string.
       
  4931 		 * @param {function} selector Selection function or CSS selector to execute on each node.
       
  4932 		 * @param {Node} root Optional root element, never go below this point.
       
  4933 		 * @return {Node} DOM Node or null if it wasn't found.
       
  4934 		 */
       
  4935 		getParent: function(node, selector, root) {
       
  4936 			return this.getParents(node, selector, root, false);
       
  4937 		},
       
  4938 
       
  4939 		/**
       
  4940 		 * Returns a node list of all parents matching the specified selector function or pattern.
       
  4941 		 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
       
  4942 		 *
       
  4943 		 * @method getParents
       
  4944 		 * @param {Node/String} node DOM node to search parents on or ID string.
       
  4945 		 * @param {function} selector Selection function to execute on each node or CSS pattern.
       
  4946 		 * @param {Node} root Optional root element, never go below this point.
       
  4947 		 * @return {Array} Array of nodes or null if it wasn't found.
       
  4948 		 */
       
  4949 		getParents: function(node, selector, root, collect) {
       
  4950 			var self = this, selectorVal, result = [];
       
  4951 
       
  4952 			node = self.get(node);
       
  4953 			collect = collect === undefined;
       
  4954 
       
  4955 			// Default root on inline mode
       
  4956 			root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
       
  4957 
       
  4958 			// Wrap node name as func
       
  4959 			if (is(selector, 'string')) {
       
  4960 				selectorVal = selector;
       
  4961 
       
  4962 				if (selector === '*') {
       
  4963 					selector = function(node) {
       
  4964 						return node.nodeType == 1;
       
  4965 					};
       
  4966 				} else {
       
  4967 					selector = function(node) {
       
  4968 						return self.is(node, selectorVal);
       
  4969 					};
       
  4970 				}
       
  4971 			}
       
  4972 
       
  4973 			while (node) {
       
  4974 				if (node == root || !node.nodeType || node.nodeType === 9) {
       
  4975 					break;
       
  4976 				}
       
  4977 
       
  4978 				if (!selector || selector(node)) {
       
  4979 					if (collect) {
       
  4980 						result.push(node);
       
  4981 					} else {
       
  4982 						return node;
       
  4983 					}
       
  4984 				}
       
  4985 
       
  4986 				node = node.parentNode;
       
  4987 			}
       
  4988 
       
  4989 			return collect ? result : null;
       
  4990 		},
       
  4991 
       
  4992 		/**
       
  4993 		 * Returns the specified element by ID or the input element if it isn't a string.
       
  4994 		 *
       
  4995 		 * @method get
       
  4996 		 * @param {String/Element} n Element id to look for or element to just pass though.
       
  4997 		 * @return {Element} Element matching the specified id or null if it wasn't found.
       
  4998 		 */
       
  4999 		get: function(elm) {
       
  5000 			var name;
       
  5001 
       
  5002 			if (elm && this.doc && typeof elm == 'string') {
       
  5003 				name = elm;
       
  5004 				elm = this.doc.getElementById(elm);
       
  5005 
       
  5006 				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
       
  5007 				if (elm && elm.id !== name) {
       
  5008 					return this.doc.getElementsByName(name)[1];
       
  5009 				}
       
  5010 			}
       
  5011 
       
  5012 			return elm;
       
  5013 		},
       
  5014 
       
  5015 		/**
       
  5016 		 * Returns the next node that matches selector or function
       
  5017 		 *
       
  5018 		 * @method getNext
       
  5019 		 * @param {Node} node Node to find siblings from.
       
  5020 		 * @param {String/function} selector Selector CSS expression or function.
       
  5021 		 * @return {Node} Next node item matching the selector or null if it wasn't found.
       
  5022 		 */
       
  5023 		getNext: function(node, selector) {
       
  5024 			return this._findSib(node, selector, 'nextSibling');
       
  5025 		},
       
  5026 
       
  5027 		/**
       
  5028 		 * Returns the previous node that matches selector or function
       
  5029 		 *
       
  5030 		 * @method getPrev
       
  5031 		 * @param {Node} node Node to find siblings from.
       
  5032 		 * @param {String/function} selector Selector CSS expression or function.
       
  5033 		 * @return {Node} Previous node item matching the selector or null if it wasn't found.
       
  5034 		 */
       
  5035 		getPrev: function(node, selector) {
       
  5036 			return this._findSib(node, selector, 'previousSibling');
       
  5037 		},
       
  5038 
       
  5039 		// #ifndef jquery
       
  5040 
       
  5041 		/**
       
  5042 		 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
       
  5043 		 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
       
  5044 		 * on more complex patterns.
       
  5045 		 *
       
  5046 		 * @method select
       
  5047 		 * @param {String} selector CSS level 3 pattern to select/find elements by.
       
  5048 		 * @param {Object} scope Optional root element/scope element to search in.
       
  5049 		 * @return {Array} Array with all matched elements.
       
  5050 		 * @example
       
  5051 		 * // Adds a class to all paragraphs in the currently active editor
       
  5052 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
  5053 		 *
       
  5054 		 * // Adds a class to all spans that have the test class in the currently active editor
       
  5055 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
       
  5056 		 */
       
  5057 		select: function(selector, scope) {
       
  5058 			var self = this;
       
  5059 
       
  5060 			/*eslint new-cap:0 */
       
  5061 			return Sizzle(selector, self.get(scope) || self.settings.root_element || self.doc, []);
       
  5062 		},
       
  5063 
       
  5064 		/**
       
  5065 		 * Returns true/false if the specified element matches the specified css pattern.
       
  5066 		 *
       
  5067 		 * @method is
       
  5068 		 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
       
  5069 		 * @param {String} selector CSS pattern to match the element against.
       
  5070 		 */
       
  5071 		is: function(elm, selector) {
       
  5072 			var i;
       
  5073 
       
  5074 			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
       
  5075 			if (elm.length === undefined) {
       
  5076 				// Simple all selector
       
  5077 				if (selector === '*') {
       
  5078 					return elm.nodeType == 1;
       
  5079 				}
       
  5080 
       
  5081 				// Simple selector just elements
       
  5082 				if (simpleSelectorRe.test(selector)) {
       
  5083 					selector = selector.toLowerCase().split(/,/);
       
  5084 					elm = elm.nodeName.toLowerCase();
       
  5085 
       
  5086 					for (i = selector.length - 1; i >= 0; i--) {
       
  5087 						if (selector[i] == elm) {
       
  5088 							return true;
       
  5089 						}
       
  5090 					}
       
  5091 
       
  5092 					return false;
       
  5093 				}
       
  5094 			}
       
  5095 
       
  5096 			// Is non element
       
  5097 			if (elm.nodeType && elm.nodeType != 1) {
       
  5098 				return false;
       
  5099 			}
       
  5100 
       
  5101 			var elms = elm.nodeType ? [elm] : elm;
       
  5102 
       
  5103 			/*eslint new-cap:0 */
       
  5104 			return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0;
       
  5105 		},
       
  5106 
       
  5107 		// #endif
       
  5108 
       
  5109 		/**
       
  5110 		 * Adds the specified element to another element or elements.
       
  5111 		 *
       
  5112 		 * @method add
       
  5113 		 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
       
  5114 		 * @param {String/Element} name Name of new element to add or existing element to add.
       
  5115 		 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
       
  5116 		 * @param {String} html Optional inner HTML contents to add for each element.
       
  5117 		 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
       
  5118 		 * were passed in.
       
  5119 		 * @example
       
  5120 		 * // Adds a new paragraph to the end of the active editor
       
  5121 		 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
       
  5122 		 */
       
  5123 		add: function(parentElm, name, attrs, html, create) {
       
  5124 			var self = this;
       
  5125 
       
  5126 			return this.run(parentElm, function(parentElm) {
       
  5127 				var newElm;
       
  5128 
       
  5129 				newElm = is(name, 'string') ? self.doc.createElement(name) : name;
       
  5130 				self.setAttribs(newElm, attrs);
       
  5131 
       
  5132 				if (html) {
       
  5133 					if (html.nodeType) {
       
  5134 						newElm.appendChild(html);
       
  5135 					} else {
       
  5136 						self.setHTML(newElm, html);
       
  5137 					}
       
  5138 				}
       
  5139 
       
  5140 				return !create ? parentElm.appendChild(newElm) : newElm;
       
  5141 			});
       
  5142 		},
       
  5143 
       
  5144 		/**
       
  5145 		 * Creates a new element.
       
  5146 		 *
       
  5147 		 * @method create
       
  5148 		 * @param {String} name Name of new element.
       
  5149 		 * @param {Object} attrs Optional object name/value collection with element attributes.
       
  5150 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
       
  5151 		 * @return {Element} HTML DOM node element that got created.
       
  5152 		 * @example
       
  5153 		 * // Adds an element where the caret/selection is in the active editor
       
  5154 		 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
       
  5155 		 * tinymce.activeEditor.selection.setNode(el);
       
  5156 		 */
       
  5157 		create: function(name, attrs, html) {
       
  5158 			return this.add(this.doc.createElement(name), name, attrs, html, 1);
       
  5159 		},
       
  5160 
       
  5161 		/**
       
  5162 		 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
       
  5163 		 *
       
  5164 		 * @method createHTML
       
  5165 		 * @param {String} name Name of new element.
       
  5166 		 * @param {Object} attrs Optional object name/value collection with element attributes.
       
  5167 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
       
  5168 		 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
       
  5169 		 * @example
       
  5170 		 * // Creates a html chunk and inserts it at the current selection/caret location
       
  5171 		 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
       
  5172 		 */
       
  5173 		createHTML: function(name, attrs, html) {
       
  5174 			var outHtml = '', key;
       
  5175 
       
  5176 			outHtml += '<' + name;
       
  5177 
       
  5178 			for (key in attrs) {
       
  5179 				if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') {
       
  5180 					outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
       
  5181 				}
       
  5182 			}
       
  5183 
       
  5184 			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
       
  5185 			if (typeof html != "undefined") {
       
  5186 				return outHtml + '>' + html + '</' + name + '>';
       
  5187 			}
       
  5188 
       
  5189 			return outHtml + ' />';
       
  5190 		},
       
  5191 
       
  5192 		/**
       
  5193 		 * Creates a document fragment out of the specified HTML string.
       
  5194 		 *
       
  5195 		 * @method createFragment
       
  5196 		 * @param {String} html Html string to create fragment from.
       
  5197 		 * @return {DocumentFragment} Document fragment node.
       
  5198 		 */
       
  5199 		createFragment: function(html) {
       
  5200 			var frag, node, doc = this.doc, container;
       
  5201 
       
  5202 			container = doc.createElement("div");
       
  5203 			frag = doc.createDocumentFragment();
       
  5204 
       
  5205 			if (html) {
       
  5206 				container.innerHTML = html;
       
  5207 			}
       
  5208 
       
  5209 			while ((node = container.firstChild)) {
       
  5210 				frag.appendChild(node);
       
  5211 			}
       
  5212 
       
  5213 			return frag;
       
  5214 		},
       
  5215 
       
  5216 		/**
       
  5217 		 * Removes/deletes the specified element(s) from the DOM.
       
  5218 		 *
       
  5219 		 * @method remove
       
  5220 		 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
       
  5221 		 * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be
       
  5222 		 * placed at the location of the removed element.
       
  5223 		 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
       
  5224 		 * were passed in.
       
  5225 		 * @example
       
  5226 		 * // Removes all paragraphs in the active editor
       
  5227 		 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
       
  5228 		 *
       
  5229 		 * // Removes an element by id in the document
       
  5230 		 * tinymce.DOM.remove('mydiv');
       
  5231 		 */
       
  5232 		remove: function(node, keepChildren) {
       
  5233 			node = this.$$(node);
       
  5234 
       
  5235 			if (keepChildren) {
       
  5236 				node.each(function() {
       
  5237 					var child;
       
  5238 
       
  5239 					while ((child = this.firstChild)) {
       
  5240 						if (child.nodeType == 3 && child.data.length === 0) {
       
  5241 							this.removeChild(child);
       
  5242 						} else {
       
  5243 							this.parentNode.insertBefore(child, this);
       
  5244 						}
       
  5245 					}
       
  5246 				}).remove();
       
  5247 			} else {
       
  5248 				node.remove();
       
  5249 			}
       
  5250 
       
  5251 			return node.length > 1 ? node.toArray() : node[0];
       
  5252 		},
       
  5253 
       
  5254 		/**
       
  5255 		 * Sets the CSS style value on a HTML element. The name can be a camelcase string
       
  5256 		 * or the CSS style name like background-color.
       
  5257 		 *
       
  5258 		 * @method setStyle
       
  5259 		 * @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
       
  5260 		 * @param {String} na Name of the style value to set.
       
  5261 		 * @param {String} v Value to set on the style.
       
  5262 		 * @example
       
  5263 		 * // Sets a style value on all paragraphs in the currently active editor
       
  5264 		 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
       
  5265 		 *
       
  5266 		 * // Sets a style value to an element by id in the current document
       
  5267 		 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
       
  5268 		 */
       
  5269 		setStyle: function(elm, name, value) {
       
  5270 			elm = this.$$(elm).css(name, value);
       
  5271 
       
  5272 			if (this.settings.update_styles) {
       
  5273 				elm.attr('data-mce-style', null);
       
  5274 			}
       
  5275 		},
       
  5276 
       
  5277 		/**
       
  5278 		 * Returns the current style or runtime/computed value of an element.
       
  5279 		 *
       
  5280 		 * @method getStyle
       
  5281 		 * @param {String/Element} elm HTML element or element id string to get style from.
       
  5282 		 * @param {String} name Style name to return.
       
  5283 		 * @param {Boolean} computed Computed style.
       
  5284 		 * @return {String} Current style or computed style value of an element.
       
  5285 		 */
       
  5286 		getStyle: function(elm, name, computed) {
       
  5287 			elm = this.$$(elm);
       
  5288 
       
  5289 			if (computed) {
       
  5290 				return elm.css(name);
       
  5291 			}
       
  5292 
       
  5293 			// Camelcase it, if needed
       
  5294 			name = name.replace(/-(\D)/g, function(a, b) {
       
  5295 				return b.toUpperCase();
       
  5296 			});
       
  5297 
       
  5298 			if (name == 'float') {
       
  5299 				name = isIE ? 'styleFloat' : 'cssFloat';
       
  5300 			}
       
  5301 
       
  5302 			return elm[0] && elm[0].style ? elm[0].style[name] : undefined;
       
  5303 		},
       
  5304 
       
  5305 		/**
       
  5306 		 * Sets multiple styles on the specified element(s).
       
  5307 		 *
       
  5308 		 * @method setStyles
       
  5309 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set styles on.
       
  5310 		 * @param {Object} o Name/Value collection of style items to add to the element(s).
       
  5311 		 * @example
       
  5312 		 * // Sets styles on all paragraphs in the currently active editor
       
  5313 		 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
       
  5314 		 *
       
  5315 		 * // Sets styles to an element by id in the current document
       
  5316 		 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
       
  5317 		 */
       
  5318 		setStyles: function(elm, styles) {
       
  5319 			elm = this.$$(elm).css(styles);
       
  5320 
       
  5321 			if (this.settings.update_styles) {
       
  5322 				elm.attr('data-mce-style', null);
       
  5323 			}
       
  5324 		},
       
  5325 
       
  5326 		/**
       
  5327 		 * Removes all attributes from an element or elements.
       
  5328 		 *
       
  5329 		 * @method removeAllAttribs
       
  5330 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
       
  5331 		 */
       
  5332 		removeAllAttribs: function(e) {
       
  5333 			return this.run(e, function(e) {
       
  5334 				var i, attrs = e.attributes;
       
  5335 				for (i = attrs.length - 1; i >= 0; i--) {
       
  5336 					e.removeAttributeNode(attrs.item(i));
       
  5337 				}
       
  5338 			});
       
  5339 		},
       
  5340 
       
  5341 		/**
       
  5342 		 * Sets the specified attribute of an element or elements.
       
  5343 		 *
       
  5344 		 * @method setAttrib
       
  5345 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set attribute on.
       
  5346 		 * @param {String} n Name of attribute to set.
       
  5347 		 * @param {String} v Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove the attribute instead.
       
  5348 		 * @example
       
  5349 		 * // Sets class attribute on all paragraphs in the active editor
       
  5350 		 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
       
  5351 		 *
       
  5352 		 * // Sets class attribute on a specific element in the current page
       
  5353 		 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
       
  5354 		 */
       
  5355 		setAttrib: function(elm, name, value) {
       
  5356 			var self = this, originalValue, hook, settings = self.settings;
       
  5357 
       
  5358 			if (value === '') {
       
  5359 				value = null;
       
  5360 			}
       
  5361 
       
  5362 			elm = self.$$(elm);
       
  5363 			originalValue = elm.attr(name);
       
  5364 
       
  5365 			if (!elm.length) {
       
  5366 				return;
       
  5367 			}
       
  5368 
       
  5369 			hook = self.attrHooks[name];
       
  5370 			if (hook && hook.set) {
       
  5371 				hook.set(elm, value, name);
       
  5372 			} else {
       
  5373 				elm.attr(name, value);
       
  5374 			}
       
  5375 
       
  5376 			if (originalValue != value && settings.onSetAttrib) {
       
  5377 				settings.onSetAttrib({
       
  5378 					attrElm: elm,
       
  5379 					attrName: name,
       
  5380 					attrValue: value
       
  5381 				});
       
  5382 			}
       
  5383 		},
       
  5384 
       
  5385 		/**
       
  5386 		 * Sets two or more specified attributes of an element or elements.
       
  5387 		 *
       
  5388 		 * @method setAttribs
       
  5389 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
       
  5390 		 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
       
  5391 		 * @example
       
  5392 		 * // Sets class and title attributes on all paragraphs in the active editor
       
  5393 		 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
       
  5394 		 *
       
  5395 		 * // Sets class and title attributes on a specific element in the current page
       
  5396 		 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
       
  5397 		 */
       
  5398 		setAttribs: function(elm, attrs) {
       
  5399 			var self = this;
       
  5400 
       
  5401 			self.$$(elm).each(function(i, node) {
       
  5402 				each(attrs, function(value, name) {
       
  5403 					self.setAttrib(node, name, value);
       
  5404 				});
       
  5405 			});
       
  5406 		},
       
  5407 
       
  5408 		/**
       
  5409 		 * Returns the specified attribute by name.
       
  5410 		 *
       
  5411 		 * @method getAttrib
       
  5412 		 * @param {String/Element} elm Element string id or DOM element to get attribute from.
       
  5413 		 * @param {String} name Name of attribute to get.
       
  5414 		 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
       
  5415 		 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
       
  5416 		 */
       
  5417 		getAttrib: function(elm, name, defaultVal) {
       
  5418 			var self = this, hook, value;
       
  5419 
       
  5420 			elm = self.$$(elm);
       
  5421 
       
  5422 			if (elm.length) {
       
  5423 				hook = self.attrHooks[name];
       
  5424 
       
  5425 				if (hook && hook.get) {
       
  5426 					value = hook.get(elm, name);
       
  5427 				} else {
       
  5428 					value = elm.attr(name);
       
  5429 				}
       
  5430 			}
       
  5431 
       
  5432 			if (typeof value == 'undefined') {
       
  5433 				value = defaultVal || '';
       
  5434 			}
       
  5435 
       
  5436 			return value;
       
  5437 		},
       
  5438 
       
  5439 		/**
       
  5440 		 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
       
  5441 		 *
       
  5442 		 * @method getPos
       
  5443 		 * @param {Element/String} elm HTML element or element id to get x, y position from.
       
  5444 		 * @param {Element} rootElm Optional root element to stop calculations at.
       
  5445 		 * @return {object} Absolute position of the specified element object with x, y fields.
       
  5446 		 */
       
  5447 		getPos: function(elm, rootElm) {
       
  5448 			var self = this, x = 0, y = 0, offsetParent, doc = self.doc, body = doc.body, pos;
       
  5449 
       
  5450 			elm = self.get(elm);
       
  5451 			rootElm = rootElm || body;
       
  5452 
       
  5453 			if (elm) {
       
  5454 				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
       
  5455 				// Fallback to offsetParent calculations if the body isn't static better since it stops at the body root
       
  5456 				if (rootElm === body && elm.getBoundingClientRect && $(body).css('position') === 'static') {
       
  5457 					pos = elm.getBoundingClientRect();
       
  5458 					rootElm = self.boxModel ? doc.documentElement : body;
       
  5459 
       
  5460 					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
       
  5461 					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
       
  5462 					x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft;
       
  5463 					y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop;
       
  5464 
       
  5465 					return {x: x, y: y};
       
  5466 				}
       
  5467 
       
  5468 				offsetParent = elm;
       
  5469 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
       
  5470 					x += offsetParent.offsetLeft || 0;
       
  5471 					y += offsetParent.offsetTop || 0;
       
  5472 					offsetParent = offsetParent.offsetParent;
       
  5473 				}
       
  5474 
       
  5475 				offsetParent = elm.parentNode;
       
  5476 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
       
  5477 					x -= offsetParent.scrollLeft || 0;
       
  5478 					y -= offsetParent.scrollTop || 0;
       
  5479 					offsetParent = offsetParent.parentNode;
       
  5480 				}
       
  5481 			}
       
  5482 
       
  5483 			return {x: x, y: y};
       
  5484 		},
       
  5485 
       
  5486 		/**
       
  5487 		 * Parses the specified style value into an object collection. This parser will also
       
  5488 		 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
       
  5489 		 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
       
  5490 		 *
       
  5491 		 * @method parseStyle
       
  5492 		 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
       
  5493 		 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
       
  5494 		 */
       
  5495 		parseStyle: function(cssText) {
       
  5496 			return this.styles.parse(cssText);
       
  5497 		},
       
  5498 
       
  5499 		/**
       
  5500 		 * Serializes the specified style object into a string.
       
  5501 		 *
       
  5502 		 * @method serializeStyle
       
  5503 		 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
       
  5504 		 * @param {String} name Optional element name.
       
  5505 		 * @return {String} String representation of the style object, for example: border: 1px solid red.
       
  5506 		 */
       
  5507 		serializeStyle: function(styles, name) {
       
  5508 			return this.styles.serialize(styles, name);
       
  5509 		},
       
  5510 
       
  5511 		/**
       
  5512 		 * Adds a style element at the top of the document with the specified cssText content.
       
  5513 		 *
       
  5514 		 * @method addStyle
       
  5515 		 * @param {String} cssText CSS Text style to add to top of head of document.
       
  5516 		 */
       
  5517 		addStyle: function(cssText) {
       
  5518 			var self = this, doc = self.doc, head, styleElm;
       
  5519 
       
  5520 			// Prevent inline from loading the same styles twice
       
  5521 			if (self !== DOMUtils.DOM && doc === document) {
       
  5522 				var addedStyles = DOMUtils.DOM.addedStyles;
       
  5523 
       
  5524 				addedStyles = addedStyles || [];
       
  5525 				if (addedStyles[cssText]) {
       
  5526 					return;
       
  5527 				}
       
  5528 
       
  5529 				addedStyles[cssText] = true;
       
  5530 				DOMUtils.DOM.addedStyles = addedStyles;
       
  5531 			}
       
  5532 
       
  5533 			// Create style element if needed
       
  5534 			styleElm = doc.getElementById('mceDefaultStyles');
       
  5535 			if (!styleElm) {
       
  5536 				styleElm = doc.createElement('style');
       
  5537 				styleElm.id = 'mceDefaultStyles';
       
  5538 				styleElm.type = 'text/css';
       
  5539 
       
  5540 				head = doc.getElementsByTagName('head')[0];
       
  5541 				if (head.firstChild) {
       
  5542 					head.insertBefore(styleElm, head.firstChild);
       
  5543 				} else {
       
  5544 					head.appendChild(styleElm);
       
  5545 				}
       
  5546 			}
       
  5547 
       
  5548 			// Append style data to old or new style element
       
  5549 			if (styleElm.styleSheet) {
       
  5550 				styleElm.styleSheet.cssText += cssText;
       
  5551 			} else {
       
  5552 				styleElm.appendChild(doc.createTextNode(cssText));
       
  5553 			}
       
  5554 		},
       
  5555 
       
  5556 		/**
       
  5557 		 * Imports/loads the specified CSS file into the document bound to the class.
       
  5558 		 *
       
  5559 		 * @method loadCSS
       
  5560 		 * @param {String} u URL to CSS file to load.
       
  5561 		 * @example
       
  5562 		 * // Loads a CSS file dynamically into the current document
       
  5563 		 * tinymce.DOM.loadCSS('somepath/some.css');
       
  5564 		 *
       
  5565 		 * // Loads a CSS file into the currently active editor instance
       
  5566 		 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
       
  5567 		 *
       
  5568 		 * // Loads a CSS file into an editor instance by id
       
  5569 		 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
       
  5570 		 *
       
  5571 		 * // Loads multiple CSS files into the current document
       
  5572 		 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
       
  5573 		 */
       
  5574 		loadCSS: function(url) {
       
  5575 			var self = this, doc = self.doc, head;
       
  5576 
       
  5577 			// Prevent inline from loading the same CSS file twice
       
  5578 			if (self !== DOMUtils.DOM && doc === document) {
       
  5579 				DOMUtils.DOM.loadCSS(url);
       
  5580 				return;
       
  5581 			}
       
  5582 
       
  5583 			if (!url) {
       
  5584 				url = '';
       
  5585 			}
       
  5586 
       
  5587 			head = doc.getElementsByTagName('head')[0];
       
  5588 
       
  5589 			each(url.split(','), function(url) {
       
  5590 				var link;
       
  5591 
       
  5592 				url = Tools._addCacheSuffix(url);
       
  5593 
       
  5594 				if (self.files[url]) {
       
  5595 					return;
       
  5596 				}
       
  5597 
       
  5598 				self.files[url] = true;
       
  5599 				link = self.create('link', {rel: 'stylesheet', href: url});
       
  5600 
       
  5601 				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
       
  5602 				// This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
       
  5603 				// It's ugly but it seems to work fine.
       
  5604 				if (isIE && doc.documentMode && doc.recalc) {
       
  5605 					link.onload = function() {
       
  5606 						if (doc.recalc) {
       
  5607 							doc.recalc();
       
  5608 						}
       
  5609 
       
  5610 						link.onload = null;
       
  5611 					};
       
  5612 				}
       
  5613 
       
  5614 				head.appendChild(link);
       
  5615 			});
       
  5616 		},
       
  5617 
       
  5618 		/**
       
  5619 		 * Adds a class to the specified element or elements.
       
  5620 		 *
       
  5621 		 * @method addClass
       
  5622 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
       
  5623 		 * @param {String} cls Class name to add to each element.
       
  5624 		 * @return {String/Array} String with new class value or array with new class values for all elements.
       
  5625 		 * @example
       
  5626 		 * // Adds a class to all paragraphs in the active editor
       
  5627 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
       
  5628 		 *
       
  5629 		 * // Adds a class to a specific element in the current page
       
  5630 		 * tinymce.DOM.addClass('mydiv', 'myclass');
       
  5631 		 */
       
  5632 		addClass: function(elm, cls) {
       
  5633 			this.$$(elm).addClass(cls);
       
  5634 		},
       
  5635 
       
  5636 		/**
       
  5637 		 * Removes a class from the specified element or elements.
       
  5638 		 *
       
  5639 		 * @method removeClass
       
  5640 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
       
  5641 		 * @param {String} cls Class name to remove from each element.
       
  5642 		 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
       
  5643 		 * were passed in.
       
  5644 		 * @example
       
  5645 		 * // Removes a class from all paragraphs in the active editor
       
  5646 		 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
       
  5647 		 *
       
  5648 		 * // Removes a class from a specific element in the current page
       
  5649 		 * tinymce.DOM.removeClass('mydiv', 'myclass');
       
  5650 		 */
       
  5651 		removeClass: function(elm, cls) {
       
  5652 			this.toggleClass(elm, cls, false);
       
  5653 		},
       
  5654 
       
  5655 		/**
       
  5656 		 * Returns true if the specified element has the specified class.
       
  5657 		 *
       
  5658 		 * @method hasClass
       
  5659 		 * @param {String/Element} n HTML element or element id string to check CSS class on.
       
  5660 		 * @param {String} c CSS class to check for.
       
  5661 		 * @return {Boolean} true/false if the specified element has the specified class.
       
  5662 		 */
       
  5663 		hasClass: function(elm, cls) {
       
  5664 			return this.$$(elm).hasClass(cls);
       
  5665 		},
       
  5666 
       
  5667 		/**
       
  5668 		 * Toggles the specified class on/off.
       
  5669 		 *
       
  5670 		 * @method toggleClass
       
  5671 		 * @param {Element} elm Element to toggle class on.
       
  5672 		 * @param {[type]} cls Class to toggle on/off.
       
  5673 		 * @param {[type]} state Optional state to set.
       
  5674 		 */
       
  5675 		toggleClass: function(elm, cls, state) {
       
  5676 			this.$$(elm).toggleClass(cls, state).each(function() {
       
  5677 				if (this.className === '') {
       
  5678 					$(this).attr('class', null);
       
  5679 				}
       
  5680 			});
       
  5681 		},
       
  5682 
       
  5683 		/**
       
  5684 		 * Shows the specified element(s) by ID by setting the "display" style.
       
  5685 		 *
       
  5686 		 * @method show
       
  5687 		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
       
  5688 		 */
       
  5689 		show: function(elm) {
       
  5690 			this.$$(elm).show();
       
  5691 		},
       
  5692 
       
  5693 		/**
       
  5694 		 * Hides the specified element(s) by ID by setting the "display" style.
       
  5695 		 *
       
  5696 		 * @method hide
       
  5697 		 * @param {String/Element/Array} e ID of DOM element or DOM element or array with elements or IDs to hide.
       
  5698 		 * @example
       
  5699 		 * // Hides an element by id in the document
       
  5700 		 * tinymce.DOM.hide('myid');
       
  5701 		 */
       
  5702 		hide: function(elm) {
       
  5703 			this.$$(elm).hide();
       
  5704 		},
       
  5705 
       
  5706 		/**
       
  5707 		 * Returns true/false if the element is hidden or not by checking the "display" style.
       
  5708 		 *
       
  5709 		 * @method isHidden
       
  5710 		 * @param {String/Element} e Id or element to check display state on.
       
  5711 		 * @return {Boolean} true/false if the element is hidden or not.
       
  5712 		 */
       
  5713 		isHidden: function(elm) {
       
  5714 			return this.$$(elm).css('display') == 'none';
       
  5715 		},
       
  5716 
       
  5717 		/**
       
  5718 		 * Returns a unique id. This can be useful when generating elements on the fly.
       
  5719 		 * This method will not check if the element already exists.
       
  5720 		 *
       
  5721 		 * @method uniqueId
       
  5722 		 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
       
  5723 		 * @return {String} Unique id.
       
  5724 		 */
       
  5725 		uniqueId: function(prefix) {
       
  5726 			return (!prefix ? 'mce_' : prefix) + (this.counter++);
       
  5727 		},
       
  5728 
       
  5729 		/**
       
  5730 		 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
       
  5731 		 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
       
  5732 		 *
       
  5733 		 * @method setHTML
       
  5734 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of.
       
  5735 		 * @param {String} h HTML content to set as inner HTML of the element.
       
  5736 		 * @example
       
  5737 		 * // Sets the inner HTML of all paragraphs in the active editor
       
  5738 		 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
       
  5739 		 *
       
  5740 		 * // Sets the inner HTML of an element by id in the document
       
  5741 		 * tinymce.DOM.setHTML('mydiv', 'some inner html');
       
  5742 		 */
       
  5743 		setHTML: function(elm, html) {
       
  5744 			elm = this.$$(elm);
       
  5745 
       
  5746 			if (isIE) {
       
  5747 				elm.each(function(i, target) {
       
  5748 					if (target.canHaveHTML === false) {
       
  5749 						return;
       
  5750 					}
       
  5751 
       
  5752 					// Remove all child nodes, IE keeps empty text nodes in DOM
       
  5753 					while (target.firstChild) {
       
  5754 						target.removeChild(target.firstChild);
       
  5755 					}
       
  5756 
       
  5757 					try {
       
  5758 						// IE will remove comments from the beginning
       
  5759 						// unless you padd the contents with something
       
  5760 						target.innerHTML = '<br>' + html;
       
  5761 						target.removeChild(target.firstChild);
       
  5762 					} catch (ex) {
       
  5763 						// IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p
       
  5764 						$('<div>').html('<br>' + html).contents().slice(1).appendTo(target);
       
  5765 					}
       
  5766 
       
  5767 					return html;
       
  5768 				});
       
  5769 			} else {
       
  5770 				elm.html(html);
       
  5771 			}
       
  5772 		},
       
  5773 
       
  5774 		/**
       
  5775 		 * Returns the outer HTML of an element.
       
  5776 		 *
       
  5777 		 * @method getOuterHTML
       
  5778 		 * @param {String/Element} elm Element ID or element object to get outer HTML from.
       
  5779 		 * @return {String} Outer HTML string.
       
  5780 		 * @example
       
  5781 		 * tinymce.DOM.getOuterHTML(editorElement);
       
  5782 		 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
       
  5783 		 */
       
  5784 		getOuterHTML: function(elm) {
       
  5785 			elm = this.get(elm);
       
  5786 
       
  5787 			// Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
       
  5788 			return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : $('<div>').append($(elm).clone()).html();
       
  5789 		},
       
  5790 
       
  5791 		/**
       
  5792 		 * Sets the specified outer HTML on an element or elements.
       
  5793 		 *
       
  5794 		 * @method setOuterHTML
       
  5795 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
       
  5796 		 * @param {Object} html HTML code to set as outer value for the element.
       
  5797 		 * @param {Document} doc Optional document scope to use in this process - defaults to the document of the DOM class.
       
  5798 		 * @example
       
  5799 		 * // Sets the outer HTML of all paragraphs in the active editor
       
  5800 		 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
       
  5801 		 *
       
  5802 		 * // Sets the outer HTML of an element by id in the document
       
  5803 		 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
       
  5804 		 */
       
  5805 		setOuterHTML: function(elm, html) {
       
  5806 			var self = this;
       
  5807 
       
  5808 			self.$$(elm).each(function() {
       
  5809 				try {
       
  5810 					// Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
       
  5811 					if ("outerHTML" in this) {
       
  5812 						this.outerHTML = html;
       
  5813 						return;
       
  5814 					}
       
  5815 				} catch (ex) {
       
  5816 					// Ignore
       
  5817 				}
       
  5818 
       
  5819 				// OuterHTML for IE it sometimes produces an "unknown runtime error"
       
  5820 				self.remove($(this).html(html), true);
       
  5821 			});
       
  5822 		},
       
  5823 
       
  5824 		/**
       
  5825 		 * Entity decodes a string. This method decodes any HTML entities, such as &aring;.
       
  5826 		 *
       
  5827 		 * @method decode
       
  5828 		 * @param {String} s String to decode entities on.
       
  5829 		 * @return {String} Entity decoded string.
       
  5830 		 */
       
  5831 		decode: Entities.decode,
       
  5832 
       
  5833 		/**
       
  5834 		 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
       
  5835 		 *
       
  5836 		 * @method encode
       
  5837 		 * @param {String} text String to encode with entities.
       
  5838 		 * @return {String} Entity encoded string.
       
  5839 		 */
       
  5840 		encode: Entities.encodeAllRaw,
       
  5841 
       
  5842 		/**
       
  5843 		 * Inserts an element after the reference element.
       
  5844 		 *
       
  5845 		 * @method insertAfter
       
  5846 		 * @param {Element} node Element to insert after the reference.
       
  5847 		 * @param {Element/String/Array} reference_node Reference element, element id or array of elements to insert after.
       
  5848 		 * @return {Element/Array} Element that got added or an array with elements.
       
  5849 		 */
       
  5850 		insertAfter: function(node, referenceNode) {
       
  5851 			referenceNode = this.get(referenceNode);
       
  5852 
       
  5853 			return this.run(node, function(node) {
       
  5854 				var parent, nextSibling;
       
  5855 
       
  5856 				parent = referenceNode.parentNode;
       
  5857 				nextSibling = referenceNode.nextSibling;
       
  5858 
       
  5859 				if (nextSibling) {
       
  5860 					parent.insertBefore(node, nextSibling);
       
  5861 				} else {
       
  5862 					parent.appendChild(node);
       
  5863 				}
       
  5864 
       
  5865 				return node;
       
  5866 			});
       
  5867 		},
       
  5868 
       
  5869 		/**
       
  5870 		 * Replaces the specified element or elements with the new element specified. The new element will
       
  5871 		 * be cloned if multiple input elements are passed in.
       
  5872 		 *
       
  5873 		 * @method replace
       
  5874 		 * @param {Element} newElm New element to replace old ones with.
       
  5875 		 * @param {Element/String/Array} oldELm Element DOM node, element id or array of elements or ids to replace.
       
  5876 		 * @param {Boolean} k Optional keep children state, if set to true child nodes from the old object will be added to new ones.
       
  5877 		 */
       
  5878 		replace: function(newElm, oldElm, keepChildren) {
       
  5879 			var self = this;
       
  5880 
       
  5881 			return self.run(oldElm, function(oldElm) {
       
  5882 				if (is(oldElm, 'array')) {
       
  5883 					newElm = newElm.cloneNode(true);
       
  5884 				}
       
  5885 
       
  5886 				if (keepChildren) {
       
  5887 					each(grep(oldElm.childNodes), function(node) {
       
  5888 						newElm.appendChild(node);
       
  5889 					});
       
  5890 				}
       
  5891 
       
  5892 				return oldElm.parentNode.replaceChild(newElm, oldElm);
       
  5893 			});
       
  5894 		},
       
  5895 
       
  5896 		/**
       
  5897 		 * Renames the specified element and keeps its attributes and children.
       
  5898 		 *
       
  5899 		 * @method rename
       
  5900 		 * @param {Element} elm Element to rename.
       
  5901 		 * @param {String} name Name of the new element.
       
  5902 		 * @return {Element} New element or the old element if it needed renaming.
       
  5903 		 */
       
  5904 		rename: function(elm, name) {
       
  5905 			var self = this, newElm;
       
  5906 
       
  5907 			if (elm.nodeName != name.toUpperCase()) {
       
  5908 				// Rename block element
       
  5909 				newElm = self.create(name);
       
  5910 
       
  5911 				// Copy attribs to new block
       
  5912 				each(self.getAttribs(elm), function(attrNode) {
       
  5913 					self.setAttrib(newElm, attrNode.nodeName, self.getAttrib(elm, attrNode.nodeName));
       
  5914 				});
       
  5915 
       
  5916 				// Replace block
       
  5917 				self.replace(newElm, elm, 1);
       
  5918 			}
       
  5919 
       
  5920 			return newElm || elm;
       
  5921 		},
       
  5922 
       
  5923 		/**
       
  5924 		 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
       
  5925 		 *
       
  5926 		 * @method findCommonAncestor
       
  5927 		 * @param {Element} a Element to find common ancestor of.
       
  5928 		 * @param {Element} b Element to find common ancestor of.
       
  5929 		 * @return {Element} Common ancestor element of the two input elements.
       
  5930 		 */
       
  5931 		findCommonAncestor: function(a, b) {
       
  5932 			var ps = a, pe;
       
  5933 
       
  5934 			while (ps) {
       
  5935 				pe = b;
       
  5936 
       
  5937 				while (pe && ps != pe) {
       
  5938 					pe = pe.parentNode;
       
  5939 				}
       
  5940 
       
  5941 				if (ps == pe) {
       
  5942 					break;
       
  5943 				}
       
  5944 
       
  5945 				ps = ps.parentNode;
       
  5946 			}
       
  5947 
       
  5948 			if (!ps && a.ownerDocument) {
       
  5949 				return a.ownerDocument.documentElement;
       
  5950 			}
       
  5951 
       
  5952 			return ps;
       
  5953 		},
       
  5954 
       
  5955 		/**
       
  5956 		 * Parses the specified RGB color value and returns a hex version of that color.
       
  5957 		 *
       
  5958 		 * @method toHex
       
  5959 		 * @param {String} rgbVal RGB string value like rgb(1,2,3)
       
  5960 		 * @return {String} Hex version of that RGB value like #FF00FF.
       
  5961 		 */
       
  5962 		toHex: function(rgbVal) {
       
  5963 			return this.styles.toHex(Tools.trim(rgbVal));
       
  5964 		},
       
  5965 
       
  5966 		/**
       
  5967 		 * Executes the specified function on the element by id or dom element node or array of elements/id.
       
  5968 		 *
       
  5969 		 * @method run
       
  5970 		 * @param {String/Element/Array} Element ID or DOM element object or array with ids or elements.
       
  5971 		 * @param {function} f Function to execute for each item.
       
  5972 		 * @param {Object} s Optional scope to execute the function in.
       
  5973 		 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
       
  5974 		 */
       
  5975 		run: function(elm, func, scope) {
       
  5976 			var self = this, result;
       
  5977 
       
  5978 			if (typeof elm === 'string') {
       
  5979 				elm = self.get(elm);
       
  5980 			}
       
  5981 
       
  5982 			if (!elm) {
       
  5983 				return false;
       
  5984 			}
       
  5985 
       
  5986 			scope = scope || this;
       
  5987 			if (!elm.nodeType && (elm.length || elm.length === 0)) {
       
  5988 				result = [];
       
  5989 
       
  5990 				each(elm, function(elm, i) {
       
  5991 					if (elm) {
       
  5992 						if (typeof elm == 'string') {
       
  5993 							elm = self.get(elm);
       
  5994 						}
       
  5995 
       
  5996 						result.push(func.call(scope, elm, i));
       
  5997 					}
       
  5998 				});
       
  5999 
       
  6000 				return result;
       
  6001 			}
       
  6002 
       
  6003 			return func.call(scope, elm);
       
  6004 		},
       
  6005 
       
  6006 		/**
       
  6007 		 * Returns a NodeList with attributes for the element.
       
  6008 		 *
       
  6009 		 * @method getAttribs
       
  6010 		 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
       
  6011 		 * @return {NodeList} NodeList with attributes.
       
  6012 		 */
       
  6013 		getAttribs: function(elm) {
       
  6014 			var attrs;
       
  6015 
       
  6016 			elm = this.get(elm);
       
  6017 
       
  6018 			if (!elm) {
       
  6019 				return [];
       
  6020 			}
       
  6021 
       
  6022 			if (isIE) {
       
  6023 				attrs = [];
       
  6024 
       
  6025 				// Object will throw exception in IE
       
  6026 				if (elm.nodeName == 'OBJECT') {
       
  6027 					return elm.attributes;
       
  6028 				}
       
  6029 
       
  6030 				// IE doesn't keep the selected attribute if you clone option elements
       
  6031 				if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
       
  6032 					attrs.push({specified: 1, nodeName: 'selected'});
       
  6033 				}
       
  6034 
       
  6035 				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
       
  6036 				var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
       
  6037 				elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
       
  6038 					attrs.push({specified: 1, nodeName: a});
       
  6039 				});
       
  6040 
       
  6041 				return attrs;
       
  6042 			}
       
  6043 
       
  6044 			return elm.attributes;
       
  6045 		},
       
  6046 
       
  6047 		/**
       
  6048 		 * Returns true/false if the specified node is to be considered empty or not.
       
  6049 		 *
       
  6050 		 * @example
       
  6051 		 * tinymce.DOM.isEmpty(node, {img: true});
       
  6052 		 * @method isEmpty
       
  6053 		 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
       
  6054 		 * @return {Boolean} true/false if the node is empty or not.
       
  6055 		 */
       
  6056 		isEmpty: function(node, elements) {
       
  6057 			var self = this, i, attributes, type, walker, name, brCount = 0;
       
  6058 
       
  6059 			node = node.firstChild;
       
  6060 			if (node) {
       
  6061 				walker = new TreeWalker(node, node.parentNode);
       
  6062 				elements = elements || (self.schema ? self.schema.getNonEmptyElements() : null);
       
  6063 
       
  6064 				do {
       
  6065 					type = node.nodeType;
       
  6066 
       
  6067 					if (type === 1) {
       
  6068 						// Ignore bogus elements
       
  6069 						if (node.getAttribute('data-mce-bogus')) {
       
  6070 							continue;
       
  6071 						}
       
  6072 
       
  6073 						// Keep empty elements like <img />
       
  6074 						name = node.nodeName.toLowerCase();
       
  6075 						if (elements && elements[name]) {
       
  6076 							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
       
  6077 							if (name === 'br') {
       
  6078 								brCount++;
       
  6079 								continue;
       
  6080 							}
       
  6081 
       
  6082 							return false;
       
  6083 						}
       
  6084 
       
  6085 						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
       
  6086 						attributes = self.getAttribs(node);
       
  6087 						i = attributes.length;
       
  6088 						while (i--) {
       
  6089 							name = attributes[i].nodeName;
       
  6090 							if (name === "name" || name === 'data-mce-bookmark') {
       
  6091 								return false;
       
  6092 							}
       
  6093 						}
       
  6094 					}
       
  6095 
       
  6096 					// Keep comment nodes
       
  6097 					if (type == 8) {
       
  6098 						return false;
       
  6099 					}
       
  6100 
       
  6101 					// Keep non whitespace text nodes
       
  6102 					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
       
  6103 						return false;
       
  6104 					}
       
  6105 				} while ((node = walker.next()));
       
  6106 			}
       
  6107 
       
  6108 			return brCount <= 1;
       
  6109 		},
       
  6110 
       
  6111 		/**
       
  6112 		 * Creates a new DOM Range object. This will use the native DOM Range API if it's
       
  6113 		 * available. If it's not, it will fall back to the custom TinyMCE implementation.
       
  6114 		 *
       
  6115 		 * @method createRng
       
  6116 		 * @return {DOMRange} DOM Range object.
       
  6117 		 * @example
       
  6118 		 * var rng = tinymce.DOM.createRng();
       
  6119 		 * alert(rng.startContainer + "," + rng.startOffset);
       
  6120 		 */
       
  6121 		createRng: function() {
       
  6122 			var doc = this.doc;
       
  6123 
       
  6124 			return doc.createRange ? doc.createRange() : new Range(this);
       
  6125 		},
       
  6126 
       
  6127 		/**
       
  6128 		 * Returns the index of the specified node within its parent.
       
  6129 		 *
       
  6130 		 * @method nodeIndex
       
  6131 		 * @param {Node} node Node to look for.
       
  6132 		 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
       
  6133 		 * @return {Number} Index of the specified node.
       
  6134 		 */
       
  6135 		nodeIndex: function(node, normalized) {
       
  6136 			var idx = 0, lastNodeType, nodeType;
       
  6137 
       
  6138 			if (node) {
       
  6139 				for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
       
  6140 					nodeType = node.nodeType;
       
  6141 
       
  6142 					// Normalize text nodes
       
  6143 					if (normalized && nodeType == 3) {
       
  6144 						if (nodeType == lastNodeType || !node.nodeValue.length) {
       
  6145 							continue;
       
  6146 						}
       
  6147 					}
       
  6148 					idx++;
       
  6149 					lastNodeType = nodeType;
       
  6150 				}
       
  6151 			}
       
  6152 
       
  6153 			return idx;
       
  6154 		},
       
  6155 
       
  6156 		/**
       
  6157 		 * Splits an element into two new elements and places the specified split
       
  6158 		 * element or elements between the new ones. For example splitting the paragraph at the bold element in
       
  6159 		 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
       
  6160 		 *
       
  6161 		 * @method split
       
  6162 		 * @param {Element} parentElm Parent element to split.
       
  6163 		 * @param {Element} splitElm Element to split at.
       
  6164 		 * @param {Element} replacementElm Optional replacement element to replace the split element with.
       
  6165 		 * @return {Element} Returns the split element or the replacement element if that is specified.
       
  6166 		 */
       
  6167 		split: function(parentElm, splitElm, replacementElm) {
       
  6168 			var self = this, r = self.createRng(), bef, aft, pa;
       
  6169 
       
  6170 			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
       
  6171 			// but we don't want that in our code since it serves no purpose for the end user
       
  6172 			// For example splitting this html at the bold element:
       
  6173 			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
       
  6174 			// would produce:
       
  6175 			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
       
  6176 			// this function will then trim off empty edges and produce:
       
  6177 			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
       
  6178 			function trimNode(node) {
       
  6179 				var i, children = node.childNodes, type = node.nodeType;
       
  6180 
       
  6181 				function surroundedBySpans(node) {
       
  6182 					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
       
  6183 					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
       
  6184 					return previousIsSpan && nextIsSpan;
       
  6185 				}
       
  6186 
       
  6187 				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
       
  6188 					return;
       
  6189 				}
       
  6190 
       
  6191 				for (i = children.length - 1; i >= 0; i--) {
       
  6192 					trimNode(children[i]);
       
  6193 				}
       
  6194 
       
  6195 				if (type != 9) {
       
  6196 					// Keep non whitespace text nodes
       
  6197 					if (type == 3 && node.nodeValue.length > 0) {
       
  6198 						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
       
  6199 						// Also keep text nodes with only spaces if surrounded by spans.
       
  6200 						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
       
  6201 						var trimmedLength = trim(node.nodeValue).length;
       
  6202 						if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
       
  6203 							return;
       
  6204 						}
       
  6205 					} else if (type == 1) {
       
  6206 						// If the only child is a bookmark then move it up
       
  6207 						children = node.childNodes;
       
  6208 
       
  6209 						// TODO fix this complex if
       
  6210 						if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
       
  6211 							children[0].getAttribute('data-mce-type') == 'bookmark') {
       
  6212 							node.parentNode.insertBefore(children[0], node);
       
  6213 						}
       
  6214 
       
  6215 						// Keep non empty elements or img, hr etc
       
  6216 						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
       
  6217 							return;
       
  6218 						}
       
  6219 					}
       
  6220 
       
  6221 					self.remove(node);
       
  6222 				}
       
  6223 
       
  6224 				return node;
       
  6225 			}
       
  6226 
       
  6227 			if (parentElm && splitElm) {
       
  6228 				// Get before chunk
       
  6229 				r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
       
  6230 				r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
       
  6231 				bef = r.extractContents();
       
  6232 
       
  6233 				// Get after chunk
       
  6234 				r = self.createRng();
       
  6235 				r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
       
  6236 				r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
       
  6237 				aft = r.extractContents();
       
  6238 
       
  6239 				// Insert before chunk
       
  6240 				pa = parentElm.parentNode;
       
  6241 				pa.insertBefore(trimNode(bef), parentElm);
       
  6242 
       
  6243 				// Insert middle chunk
       
  6244 				if (replacementElm) {
       
  6245 					pa.replaceChild(replacementElm, splitElm);
       
  6246 				} else {
       
  6247 					pa.insertBefore(splitElm, parentElm);
       
  6248 				}
       
  6249 
       
  6250 				// Insert after chunk
       
  6251 				pa.insertBefore(trimNode(aft), parentElm);
       
  6252 				self.remove(parentElm);
       
  6253 
       
  6254 				return replacementElm || splitElm;
       
  6255 			}
       
  6256 		},
       
  6257 
       
  6258 		/**
       
  6259 		 * Adds an event handler to the specified object.
       
  6260 		 *
       
  6261 		 * @method bind
       
  6262 		 * @param {Element/Document/Window/Array} target Target element to bind events to.
       
  6263 		 * handler to or an array of elements/ids/documents.
       
  6264 		 * @param {String} name Name of event handler to add, for example: click.
       
  6265 		 * @param {function} func Function to execute when the event occurs.
       
  6266 		 * @param {Object} scope Optional scope to execute the function in.
       
  6267 		 * @return {function} Function callback handler the same as the one passed in.
       
  6268 		 */
       
  6269 		bind: function(target, name, func, scope) {
       
  6270 			var self = this;
       
  6271 
       
  6272 			if (Tools.isArray(target)) {
       
  6273 				var i = target.length;
       
  6274 
       
  6275 				while (i--) {
       
  6276 					target[i] = self.bind(target[i], name, func, scope);
       
  6277 				}
       
  6278 
       
  6279 				return target;
       
  6280 			}
       
  6281 
       
  6282 			// Collect all window/document events bound by editor instance
       
  6283 			if (self.settings.collect && (target === self.doc || target === self.win)) {
       
  6284 				self.boundEvents.push([target, name, func, scope]);
       
  6285 			}
       
  6286 
       
  6287 			return self.events.bind(target, name, func, scope || self);
       
  6288 		},
       
  6289 
       
  6290 		/**
       
  6291 		 * Removes the specified event handler by name and function from an element or collection of elements.
       
  6292 		 *
       
  6293 		 * @method unbind
       
  6294 		 * @param {Element/Document/Window/Array} target Target element to unbind events on.
       
  6295 		 * @param {String} name Event handler name, for example: "click"
       
  6296 		 * @param {function} func Function to remove.
       
  6297 		 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
       
  6298 		 * were passed in.
       
  6299 		 */
       
  6300 		unbind: function(target, name, func) {
       
  6301 			var self = this, i;
       
  6302 
       
  6303 			if (Tools.isArray(target)) {
       
  6304 				i = target.length;
       
  6305 
       
  6306 				while (i--) {
       
  6307 					target[i] = self.unbind(target[i], name, func);
       
  6308 				}
       
  6309 
       
  6310 				return target;
       
  6311 			}
       
  6312 
       
  6313 			// Remove any bound events matching the input
       
  6314 			if (self.boundEvents && (target === self.doc || target === self.win)) {
       
  6315 				i = self.boundEvents.length;
       
  6316 
       
  6317 				while (i--) {
       
  6318 					var item = self.boundEvents[i];
       
  6319 
       
  6320 					if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
       
  6321 						this.events.unbind(item[0], item[1], item[2]);
       
  6322 					}
       
  6323 				}
       
  6324 			}
       
  6325 
       
  6326 			return this.events.unbind(target, name, func);
       
  6327 		},
       
  6328 
       
  6329 		/**
       
  6330 		 * Fires the specified event name with object on target.
       
  6331 		 *
       
  6332 		 * @method fire
       
  6333 		 * @param {Node/Document/Window} target Target element or object to fire event on.
       
  6334 		 * @param {String} name Name of the event to fire.
       
  6335 		 * @param {Object} evt Event object to send.
       
  6336 		 * @return {Event} Event object.
       
  6337 		 */
       
  6338 		fire: function(target, name, evt) {
       
  6339 			return this.events.fire(target, name, evt);
       
  6340 		},
       
  6341 
       
  6342 		// Returns the content editable state of a node
       
  6343 		getContentEditable: function(node) {
       
  6344 			var contentEditable;
       
  6345 
       
  6346 			// Check type
       
  6347 			if (!node || node.nodeType != 1) {
       
  6348 				return null;
       
  6349 			}
       
  6350 
       
  6351 			// Check for fake content editable
       
  6352 			contentEditable = node.getAttribute("data-mce-contenteditable");
       
  6353 			if (contentEditable && contentEditable !== "inherit") {
       
  6354 				return contentEditable;
       
  6355 			}
       
  6356 
       
  6357 			// Check for real content editable
       
  6358 			return node.contentEditable !== "inherit" ? node.contentEditable : null;
       
  6359 		},
       
  6360 
       
  6361 		getContentEditableParent: function(node) {
       
  6362 			var root = this.getRoot(), state = null;
       
  6363 
       
  6364 			for (; node && node !== root; node = node.parentNode) {
       
  6365 				state = this.getContentEditable(node);
       
  6366 
       
  6367 				if (state !== null) {
       
  6368 					break;
       
  6369 				}
       
  6370 			}
       
  6371 
       
  6372 			return state;
       
  6373 		},
       
  6374 
       
  6375 		/**
       
  6376 		 * Destroys all internal references to the DOM to solve IE leak issues.
       
  6377 		 *
       
  6378 		 * @method destroy
       
  6379 		 */
       
  6380 		destroy: function() {
       
  6381 			var self = this;
       
  6382 
       
  6383 			// Unbind all events bound to window/document by editor instance
       
  6384 			if (self.boundEvents) {
       
  6385 				var i = self.boundEvents.length;
       
  6386 
       
  6387 				while (i--) {
       
  6388 					var item = self.boundEvents[i];
       
  6389 					this.events.unbind(item[0], item[1], item[2]);
       
  6390 				}
       
  6391 
       
  6392 				self.boundEvents = null;
       
  6393 			}
       
  6394 
       
  6395 			// Restore sizzle document to window.document
       
  6396 			// Since the current document might be removed producing "Permission denied" on IE see #6325
       
  6397 			if (Sizzle.setDocument) {
       
  6398 				Sizzle.setDocument();
       
  6399 			}
       
  6400 
       
  6401 			self.win = self.doc = self.root = self.events = self.frag = null;
       
  6402 		},
       
  6403 
       
  6404 		isChildOf: function(node, parent) {
       
  6405 			while (node) {
       
  6406 				if (parent === node) {
       
  6407 					return true;
       
  6408 				}
       
  6409 
       
  6410 				node = node.parentNode;
       
  6411 			}
       
  6412 
       
  6413 			return false;
       
  6414 		},
       
  6415 
       
  6416 		// #ifdef debug
       
  6417 
       
  6418 		dumpRng: function(r) {
       
  6419 			return (
       
  6420 				'startContainer: ' + r.startContainer.nodeName +
       
  6421 				', startOffset: ' + r.startOffset +
       
  6422 				', endContainer: ' + r.endContainer.nodeName +
       
  6423 				', endOffset: ' + r.endOffset
       
  6424 			);
       
  6425 		},
       
  6426 
       
  6427 		// #endif
       
  6428 
       
  6429 		_findSib: function(node, selector, name) {
       
  6430 			var self = this, func = selector;
       
  6431 
       
  6432 			if (node) {
       
  6433 				// If expression make a function of it using is
       
  6434 				if (typeof func == 'string') {
       
  6435 					func = function(node) {
       
  6436 						return self.is(node, selector);
       
  6437 					};
       
  6438 				}
       
  6439 
       
  6440 				// Loop all siblings
       
  6441 				for (node = node[name]; node; node = node[name]) {
       
  6442 					if (func(node)) {
       
  6443 						return node;
       
  6444 					}
       
  6445 				}
       
  6446 			}
       
  6447 
       
  6448 			return null;
       
  6449 		}
       
  6450 	};
       
  6451 
       
  6452 	/**
       
  6453 	 * Instance of DOMUtils for the current document.
       
  6454 	 *
       
  6455 	 * @static
       
  6456 	 * @property DOM
       
  6457 	 * @type tinymce.dom.DOMUtils
       
  6458 	 * @example
       
  6459 	 * // Example of how to add a class to some element by id
       
  6460 	 * tinymce.DOM.addClass('someid', 'someclass');
       
  6461 	 */
       
  6462 	DOMUtils.DOM = new DOMUtils(document);
       
  6463 
       
  6464 	return DOMUtils;
       
  6465 });
       
  6466 
       
  6467 // Included from: js/tinymce/classes/dom/ScriptLoader.js
       
  6468 
       
  6469 /**
       
  6470  * ScriptLoader.js
       
  6471  *
       
  6472  * Copyright, Moxiecode Systems AB
       
  6473  * Released under LGPL License.
       
  6474  *
       
  6475  * License: http://www.tinymce.com/license
       
  6476  * Contributing: http://www.tinymce.com/contributing
       
  6477  */
       
  6478 
       
  6479 /*globals console*/
       
  6480 
       
  6481 /**
       
  6482  * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
       
  6483  * when various items gets loaded. This class is useful to load external JavaScript files.
       
  6484  *
       
  6485  * @class tinymce.dom.ScriptLoader
       
  6486  * @example
       
  6487  * // Load a script from a specific URL using the global script loader
       
  6488  * tinymce.ScriptLoader.load('somescript.js');
       
  6489  *
       
  6490  * // Load a script using a unique instance of the script loader
       
  6491  * var scriptLoader = new tinymce.dom.ScriptLoader();
       
  6492  *
       
  6493  * scriptLoader.load('somescript.js');
       
  6494  *
       
  6495  * // Load multiple scripts
       
  6496  * var scriptLoader = new tinymce.dom.ScriptLoader();
       
  6497  *
       
  6498  * scriptLoader.add('somescript1.js');
       
  6499  * scriptLoader.add('somescript2.js');
       
  6500  * scriptLoader.add('somescript3.js');
       
  6501  *
       
  6502  * scriptLoader.loadQueue(function() {
       
  6503  *    alert('All scripts are now loaded.');
       
  6504  * });
       
  6505  */
       
  6506 define("tinymce/dom/ScriptLoader", [
       
  6507 	"tinymce/dom/DOMUtils",
       
  6508 	"tinymce/util/Tools"
       
  6509 ], function(DOMUtils, Tools) {
       
  6510 	var DOM = DOMUtils.DOM;
       
  6511 	var each = Tools.each, grep = Tools.grep;
       
  6512 
       
  6513 	function ScriptLoader() {
       
  6514 		var QUEUED = 0,
       
  6515 			LOADING = 1,
       
  6516 			LOADED = 2,
       
  6517 			states = {},
       
  6518 			queue = [],
       
  6519 			scriptLoadedCallbacks = {},
       
  6520 			queueLoadedCallbacks = [],
       
  6521 			loading = 0,
       
  6522 			undef;
       
  6523 
       
  6524 		/**
       
  6525 		 * Loads a specific script directly without adding it to the load queue.
       
  6526 		 *
       
  6527 		 * @method load
       
  6528 		 * @param {String} url Absolute URL to script to add.
       
  6529 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
       
  6530 		 * @param {Object} scope Optional scope to execute callback in.
       
  6531 		 */
       
  6532 		function loadScript(url, callback) {
       
  6533 			var dom = DOM, elm, id;
       
  6534 
       
  6535 			// Execute callback when script is loaded
       
  6536 			function done() {
       
  6537 				dom.remove(id);
       
  6538 
       
  6539 				if (elm) {
       
  6540 					elm.onreadystatechange = elm.onload = elm = null;
       
  6541 				}
       
  6542 
       
  6543 				callback();
       
  6544 			}
       
  6545 
       
  6546 			function error() {
       
  6547 				/*eslint no-console:0 */
       
  6548 
       
  6549 				// Report the error so it's easier for people to spot loading errors
       
  6550 				if (typeof console !== "undefined" && console.log) {
       
  6551 					console.log("Failed to load: " + url);
       
  6552 				}
       
  6553 
       
  6554 				// We can't mark it as done if there is a load error since
       
  6555 				// A) We don't want to produce 404 errors on the server and
       
  6556 				// B) the onerror event won't fire on all browsers.
       
  6557 				// done();
       
  6558 			}
       
  6559 
       
  6560 			id = dom.uniqueId();
       
  6561 
       
  6562 			// Create new script element
       
  6563 			elm = document.createElement('script');
       
  6564 			elm.id = id;
       
  6565 			elm.type = 'text/javascript';
       
  6566 			elm.src = Tools._addCacheSuffix(url);
       
  6567 
       
  6568 			// Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
       
  6569 			if ("onreadystatechange" in elm) {
       
  6570 				elm.onreadystatechange = function() {
       
  6571 					if (/loaded|complete/.test(elm.readyState)) {
       
  6572 						done();
       
  6573 					}
       
  6574 				};
       
  6575 			} else {
       
  6576 				elm.onload = done;
       
  6577 			}
       
  6578 
       
  6579 			// Add onerror event will get fired on some browsers but not all of them
       
  6580 			elm.onerror = error;
       
  6581 
       
  6582 			// Add script to document
       
  6583 			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
       
  6584 		}
       
  6585 
       
  6586 		/**
       
  6587 		 * Returns true/false if a script has been loaded or not.
       
  6588 		 *
       
  6589 		 * @method isDone
       
  6590 		 * @param {String} url URL to check for.
       
  6591 		 * @return {Boolean} true/false if the URL is loaded.
       
  6592 		 */
       
  6593 		this.isDone = function(url) {
       
  6594 			return states[url] == LOADED;
       
  6595 		};
       
  6596 
       
  6597 		/**
       
  6598 		 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
       
  6599 		 * the script loader or to skip it from loading some script.
       
  6600 		 *
       
  6601 		 * @method markDone
       
  6602 		 * @param {string} u Absolute URL to the script to mark as loaded.
       
  6603 		 */
       
  6604 		this.markDone = function(url) {
       
  6605 			states[url] = LOADED;
       
  6606 		};
       
  6607 
       
  6608 		/**
       
  6609 		 * Adds a specific script to the load queue of the script loader.
       
  6610 		 *
       
  6611 		 * @method add
       
  6612 		 * @param {String} url Absolute URL to script to add.
       
  6613 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
       
  6614 		 * @param {Object} scope Optional scope to execute callback in.
       
  6615 		 */
       
  6616 		this.add = this.load = function(url, callback, scope) {
       
  6617 			var state = states[url];
       
  6618 
       
  6619 			// Add url to load queue
       
  6620 			if (state == undef) {
       
  6621 				queue.push(url);
       
  6622 				states[url] = QUEUED;
       
  6623 			}
       
  6624 
       
  6625 			if (callback) {
       
  6626 				// Store away callback for later execution
       
  6627 				if (!scriptLoadedCallbacks[url]) {
       
  6628 					scriptLoadedCallbacks[url] = [];
       
  6629 				}
       
  6630 
       
  6631 				scriptLoadedCallbacks[url].push({
       
  6632 					func: callback,
       
  6633 					scope: scope || this
       
  6634 				});
       
  6635 			}
       
  6636 		};
       
  6637 
       
  6638 		/**
       
  6639 		 * Starts the loading of the queue.
       
  6640 		 *
       
  6641 		 * @method loadQueue
       
  6642 		 * @param {function} callback Optional callback to execute when all queued items are loaded.
       
  6643 		 * @param {Object} scope Optional scope to execute the callback in.
       
  6644 		 */
       
  6645 		this.loadQueue = function(callback, scope) {
       
  6646 			this.loadScripts(queue, callback, scope);
       
  6647 		};
       
  6648 
       
  6649 		/**
       
  6650 		 * Loads the specified queue of files and executes the callback ones they are loaded.
       
  6651 		 * This method is generally not used outside this class but it might be useful in some scenarios.
       
  6652 		 *
       
  6653 		 * @method loadScripts
       
  6654 		 * @param {Array} scripts Array of queue items to load.
       
  6655 		 * @param {function} callback Optional callback to execute ones all items are loaded.
       
  6656 		 * @param {Object} scope Optional scope to execute callback in.
       
  6657 		 */
       
  6658 		this.loadScripts = function(scripts, callback, scope) {
       
  6659 			var loadScripts;
       
  6660 
       
  6661 			function execScriptLoadedCallbacks(url) {
       
  6662 				// Execute URL callback functions
       
  6663 				each(scriptLoadedCallbacks[url], function(callback) {
       
  6664 					callback.func.call(callback.scope);
       
  6665 				});
       
  6666 
       
  6667 				scriptLoadedCallbacks[url] = undef;
       
  6668 			}
       
  6669 
       
  6670 			queueLoadedCallbacks.push({
       
  6671 				func: callback,
       
  6672 				scope: scope || this
       
  6673 			});
       
  6674 
       
  6675 			loadScripts = function() {
       
  6676 				var loadingScripts = grep(scripts);
       
  6677 
       
  6678 				// Current scripts has been handled
       
  6679 				scripts.length = 0;
       
  6680 
       
  6681 				// Load scripts that needs to be loaded
       
  6682 				each(loadingScripts, function(url) {
       
  6683 					// Script is already loaded then execute script callbacks directly
       
  6684 					if (states[url] == LOADED) {
       
  6685 						execScriptLoadedCallbacks(url);
       
  6686 						return;
       
  6687 					}
       
  6688 
       
  6689 					// Is script not loading then start loading it
       
  6690 					if (states[url] != LOADING) {
       
  6691 						states[url] = LOADING;
       
  6692 						loading++;
       
  6693 
       
  6694 						loadScript(url, function() {
       
  6695 							states[url] = LOADED;
       
  6696 							loading--;
       
  6697 
       
  6698 							execScriptLoadedCallbacks(url);
       
  6699 
       
  6700 							// Load more scripts if they where added by the recently loaded script
       
  6701 							loadScripts();
       
  6702 						});
       
  6703 					}
       
  6704 				});
       
  6705 
       
  6706 				// No scripts are currently loading then execute all pending queue loaded callbacks
       
  6707 				if (!loading) {
       
  6708 					each(queueLoadedCallbacks, function(callback) {
       
  6709 						callback.func.call(callback.scope);
       
  6710 					});
       
  6711 
       
  6712 					queueLoadedCallbacks.length = 0;
       
  6713 				}
       
  6714 			};
       
  6715 
       
  6716 			loadScripts();
       
  6717 		};
       
  6718 	}
       
  6719 
       
  6720 	ScriptLoader.ScriptLoader = new ScriptLoader();
       
  6721 
       
  6722 	return ScriptLoader;
       
  6723 });
       
  6724 
       
  6725 // Included from: js/tinymce/classes/AddOnManager.js
       
  6726 
       
  6727 /**
       
  6728  * AddOnManager.js
       
  6729  *
       
  6730  * Copyright, Moxiecode Systems AB
       
  6731  * Released under LGPL License.
       
  6732  *
       
  6733  * License: http://www.tinymce.com/license
       
  6734  * Contributing: http://www.tinymce.com/contributing
       
  6735  */
       
  6736 
       
  6737 /**
       
  6738  * This class handles the loading of themes/plugins or other add-ons and their language packs.
       
  6739  *
       
  6740  * @class tinymce.AddOnManager
       
  6741  */
       
  6742 define("tinymce/AddOnManager", [
       
  6743 	"tinymce/dom/ScriptLoader",
       
  6744 	"tinymce/util/Tools"
       
  6745 ], function(ScriptLoader, Tools) {
       
  6746 	var each = Tools.each;
       
  6747 
       
  6748 	function AddOnManager() {
       
  6749 		var self = this;
       
  6750 
       
  6751 		self.items = [];
       
  6752 		self.urls = {};
       
  6753 		self.lookup = {};
       
  6754 	}
       
  6755 
       
  6756 	AddOnManager.prototype = {
       
  6757 		/**
       
  6758 		 * Returns the specified add on by the short name.
       
  6759 		 *
       
  6760 		 * @method get
       
  6761 		 * @param {String} name Add-on to look for.
       
  6762 		 * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
       
  6763 		 */
       
  6764 		get: function(name) {
       
  6765 			if (this.lookup[name]) {
       
  6766 				return this.lookup[name].instance;
       
  6767 			} else {
       
  6768 				return undefined;
       
  6769 			}
       
  6770 		},
       
  6771 
       
  6772 		dependencies: function(name) {
       
  6773 			var result;
       
  6774 
       
  6775 			if (this.lookup[name]) {
       
  6776 				result = this.lookup[name].dependencies;
       
  6777 			}
       
  6778 
       
  6779 			return result || [];
       
  6780 		},
       
  6781 
       
  6782 		/**
       
  6783 		 * Loads a language pack for the specified add-on.
       
  6784 		 *
       
  6785 		 * @method requireLangPack
       
  6786 		 * @param {String} name Short name of the add-on.
       
  6787 		 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
       
  6788 		 */
       
  6789 		requireLangPack: function(name, languages) {
       
  6790 			var language = AddOnManager.language;
       
  6791 
       
  6792 			if (language && AddOnManager.languageLoad !== false) {
       
  6793 				if (languages) {
       
  6794 					languages = ',' + languages + ',';
       
  6795 
       
  6796 					// Load short form sv.js or long form sv_SE.js
       
  6797 					if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) {
       
  6798 						language = language.substr(0, 2);
       
  6799 					} else if (languages.indexOf(',' + language + ',') == -1) {
       
  6800 						return;
       
  6801 					}
       
  6802 				}
       
  6803 
       
  6804 				ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js');
       
  6805 			}
       
  6806 		},
       
  6807 
       
  6808 		/**
       
  6809 		 * Adds a instance of the add-on by it's short name.
       
  6810 		 *
       
  6811 		 * @method add
       
  6812 		 * @param {String} id Short name/id for the add-on.
       
  6813 		 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
       
  6814 		 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
       
  6815 		 * @example
       
  6816 		 * // Create a simple plugin
       
  6817 		 * tinymce.create('tinymce.plugins.TestPlugin', {
       
  6818 		 *   TestPlugin: function(ed, url) {
       
  6819 		 *   ed.on('click', function(e) {
       
  6820 		 *      ed.windowManager.alert('Hello World!');
       
  6821 		 *   });
       
  6822 		 *   }
       
  6823 		 * });
       
  6824 		 *
       
  6825 		 * // Register plugin using the add method
       
  6826 		 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
       
  6827 		 *
       
  6828 		 * // Initialize TinyMCE
       
  6829 		 * tinymce.init({
       
  6830 		 *  ...
       
  6831 		 *  plugins: '-test' // Init the plugin but don't try to load it
       
  6832 		 * });
       
  6833 		 */
       
  6834 		add: function(id, addOn, dependencies) {
       
  6835 			this.items.push(addOn);
       
  6836 			this.lookup[id] = {instance: addOn, dependencies: dependencies};
       
  6837 
       
  6838 			return addOn;
       
  6839 		},
       
  6840 
       
  6841 		createUrl: function(baseUrl, dep) {
       
  6842 			if (typeof dep === "object") {
       
  6843 				return dep;
       
  6844 			} else {
       
  6845 				return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
       
  6846 			}
       
  6847 		},
       
  6848 
       
  6849 		/**
       
  6850 		 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
       
  6851 		 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
       
  6852 		 * components are put together into the plugin.js file and compressed correctly.
       
  6853 		 *
       
  6854 		 * @method addComponents
       
  6855 		 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
       
  6856 		 * @param {Array} scripts Array containing the names of the scripts to load.
       
  6857 		 */
       
  6858 		addComponents: function(pluginName, scripts) {
       
  6859 			var pluginUrl = this.urls[pluginName];
       
  6860 
       
  6861 			each(scripts, function(script) {
       
  6862 				ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
       
  6863 			});
       
  6864 		},
       
  6865 
       
  6866 		/**
       
  6867 		 * Loads an add-on from a specific url.
       
  6868 		 *
       
  6869 		 * @method load
       
  6870 		 * @param {String} name Short name of the add-on that gets loaded.
       
  6871 		 * @param {String} addOnUrl URL to the add-on that will get loaded.
       
  6872 		 * @param {function} callback Optional callback to execute ones the add-on is loaded.
       
  6873 		 * @param {Object} scope Optional scope to execute the callback in.
       
  6874 		 * @example
       
  6875 		 * // Loads a plugin from an external URL
       
  6876 		 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
       
  6877 		 *
       
  6878 		 * // Initialize TinyMCE
       
  6879 		 * tinymce.init({
       
  6880 		 *  ...
       
  6881 		 *  plugins: '-myplugin' // Don't try to load it again
       
  6882 		 * });
       
  6883 		 */
       
  6884 		load: function(name, addOnUrl, callback, scope) {
       
  6885 			var self = this, url = addOnUrl;
       
  6886 
       
  6887 			function loadDependencies() {
       
  6888 				var dependencies = self.dependencies(name);
       
  6889 
       
  6890 				each(dependencies, function(dep) {
       
  6891 					var newUrl = self.createUrl(addOnUrl, dep);
       
  6892 
       
  6893 					self.load(newUrl.resource, newUrl, undefined, undefined);
       
  6894 				});
       
  6895 
       
  6896 				if (callback) {
       
  6897 					if (scope) {
       
  6898 						callback.call(scope);
       
  6899 					} else {
       
  6900 						callback.call(ScriptLoader);
       
  6901 					}
       
  6902 				}
       
  6903 			}
       
  6904 
       
  6905 			if (self.urls[name]) {
       
  6906 				return;
       
  6907 			}
       
  6908 
       
  6909 			if (typeof addOnUrl === "object") {
       
  6910 				url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
       
  6911 			}
       
  6912 
       
  6913 			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
       
  6914 				url = AddOnManager.baseURL + '/' + url;
       
  6915 			}
       
  6916 
       
  6917 			self.urls[name] = url.substring(0, url.lastIndexOf('/'));
       
  6918 
       
  6919 			if (self.lookup[name]) {
       
  6920 				loadDependencies();
       
  6921 			} else {
       
  6922 				ScriptLoader.ScriptLoader.add(url, loadDependencies, scope);
       
  6923 			}
       
  6924 		}
       
  6925 	};
       
  6926 
       
  6927 	AddOnManager.PluginManager = new AddOnManager();
       
  6928 	AddOnManager.ThemeManager = new AddOnManager();
       
  6929 
       
  6930 	return AddOnManager;
       
  6931 });
       
  6932 
       
  6933 /**
       
  6934  * TinyMCE theme class.
       
  6935  *
       
  6936  * @class tinymce.Theme
       
  6937  */
       
  6938 
       
  6939 /**
       
  6940  * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
       
  6941  *
       
  6942  * @method renderUI
       
  6943  * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
       
  6944  * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
       
  6945  */
       
  6946 
       
  6947 /**
       
  6948  * 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.
       
  6949  *
       
  6950  * @class tinymce.Plugin
       
  6951  * @example
       
  6952  * tinymce.PluginManager.add('example', function(editor, url) {
       
  6953  *     // Add a button that opens a window
       
  6954  *     editor.addButton('example', {
       
  6955  *         text: 'My button',
       
  6956  *         icon: false,
       
  6957  *         onclick: function() {
       
  6958  *             // Open window
       
  6959  *             editor.windowManager.open({
       
  6960  *                 title: 'Example plugin',
       
  6961  *                 body: [
       
  6962  *                     {type: 'textbox', name: 'title', label: 'Title'}
       
  6963  *                 ],
       
  6964  *                 onsubmit: function(e) {
       
  6965  *                     // Insert content when the window form is submitted
       
  6966  *                     editor.insertContent('Title: ' + e.data.title);
       
  6967  *                 }
       
  6968  *             });
       
  6969  *         }
       
  6970  *     });
       
  6971  *
       
  6972  *     // Adds a menu item to the tools menu
       
  6973  *     editor.addMenuItem('example', {
       
  6974  *         text: 'Example plugin',
       
  6975  *         context: 'tools',
       
  6976  *         onclick: function() {
       
  6977  *             // Open window with a specific url
       
  6978  *             editor.windowManager.open({
       
  6979  *                 title: 'TinyMCE site',
       
  6980  *                 url: 'http://www.tinymce.com',
       
  6981  *                 width: 800,
       
  6982  *                 height: 600,
       
  6983  *                 buttons: [{
       
  6984  *                     text: 'Close',
       
  6985  *                     onclick: 'close'
       
  6986  *                 }]
       
  6987  *             });
       
  6988  *         }
       
  6989  *     });
       
  6990  * });
       
  6991  */
       
  6992 
       
  6993 // Included from: js/tinymce/classes/dom/RangeUtils.js
       
  6994 
       
  6995 /**
       
  6996  * RangeUtils.js
       
  6997  *
       
  6998  * Copyright, Moxiecode Systems AB
       
  6999  * Released under LGPL License.
       
  7000  *
       
  7001  * License: http://www.tinymce.com/license
       
  7002  * Contributing: http://www.tinymce.com/contributing
       
  7003  */
       
  7004 
       
  7005 /**
       
  7006  * This class contains a few utility methods for ranges.
       
  7007  *
       
  7008  * @class tinymce.dom.RangeUtils
       
  7009  */
       
  7010 define("tinymce/dom/RangeUtils", [
       
  7011 	"tinymce/util/Tools",
       
  7012 	"tinymce/dom/TreeWalker"
       
  7013 ], function(Tools, TreeWalker) {
       
  7014 	var each = Tools.each;
       
  7015 
       
  7016 	function getEndChild(container, index) {
       
  7017 		var childNodes = container.childNodes;
       
  7018 
       
  7019 		index--;
       
  7020 
       
  7021 		if (index > childNodes.length - 1) {
       
  7022 			index = childNodes.length - 1;
       
  7023 		} else if (index < 0) {
       
  7024 			index = 0;
       
  7025 		}
       
  7026 
       
  7027 		return childNodes[index] || container;
       
  7028 	}
       
  7029 
       
  7030 	function RangeUtils(dom) {
       
  7031 		/**
       
  7032 		 * Walks the specified range like object and executes the callback for each sibling collection it finds.
       
  7033 		 *
       
  7034 		 * @method walk
       
  7035 		 * @param {Object} rng Range like object.
       
  7036 		 * @param {function} callback Callback function to execute for each sibling collection.
       
  7037 		 */
       
  7038 		this.walk = function(rng, callback) {
       
  7039 			var startContainer = rng.startContainer,
       
  7040 				startOffset = rng.startOffset,
       
  7041 				endContainer = rng.endContainer,
       
  7042 				endOffset = rng.endOffset,
       
  7043 				ancestor, startPoint,
       
  7044 				endPoint, node, parent, siblings, nodes;
       
  7045 
       
  7046 			// Handle table cell selection the table plugin enables
       
  7047 			// you to fake select table cells and perform formatting actions on them
       
  7048 			nodes = dom.select('td.mce-item-selected,th.mce-item-selected');
       
  7049 			if (nodes.length > 0) {
       
  7050 				each(nodes, function(node) {
       
  7051 					callback([node]);
       
  7052 				});
       
  7053 
       
  7054 				return;
       
  7055 			}
       
  7056 
       
  7057 			/**
       
  7058 			 * Excludes start/end text node if they are out side the range
       
  7059 			 *
       
  7060 			 * @private
       
  7061 			 * @param {Array} nodes Nodes to exclude items from.
       
  7062 			 * @return {Array} Array with nodes excluding the start/end container if needed.
       
  7063 			 */
       
  7064 			function exclude(nodes) {
       
  7065 				var node;
       
  7066 
       
  7067 				// First node is excluded
       
  7068 				node = nodes[0];
       
  7069 				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
       
  7070 					nodes.splice(0, 1);
       
  7071 				}
       
  7072 
       
  7073 				// Last node is excluded
       
  7074 				node = nodes[nodes.length - 1];
       
  7075 				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
       
  7076 					nodes.splice(nodes.length - 1, 1);
       
  7077 				}
       
  7078 
       
  7079 				return nodes;
       
  7080 			}
       
  7081 
       
  7082 			/**
       
  7083 			 * Collects siblings
       
  7084 			 *
       
  7085 			 * @private
       
  7086 			 * @param {Node} node Node to collect siblings from.
       
  7087 			 * @param {String} name Name of the sibling to check for.
       
  7088 			 * @return {Array} Array of collected siblings.
       
  7089 			 */
       
  7090 			function collectSiblings(node, name, end_node) {
       
  7091 				var siblings = [];
       
  7092 
       
  7093 				for (; node && node != end_node; node = node[name]) {
       
  7094 					siblings.push(node);
       
  7095 				}
       
  7096 
       
  7097 				return siblings;
       
  7098 			}
       
  7099 
       
  7100 			/**
       
  7101 			 * Find an end point this is the node just before the common ancestor root.
       
  7102 			 *
       
  7103 			 * @private
       
  7104 			 * @param {Node} node Node to start at.
       
  7105 			 * @param {Node} root Root/ancestor element to stop just before.
       
  7106 			 * @return {Node} Node just before the root element.
       
  7107 			 */
       
  7108 			function findEndPoint(node, root) {
       
  7109 				do {
       
  7110 					if (node.parentNode == root) {
       
  7111 						return node;
       
  7112 					}
       
  7113 
       
  7114 					node = node.parentNode;
       
  7115 				} while (node);
       
  7116 			}
       
  7117 
       
  7118 			function walkBoundary(start_node, end_node, next) {
       
  7119 				var siblingName = next ? 'nextSibling' : 'previousSibling';
       
  7120 
       
  7121 				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
       
  7122 					parent = node.parentNode;
       
  7123 					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
       
  7124 
       
  7125 					if (siblings.length) {
       
  7126 						if (!next) {
       
  7127 							siblings.reverse();
       
  7128 						}
       
  7129 
       
  7130 						callback(exclude(siblings));
       
  7131 					}
       
  7132 				}
       
  7133 			}
       
  7134 
       
  7135 			// If index based start position then resolve it
       
  7136 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
       
  7137 				startContainer = startContainer.childNodes[startOffset];
       
  7138 			}
       
  7139 
       
  7140 			// If index based end position then resolve it
       
  7141 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
       
  7142 				endContainer = getEndChild(endContainer, endOffset);
       
  7143 			}
       
  7144 
       
  7145 			// Same container
       
  7146 			if (startContainer == endContainer) {
       
  7147 				return callback(exclude([startContainer]));
       
  7148 			}
       
  7149 
       
  7150 			// Find common ancestor and end points
       
  7151 			ancestor = dom.findCommonAncestor(startContainer, endContainer);
       
  7152 
       
  7153 			// Process left side
       
  7154 			for (node = startContainer; node; node = node.parentNode) {
       
  7155 				if (node === endContainer) {
       
  7156 					return walkBoundary(startContainer, ancestor, true);
       
  7157 				}
       
  7158 
       
  7159 				if (node === ancestor) {
       
  7160 					break;
       
  7161 				}
       
  7162 			}
       
  7163 
       
  7164 			// Process right side
       
  7165 			for (node = endContainer; node; node = node.parentNode) {
       
  7166 				if (node === startContainer) {
       
  7167 					return walkBoundary(endContainer, ancestor);
       
  7168 				}
       
  7169 
       
  7170 				if (node === ancestor) {
       
  7171 					break;
       
  7172 				}
       
  7173 			}
       
  7174 
       
  7175 			// Find start/end point
       
  7176 			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
       
  7177 			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
       
  7178 
       
  7179 			// Walk left leaf
       
  7180 			walkBoundary(startContainer, startPoint, true);
       
  7181 
       
  7182 			// Walk the middle from start to end point
       
  7183 			siblings = collectSiblings(
       
  7184 				startPoint == startContainer ? startPoint : startPoint.nextSibling,
       
  7185 				'nextSibling',
       
  7186 				endPoint == endContainer ? endPoint.nextSibling : endPoint
       
  7187 			);
       
  7188 
       
  7189 			if (siblings.length) {
       
  7190 				callback(exclude(siblings));
       
  7191 			}
       
  7192 
       
  7193 			// Walk right leaf
       
  7194 			walkBoundary(endContainer, endPoint);
       
  7195 		};
       
  7196 
       
  7197 		/**
       
  7198 		 * Splits the specified range at it's start/end points.
       
  7199 		 *
       
  7200 		 * @private
       
  7201 		 * @param {Range/RangeObject} rng Range to split.
       
  7202 		 * @return {Object} Range position object.
       
  7203 		 */
       
  7204 		this.split = function(rng) {
       
  7205 			var startContainer = rng.startContainer,
       
  7206 				startOffset = rng.startOffset,
       
  7207 				endContainer = rng.endContainer,
       
  7208 				endOffset = rng.endOffset;
       
  7209 
       
  7210 			function splitText(node, offset) {
       
  7211 				return node.splitText(offset);
       
  7212 			}
       
  7213 
       
  7214 			// Handle single text node
       
  7215 			if (startContainer == endContainer && startContainer.nodeType == 3) {
       
  7216 				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
       
  7217 					endContainer = splitText(startContainer, startOffset);
       
  7218 					startContainer = endContainer.previousSibling;
       
  7219 
       
  7220 					if (endOffset > startOffset) {
       
  7221 						endOffset = endOffset - startOffset;
       
  7222 						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
       
  7223 						endOffset = endContainer.nodeValue.length;
       
  7224 						startOffset = 0;
       
  7225 					} else {
       
  7226 						endOffset = 0;
       
  7227 					}
       
  7228 				}
       
  7229 			} else {
       
  7230 				// Split startContainer text node if needed
       
  7231 				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
       
  7232 					startContainer = splitText(startContainer, startOffset);
       
  7233 					startOffset = 0;
       
  7234 				}
       
  7235 
       
  7236 				// Split endContainer text node if needed
       
  7237 				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
       
  7238 					endContainer = splitText(endContainer, endOffset).previousSibling;
       
  7239 					endOffset = endContainer.nodeValue.length;
       
  7240 				}
       
  7241 			}
       
  7242 
       
  7243 			return {
       
  7244 				startContainer: startContainer,
       
  7245 				startOffset: startOffset,
       
  7246 				endContainer: endContainer,
       
  7247 				endOffset: endOffset
       
  7248 			};
       
  7249 		};
       
  7250 
       
  7251 		/**
       
  7252 		 * Normalizes the specified range by finding the closest best suitable caret location.
       
  7253 		 *
       
  7254 		 * @private
       
  7255 		 * @param {Range} rng Range to normalize.
       
  7256 		 * @return {Boolean} True/false if the specified range was normalized or not.
       
  7257 		 */
       
  7258 		this.normalize = function(rng) {
       
  7259 			var normalized, collapsed;
       
  7260 
       
  7261 			function normalizeEndPoint(start) {
       
  7262 				var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
       
  7263 				var directionLeft, isAfterNode;
       
  7264 
       
  7265 				function hasBrBeforeAfter(node, left) {
       
  7266 					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
       
  7267 
       
  7268 					while ((node = walker[left ? 'prev' : 'next']())) {
       
  7269 						if (node.nodeName === "BR") {
       
  7270 							return true;
       
  7271 						}
       
  7272 					}
       
  7273 				}
       
  7274 
       
  7275 				function isPrevNode(node, name) {
       
  7276 					return node.previousSibling && node.previousSibling.nodeName == name;
       
  7277 				}
       
  7278 
       
  7279 				// Walks the dom left/right to find a suitable text node to move the endpoint into
       
  7280 				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
       
  7281 				function findTextNodeRelative(left, startNode) {
       
  7282 					var walker, lastInlineElement, parentBlockContainer;
       
  7283 
       
  7284 					startNode = startNode || container;
       
  7285 					parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body;
       
  7286 
       
  7287 					// Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680
       
  7288 					// This: <p><br>|</p> becomes <p>|<br></p>
       
  7289 					if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) {
       
  7290 						container = startNode.parentNode;
       
  7291 						offset = dom.nodeIndex(startNode);
       
  7292 						normalized = true;
       
  7293 						return;
       
  7294 					}
       
  7295 
       
  7296 					// Walk left until we hit a text node we can move to or a block/br/img
       
  7297 					walker = new TreeWalker(startNode, parentBlockContainer);
       
  7298 					while ((node = walker[left ? 'prev' : 'next']())) {
       
  7299 						// Break if we hit a non content editable node
       
  7300 						if (dom.getContentEditableParent(node) === "false") {
       
  7301 							return;
       
  7302 						}
       
  7303 
       
  7304 						// Found text node that has a length
       
  7305 						if (node.nodeType === 3 && node.nodeValue.length > 0) {
       
  7306 							container = node;
       
  7307 							offset = left ? node.nodeValue.length : 0;
       
  7308 							normalized = true;
       
  7309 							return;
       
  7310 						}
       
  7311 
       
  7312 						// Break if we find a block or a BR/IMG/INPUT etc
       
  7313 						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
  7314 							return;
       
  7315 						}
       
  7316 
       
  7317 						lastInlineElement = node;
       
  7318 					}
       
  7319 
       
  7320 					// Only fetch the last inline element when in caret mode for now
       
  7321 					if (collapsed && lastInlineElement) {
       
  7322 						container = lastInlineElement;
       
  7323 						normalized = true;
       
  7324 						offset = 0;
       
  7325 					}
       
  7326 				}
       
  7327 
       
  7328 				container = rng[(start ? 'start' : 'end') + 'Container'];
       
  7329 				offset = rng[(start ? 'start' : 'end') + 'Offset'];
       
  7330 				isAfterNode = container.nodeType == 1 && offset === container.childNodes.length;
       
  7331 				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
       
  7332 				directionLeft = start;
       
  7333 
       
  7334 				if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
       
  7335 					directionLeft = false;
       
  7336 				}
       
  7337 
       
  7338 				// If the container is a document move it to the body element
       
  7339 				if (container.nodeType === 9) {
       
  7340 					container = dom.getRoot();
       
  7341 					offset = 0;
       
  7342 				}
       
  7343 
       
  7344 				// If the container is body try move it into the closest text node or position
       
  7345 				if (container === body) {
       
  7346 					// If start is before/after a image, table etc
       
  7347 					if (directionLeft) {
       
  7348 						node = container.childNodes[offset > 0 ? offset - 1 : 0];
       
  7349 						if (node) {
       
  7350 							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
       
  7351 								return;
       
  7352 							}
       
  7353 						}
       
  7354 					}
       
  7355 
       
  7356 					// Resolve the index
       
  7357 					if (container.hasChildNodes()) {
       
  7358 						offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
       
  7359 						container = container.childNodes[offset];
       
  7360 						offset = 0;
       
  7361 
       
  7362 						// Don't walk into elements that doesn't have any child nodes like a IMG
       
  7363 						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
       
  7364 							// Walk the DOM to find a text node to place the caret at or a BR
       
  7365 							node = container;
       
  7366 							walker = new TreeWalker(container, body);
       
  7367 
       
  7368 							do {
       
  7369 								// Found a text node use that position
       
  7370 								if (node.nodeType === 3 && node.nodeValue.length > 0) {
       
  7371 									offset = directionLeft ? 0 : node.nodeValue.length;
       
  7372 									container = node;
       
  7373 									normalized = true;
       
  7374 									break;
       
  7375 								}
       
  7376 
       
  7377 								// Found a BR/IMG element that we can place the caret before
       
  7378 								if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
  7379 									offset = dom.nodeIndex(node);
       
  7380 									container = node.parentNode;
       
  7381 
       
  7382 									// Put caret after image when moving the end point
       
  7383 									if (node.nodeName == "IMG" && !directionLeft) {
       
  7384 										offset++;
       
  7385 									}
       
  7386 
       
  7387 									normalized = true;
       
  7388 									break;
       
  7389 								}
       
  7390 							} while ((node = (directionLeft ? walker.next() : walker.prev())));
       
  7391 						}
       
  7392 					}
       
  7393 				}
       
  7394 
       
  7395 				// Lean the caret to the left if possible
       
  7396 				if (collapsed) {
       
  7397 					// So this: <b>x</b><i>|x</i>
       
  7398 					// Becomes: <b>x|</b><i>x</i>
       
  7399 					// Seems that only gecko has issues with this
       
  7400 					if (container.nodeType === 3 && offset === 0) {
       
  7401 						findTextNodeRelative(true);
       
  7402 					}
       
  7403 
       
  7404 					// Lean left into empty inline elements when the caret is before a BR
       
  7405 					// So this: <i><b></b><i>|<br></i>
       
  7406 					// Becomes: <i><b>|</b><i><br></i>
       
  7407 					// Seems that only gecko has issues with this.
       
  7408 					// Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p>
       
  7409 					if (container.nodeType === 1) {
       
  7410 						node = container.childNodes[offset];
       
  7411 
       
  7412 						// Offset is after the containers last child
       
  7413 						// then use the previous child for normalization
       
  7414 						if (!node) {
       
  7415 							node = container.childNodes[offset - 1];
       
  7416 						}
       
  7417 
       
  7418 						if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
       
  7419 							!hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
       
  7420 							findTextNodeRelative(true, node);
       
  7421 						}
       
  7422 					}
       
  7423 				}
       
  7424 
       
  7425 				// Lean the start of the selection right if possible
       
  7426 				// So this: x[<b>x]</b>
       
  7427 				// Becomes: x<b>[x]</b>
       
  7428 				if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
       
  7429 					findTextNodeRelative(false);
       
  7430 				}
       
  7431 
       
  7432 				// Set endpoint if it was normalized
       
  7433 				if (normalized) {
       
  7434 					rng['set' + (start ? 'Start' : 'End')](container, offset);
       
  7435 				}
       
  7436 			}
       
  7437 
       
  7438 			collapsed = rng.collapsed;
       
  7439 
       
  7440 			normalizeEndPoint(true);
       
  7441 
       
  7442 			if (!collapsed) {
       
  7443 				normalizeEndPoint();
       
  7444 			}
       
  7445 
       
  7446 			// If it was collapsed then make sure it still is
       
  7447 			if (normalized && collapsed) {
       
  7448 				rng.collapse(true);
       
  7449 			}
       
  7450 
       
  7451 			return normalized;
       
  7452 		};
       
  7453 	}
       
  7454 
       
  7455 	/**
       
  7456 	 * Compares two ranges and checks if they are equal.
       
  7457 	 *
       
  7458 	 * @static
       
  7459 	 * @method compareRanges
       
  7460 	 * @param {DOMRange} rng1 First range to compare.
       
  7461 	 * @param {DOMRange} rng2 First range to compare.
       
  7462 	 * @return {Boolean} true/false if the ranges are equal.
       
  7463 	 */
       
  7464 	RangeUtils.compareRanges = function(rng1, rng2) {
       
  7465 		if (rng1 && rng2) {
       
  7466 			// Compare native IE ranges
       
  7467 			if (rng1.item || rng1.duplicate) {
       
  7468 				// Both are control ranges and the selected element matches
       
  7469 				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
       
  7470 					return true;
       
  7471 				}
       
  7472 
       
  7473 				// Both are text ranges and the range matches
       
  7474 				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
       
  7475 					return true;
       
  7476 				}
       
  7477 			} else {
       
  7478 				// Compare w3c ranges
       
  7479 				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
       
  7480 			}
       
  7481 		}
       
  7482 
       
  7483 		return false;
       
  7484 	};
       
  7485 
       
  7486 	/**
       
  7487 	 * Gets the caret range for the given x/y location.
       
  7488 	 *
       
  7489 	 * @static
       
  7490 	 * @method getCaretRangeFromPoint
       
  7491 	 * @param {Number} x X coordinate for range
       
  7492 	 * @param {Number} y Y coordinate for range
       
  7493 	 * @param {Document} doc Document that x/y are relative to
       
  7494 	 * @returns {Range} caret range
       
  7495 	 */
       
  7496 	RangeUtils.getCaretRangeFromPoint = function(x, y, doc) {
       
  7497 		var rng, point;
       
  7498 
       
  7499 		if (doc.caretPositionFromPoint) {
       
  7500 			point = doc.caretPositionFromPoint(x, y);
       
  7501 			rng = doc.createRange();
       
  7502 			rng.setStart(point.offsetNode, point.offset);
       
  7503 			rng.collapse(true);
       
  7504 		} else if (doc.caretRangeFromPoint) {
       
  7505 			rng = doc.caretRangeFromPoint(x, y);
       
  7506 		} else if (doc.body.createTextRange) {
       
  7507 			rng = doc.body.createTextRange();
       
  7508 
       
  7509 			try {
       
  7510 				rng.moveToPoint(x, y);
       
  7511 				rng.collapse(true);
       
  7512 			} catch (ex) {
       
  7513 				// Append to top or bottom depending on drop location
       
  7514 				rng.collapse(y < doc.body.clientHeight);
       
  7515 			}
       
  7516 		}
       
  7517 
       
  7518 		return rng;
       
  7519 	};
       
  7520 
       
  7521 	RangeUtils.getNode = function(container, offset) {
       
  7522 		if (container.nodeType == 1 && container.hasChildNodes()) {
       
  7523 			if (offset >= container.childNodes.length) {
       
  7524 				offset = container.childNodes.length - 1;
       
  7525 			}
       
  7526 
       
  7527 			container = container.childNodes[offset];
       
  7528 		}
       
  7529 
       
  7530 		return container;
       
  7531 	};
       
  7532 
       
  7533 	return RangeUtils;
       
  7534 });
       
  7535 
       
  7536 // Included from: js/tinymce/classes/NodeChange.js
       
  7537 
       
  7538 /**
       
  7539  * NodeChange.js
       
  7540  *
       
  7541  * Copyright, Moxiecode Systems AB
       
  7542  * Released under LGPL License.
       
  7543  *
       
  7544  * License: http://www.tinymce.com/license
       
  7545  * Contributing: http://www.tinymce.com/contributing
       
  7546  */
       
  7547 
       
  7548 /**
       
  7549  * This class handles the nodechange event dispatching both manual and though selection change events.
       
  7550  *
       
  7551  * @class tinymce.NodeChange
       
  7552  * @private
       
  7553  */
       
  7554 define("tinymce/NodeChange", [
       
  7555 	"tinymce/dom/RangeUtils",
       
  7556 	"tinymce/Env"
       
  7557 ], function(RangeUtils, Env) {
       
  7558 	return function(editor) {
       
  7559 		var lastRng, lastPath = [];
       
  7560 
       
  7561 		/**
       
  7562 		 * Returns true/false if the current element path has been changed or not.
       
  7563 		 *
       
  7564 		 * @private
       
  7565 		 * @return {Boolean} True if the element path is the same false if it's not.
       
  7566 		 */
       
  7567 		function isSameElementPath(startElm) {
       
  7568 			var i, currentPath;
       
  7569 
       
  7570 			currentPath = editor.$(startElm).parentsUntil(editor.getBody()).add(startElm);
       
  7571 			if (currentPath.length === lastPath.length) {
       
  7572 				for (i = currentPath.length; i >= 0; i--) {
       
  7573 					if (currentPath[i] !== lastPath[i]) {
       
  7574 						break;
       
  7575 					}
       
  7576 				}
       
  7577 
       
  7578 				if (i === -1) {
       
  7579 					lastPath = currentPath;
       
  7580 					return true;
       
  7581 				}
       
  7582 			}
       
  7583 
       
  7584 			lastPath = currentPath;
       
  7585 
       
  7586 			return false;
       
  7587 		}
       
  7588 
       
  7589 		// Gecko doesn't support the "selectionchange" event
       
  7590 		if (!('onselectionchange' in editor.getDoc())) {
       
  7591 			editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) {
       
  7592 				var nativeRng, fakeRng;
       
  7593 
       
  7594 				// Since DOM Ranges mutate on modification
       
  7595 				// of the DOM we need to clone it's contents
       
  7596 				nativeRng = editor.selection.getRng();
       
  7597 				fakeRng = {
       
  7598 					startContainer: nativeRng.startContainer,
       
  7599 					startOffset: nativeRng.startOffset,
       
  7600 					endContainer: nativeRng.endContainer,
       
  7601 					endOffset: nativeRng.endOffset
       
  7602 				};
       
  7603 
       
  7604 				// Always treat nodechange as a selectionchange since applying
       
  7605 				// formatting to the current range wouldn't update the range but it's parent
       
  7606 				if (e.type == 'nodechange' || !RangeUtils.compareRanges(fakeRng, lastRng)) {
       
  7607 					editor.fire('SelectionChange');
       
  7608 				}
       
  7609 
       
  7610 				lastRng = fakeRng;
       
  7611 			});
       
  7612 		}
       
  7613 
       
  7614 		// IE has a bug where it fires a selectionchange on right click that has a range at the start of the body
       
  7615 		// When the contextmenu event fires the selection is located at the right location
       
  7616 		editor.on('contextmenu', function() {
       
  7617 			editor.fire('SelectionChange');
       
  7618 		});
       
  7619 
       
  7620 		// Selection change is delayed ~200ms on IE when you click inside the current range
       
  7621 		editor.on('SelectionChange', function() {
       
  7622 			var startElm = editor.selection.getStart(true);
       
  7623 
       
  7624 			// IE 8 will fire a selectionchange event with an incorrect selection
       
  7625 			// when focusing out of table cells. Click inside cell -> toolbar = Invalid SelectionChange event
       
  7626 			if (!Env.range && editor.selection.isCollapsed()) {
       
  7627 				return;
       
  7628 			}
       
  7629 
       
  7630 			if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
       
  7631 				editor.nodeChanged({selectionChange: true});
       
  7632 			}
       
  7633 		});
       
  7634 
       
  7635 		// Fire an extra nodeChange on mouseup for compatibility reasons
       
  7636 		editor.on('MouseUp', function(e) {
       
  7637 			if (!e.isDefaultPrevented()) {
       
  7638 				// Delay nodeChanged call for WebKit edge case issue where the range
       
  7639 				// isn't updated until after you click outside a selected image
       
  7640 				if (editor.selection.getNode().nodeName == 'IMG') {
       
  7641 					setTimeout(function() {
       
  7642 						editor.nodeChanged();
       
  7643 					}, 0);
       
  7644 				} else {
       
  7645 					editor.nodeChanged();
       
  7646 				}
       
  7647 			}
       
  7648 		});
       
  7649 
       
  7650 		/**
       
  7651 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
       
  7652 		 * need to update the UI states or element path etc.
       
  7653 		 *
       
  7654 		 * @method nodeChanged
       
  7655 		 * @param {Object} args Optional args to pass to NodeChange event handlers.
       
  7656 		 */
       
  7657 		this.nodeChanged = function(args) {
       
  7658 			var selection = editor.selection, node, parents, root;
       
  7659 
       
  7660 			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
       
  7661 			if (editor.initialized && selection && !editor.settings.disable_nodechange && !editor.settings.readonly) {
       
  7662 				// Get start node
       
  7663 				root = editor.getBody();
       
  7664 				node = selection.getStart() || root;
       
  7665 				node = node.ownerDocument != editor.getDoc() ? editor.getBody() : node;
       
  7666 
       
  7667 				// Edge case for <p>|<img></p>
       
  7668 				if (node.nodeName == 'IMG' && selection.isCollapsed()) {
       
  7669 					node = node.parentNode;
       
  7670 				}
       
  7671 
       
  7672 				// Get parents and add them to object
       
  7673 				parents = [];
       
  7674 				editor.dom.getParent(node, function(node) {
       
  7675 					if (node === root) {
       
  7676 						return true;
       
  7677 					}
       
  7678 
       
  7679 					parents.push(node);
       
  7680 				});
       
  7681 
       
  7682 				args = args || {};
       
  7683 				args.element = node;
       
  7684 				args.parents = parents;
       
  7685 
       
  7686 				editor.fire('NodeChange', args);
       
  7687 			}
       
  7688 		};
       
  7689 	};
       
  7690 });
       
  7691 
       
  7692 // Included from: js/tinymce/classes/html/Node.js
       
  7693 
       
  7694 /**
       
  7695  * Node.js
       
  7696  *
       
  7697  * Copyright, Moxiecode Systems AB
       
  7698  * Released under LGPL License.
       
  7699  *
       
  7700  * License: http://www.tinymce.com/license
       
  7701  * Contributing: http://www.tinymce.com/contributing
       
  7702  */
       
  7703 
       
  7704 /**
       
  7705  * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
       
  7706  *
       
  7707  * @example
       
  7708  * var node = new tinymce.html.Node('strong', 1);
       
  7709  * someRoot.append(node);
       
  7710  *
       
  7711  * @class tinymce.html.Node
       
  7712  * @version 3.4
       
  7713  */
       
  7714 define("tinymce/html/Node", [], function() {
       
  7715 	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
       
  7716 		'#text': 3,
       
  7717 		'#comment': 8,
       
  7718 		'#cdata': 4,
       
  7719 		'#pi': 7,
       
  7720 		'#doctype': 10,
       
  7721 		'#document-fragment': 11
       
  7722 	};
       
  7723 
       
  7724 	// Walks the tree left/right
       
  7725 	function walk(node, root_node, prev) {
       
  7726 		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
       
  7727 
       
  7728 		// Walk into nodes if it has a start
       
  7729 		if (node[startName]) {
       
  7730 			return node[startName];
       
  7731 		}
       
  7732 
       
  7733 		// Return the sibling if it has one
       
  7734 		if (node !== root_node) {
       
  7735 			sibling = node[siblingName];
       
  7736 
       
  7737 			if (sibling) {
       
  7738 				return sibling;
       
  7739 			}
       
  7740 
       
  7741 			// Walk up the parents to look for siblings
       
  7742 			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
       
  7743 				sibling = parent[siblingName];
       
  7744 
       
  7745 				if (sibling) {
       
  7746 					return sibling;
       
  7747 				}
       
  7748 			}
       
  7749 		}
       
  7750 	}
       
  7751 
       
  7752 	/**
       
  7753 	 * Constructs a new Node instance.
       
  7754 	 *
       
  7755 	 * @constructor
       
  7756 	 * @method Node
       
  7757 	 * @param {String} name Name of the node type.
       
  7758 	 * @param {Number} type Numeric type representing the node.
       
  7759 	 */
       
  7760 	function Node(name, type) {
       
  7761 		this.name = name;
       
  7762 		this.type = type;
       
  7763 
       
  7764 		if (type === 1) {
       
  7765 			this.attributes = [];
       
  7766 			this.attributes.map = {};
       
  7767 		}
       
  7768 	}
       
  7769 
       
  7770 	Node.prototype = {
       
  7771 		/**
       
  7772 		 * Replaces the current node with the specified one.
       
  7773 		 *
       
  7774 		 * @example
       
  7775 		 * someNode.replace(someNewNode);
       
  7776 		 *
       
  7777 		 * @method replace
       
  7778 		 * @param {tinymce.html.Node} node Node to replace the current node with.
       
  7779 		 * @return {tinymce.html.Node} The old node that got replaced.
       
  7780 		 */
       
  7781 		replace: function(node) {
       
  7782 			var self = this;
       
  7783 
       
  7784 			if (node.parent) {
       
  7785 				node.remove();
       
  7786 			}
       
  7787 
       
  7788 			self.insert(node, self);
       
  7789 			self.remove();
       
  7790 
       
  7791 			return self;
       
  7792 		},
       
  7793 
       
  7794 		/**
       
  7795 		 * Gets/sets or removes an attribute by name.
       
  7796 		 *
       
  7797 		 * @example
       
  7798 		 * someNode.attr("name", "value"); // Sets an attribute
       
  7799 		 * console.log(someNode.attr("name")); // Gets an attribute
       
  7800 		 * someNode.attr("name", null); // Removes an attribute
       
  7801 		 *
       
  7802 		 * @method attr
       
  7803 		 * @param {String} name Attribute name to set or get.
       
  7804 		 * @param {String} value Optional value to set.
       
  7805 		 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
       
  7806 		 */
       
  7807 		attr: function(name, value) {
       
  7808 			var self = this, attrs, i, undef;
       
  7809 
       
  7810 			if (typeof name !== "string") {
       
  7811 				for (i in name) {
       
  7812 					self.attr(i, name[i]);
       
  7813 				}
       
  7814 
       
  7815 				return self;
       
  7816 			}
       
  7817 
       
  7818 			if ((attrs = self.attributes)) {
       
  7819 				if (value !== undef) {
       
  7820 					// Remove attribute
       
  7821 					if (value === null) {
       
  7822 						if (name in attrs.map) {
       
  7823 							delete attrs.map[name];
       
  7824 
       
  7825 							i = attrs.length;
       
  7826 							while (i--) {
       
  7827 								if (attrs[i].name === name) {
       
  7828 									attrs = attrs.splice(i, 1);
       
  7829 									return self;
       
  7830 								}
       
  7831 							}
       
  7832 						}
       
  7833 
       
  7834 						return self;
       
  7835 					}
       
  7836 
       
  7837 					// Set attribute
       
  7838 					if (name in attrs.map) {
       
  7839 						// Set attribute
       
  7840 						i = attrs.length;
       
  7841 						while (i--) {
       
  7842 							if (attrs[i].name === name) {
       
  7843 								attrs[i].value = value;
       
  7844 								break;
       
  7845 							}
       
  7846 						}
       
  7847 					} else {
       
  7848 						attrs.push({name: name, value: value});
       
  7849 					}
       
  7850 
       
  7851 					attrs.map[name] = value;
       
  7852 
       
  7853 					return self;
       
  7854 				} else {
       
  7855 					return attrs.map[name];
       
  7856 				}
       
  7857 			}
       
  7858 		},
       
  7859 
       
  7860 		/**
       
  7861 		 * Does a shallow clones the node into a new node. It will also exclude id attributes since
       
  7862 		 * there should only be one id per document.
       
  7863 		 *
       
  7864 		 * @example
       
  7865 		 * var clonedNode = node.clone();
       
  7866 		 *
       
  7867 		 * @method clone
       
  7868 		 * @return {tinymce.html.Node} New copy of the original node.
       
  7869 		 */
       
  7870 		clone: function() {
       
  7871 			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
       
  7872 
       
  7873 			// Clone element attributes
       
  7874 			if ((selfAttrs = self.attributes)) {
       
  7875 				cloneAttrs = [];
       
  7876 				cloneAttrs.map = {};
       
  7877 
       
  7878 				for (i = 0, l = selfAttrs.length; i < l; i++) {
       
  7879 					selfAttr = selfAttrs[i];
       
  7880 
       
  7881 					// Clone everything except id
       
  7882 					if (selfAttr.name !== 'id') {
       
  7883 						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
       
  7884 						cloneAttrs.map[selfAttr.name] = selfAttr.value;
       
  7885 					}
       
  7886 				}
       
  7887 
       
  7888 				clone.attributes = cloneAttrs;
       
  7889 			}
       
  7890 
       
  7891 			clone.value = self.value;
       
  7892 			clone.shortEnded = self.shortEnded;
       
  7893 
       
  7894 			return clone;
       
  7895 		},
       
  7896 
       
  7897 		/**
       
  7898 		 * Wraps the node in in another node.
       
  7899 		 *
       
  7900 		 * @example
       
  7901 		 * node.wrap(wrapperNode);
       
  7902 		 *
       
  7903 		 * @method wrap
       
  7904 		 */
       
  7905 		wrap: function(wrapper) {
       
  7906 			var self = this;
       
  7907 
       
  7908 			self.parent.insert(wrapper, self);
       
  7909 			wrapper.append(self);
       
  7910 
       
  7911 			return self;
       
  7912 		},
       
  7913 
       
  7914 		/**
       
  7915 		 * Unwraps the node in other words it removes the node but keeps the children.
       
  7916 		 *
       
  7917 		 * @example
       
  7918 		 * node.unwrap();
       
  7919 		 *
       
  7920 		 * @method unwrap
       
  7921 		 */
       
  7922 		unwrap: function() {
       
  7923 			var self = this, node, next;
       
  7924 
       
  7925 			for (node = self.firstChild; node;) {
       
  7926 				next = node.next;
       
  7927 				self.insert(node, self, true);
       
  7928 				node = next;
       
  7929 			}
       
  7930 
       
  7931 			self.remove();
       
  7932 		},
       
  7933 
       
  7934 		/**
       
  7935 		 * Removes the node from it's parent.
       
  7936 		 *
       
  7937 		 * @example
       
  7938 		 * node.remove();
       
  7939 		 *
       
  7940 		 * @method remove
       
  7941 		 * @return {tinymce.html.Node} Current node that got removed.
       
  7942 		 */
       
  7943 		remove: function() {
       
  7944 			var self = this, parent = self.parent, next = self.next, prev = self.prev;
       
  7945 
       
  7946 			if (parent) {
       
  7947 				if (parent.firstChild === self) {
       
  7948 					parent.firstChild = next;
       
  7949 
       
  7950 					if (next) {
       
  7951 						next.prev = null;
       
  7952 					}
       
  7953 				} else {
       
  7954 					prev.next = next;
       
  7955 				}
       
  7956 
       
  7957 				if (parent.lastChild === self) {
       
  7958 					parent.lastChild = prev;
       
  7959 
       
  7960 					if (prev) {
       
  7961 						prev.next = null;
       
  7962 					}
       
  7963 				} else {
       
  7964 					next.prev = prev;
       
  7965 				}
       
  7966 
       
  7967 				self.parent = self.next = self.prev = null;
       
  7968 			}
       
  7969 
       
  7970 			return self;
       
  7971 		},
       
  7972 
       
  7973 		/**
       
  7974 		 * Appends a new node as a child of the current node.
       
  7975 		 *
       
  7976 		 * @example
       
  7977 		 * node.append(someNode);
       
  7978 		 *
       
  7979 		 * @method append
       
  7980 		 * @param {tinymce.html.Node} node Node to append as a child of the current one.
       
  7981 		 * @return {tinymce.html.Node} The node that got appended.
       
  7982 		 */
       
  7983 		append: function(node) {
       
  7984 			var self = this, last;
       
  7985 
       
  7986 			if (node.parent) {
       
  7987 				node.remove();
       
  7988 			}
       
  7989 
       
  7990 			last = self.lastChild;
       
  7991 			if (last) {
       
  7992 				last.next = node;
       
  7993 				node.prev = last;
       
  7994 				self.lastChild = node;
       
  7995 			} else {
       
  7996 				self.lastChild = self.firstChild = node;
       
  7997 			}
       
  7998 
       
  7999 			node.parent = self;
       
  8000 
       
  8001 			return node;
       
  8002 		},
       
  8003 
       
  8004 		/**
       
  8005 		 * Inserts a node at a specific position as a child of the current node.
       
  8006 		 *
       
  8007 		 * @example
       
  8008 		 * parentNode.insert(newChildNode, oldChildNode);
       
  8009 		 *
       
  8010 		 * @method insert
       
  8011 		 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
       
  8012 		 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
       
  8013 		 * @param {Boolean} before Optional state to insert the node before the reference node.
       
  8014 		 * @return {tinymce.html.Node} The node that got inserted.
       
  8015 		 */
       
  8016 		insert: function(node, ref_node, before) {
       
  8017 			var parent;
       
  8018 
       
  8019 			if (node.parent) {
       
  8020 				node.remove();
       
  8021 			}
       
  8022 
       
  8023 			parent = ref_node.parent || this;
       
  8024 
       
  8025 			if (before) {
       
  8026 				if (ref_node === parent.firstChild) {
       
  8027 					parent.firstChild = node;
       
  8028 				} else {
       
  8029 					ref_node.prev.next = node;
       
  8030 				}
       
  8031 
       
  8032 				node.prev = ref_node.prev;
       
  8033 				node.next = ref_node;
       
  8034 				ref_node.prev = node;
       
  8035 			} else {
       
  8036 				if (ref_node === parent.lastChild) {
       
  8037 					parent.lastChild = node;
       
  8038 				} else {
       
  8039 					ref_node.next.prev = node;
       
  8040 				}
       
  8041 
       
  8042 				node.next = ref_node.next;
       
  8043 				node.prev = ref_node;
       
  8044 				ref_node.next = node;
       
  8045 			}
       
  8046 
       
  8047 			node.parent = parent;
       
  8048 
       
  8049 			return node;
       
  8050 		},
       
  8051 
       
  8052 		/**
       
  8053 		 * Get all children by name.
       
  8054 		 *
       
  8055 		 * @method getAll
       
  8056 		 * @param {String} name Name of the child nodes to collect.
       
  8057 		 * @return {Array} Array with child nodes matchin the specified name.
       
  8058 		 */
       
  8059 		getAll: function(name) {
       
  8060 			var self = this, node, collection = [];
       
  8061 
       
  8062 			for (node = self.firstChild; node; node = walk(node, self)) {
       
  8063 				if (node.name === name) {
       
  8064 					collection.push(node);
       
  8065 				}
       
  8066 			}
       
  8067 
       
  8068 			return collection;
       
  8069 		},
       
  8070 
       
  8071 		/**
       
  8072 		 * Removes all children of the current node.
       
  8073 		 *
       
  8074 		 * @method empty
       
  8075 		 * @return {tinymce.html.Node} The current node that got cleared.
       
  8076 		 */
       
  8077 		empty: function() {
       
  8078 			var self = this, nodes, i, node;
       
  8079 
       
  8080 			// Remove all children
       
  8081 			if (self.firstChild) {
       
  8082 				nodes = [];
       
  8083 
       
  8084 				// Collect the children
       
  8085 				for (node = self.firstChild; node; node = walk(node, self)) {
       
  8086 					nodes.push(node);
       
  8087 				}
       
  8088 
       
  8089 				// Remove the children
       
  8090 				i = nodes.length;
       
  8091 				while (i--) {
       
  8092 					node = nodes[i];
       
  8093 					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
       
  8094 				}
       
  8095 			}
       
  8096 
       
  8097 			self.firstChild = self.lastChild = null;
       
  8098 
       
  8099 			return self;
       
  8100 		},
       
  8101 
       
  8102 		/**
       
  8103 		 * Returns true/false if the node is to be considered empty or not.
       
  8104 		 *
       
  8105 		 * @example
       
  8106 		 * node.isEmpty({img: true});
       
  8107 		 * @method isEmpty
       
  8108 		 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
       
  8109 		 * @return {Boolean} true/false if the node is empty or not.
       
  8110 		 */
       
  8111 		isEmpty: function(elements) {
       
  8112 			var self = this, node = self.firstChild, i, name;
       
  8113 
       
  8114 			if (node) {
       
  8115 				do {
       
  8116 					if (node.type === 1) {
       
  8117 						// Ignore bogus elements
       
  8118 						if (node.attributes.map['data-mce-bogus']) {
       
  8119 							continue;
       
  8120 						}
       
  8121 
       
  8122 						// Keep empty elements like <img />
       
  8123 						if (elements[node.name]) {
       
  8124 							return false;
       
  8125 						}
       
  8126 
       
  8127 						// Keep bookmark nodes and name attribute like <a name="1"></a>
       
  8128 						i = node.attributes.length;
       
  8129 						while (i--) {
       
  8130 							name = node.attributes[i].name;
       
  8131 							if (name === "name" || name.indexOf('data-mce-bookmark') === 0) {
       
  8132 								return false;
       
  8133 							}
       
  8134 						}
       
  8135 					}
       
  8136 
       
  8137 					// Keep comments
       
  8138 					if (node.type === 8) {
       
  8139 						return false;
       
  8140 					}
       
  8141 
       
  8142 					// Keep non whitespace text nodes
       
  8143 					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
       
  8144 						return false;
       
  8145 					}
       
  8146 				} while ((node = walk(node, self)));
       
  8147 			}
       
  8148 
       
  8149 			return true;
       
  8150 		},
       
  8151 
       
  8152 		/**
       
  8153 		 * Walks to the next or previous node and returns that node or null if it wasn't found.
       
  8154 		 *
       
  8155 		 * @method walk
       
  8156 		 * @param {Boolean} prev Optional previous node state defaults to false.
       
  8157 		 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
       
  8158 		 */
       
  8159 		walk: function(prev) {
       
  8160 			return walk(this, null, prev);
       
  8161 		}
       
  8162 	};
       
  8163 
       
  8164 	/**
       
  8165 	 * Creates a node of a specific type.
       
  8166 	 *
       
  8167 	 * @static
       
  8168 	 * @method create
       
  8169 	 * @param {String} name Name of the node type to create for example "b" or "#text".
       
  8170 	 * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
       
  8171 	 */
       
  8172 	Node.create = function(name, attrs) {
       
  8173 		var node, attrName;
       
  8174 
       
  8175 		// Create node
       
  8176 		node = new Node(name, typeLookup[name] || 1);
       
  8177 
       
  8178 		// Add attributes if needed
       
  8179 		if (attrs) {
       
  8180 			for (attrName in attrs) {
       
  8181 				node.attr(attrName, attrs[attrName]);
       
  8182 			}
       
  8183 		}
       
  8184 
       
  8185 		return node;
       
  8186 	};
       
  8187 
       
  8188 	return Node;
       
  8189 });
       
  8190 
       
  8191 // Included from: js/tinymce/classes/html/Schema.js
       
  8192 
       
  8193 /**
       
  8194  * Schema.js
       
  8195  *
       
  8196  * Copyright, Moxiecode Systems AB
       
  8197  * Released under LGPL License.
       
  8198  *
       
  8199  * License: http://www.tinymce.com/license
       
  8200  * Contributing: http://www.tinymce.com/contributing
       
  8201  */
       
  8202 
       
  8203 /**
       
  8204  * Schema validator class.
       
  8205  *
       
  8206  * @class tinymce.html.Schema
       
  8207  * @example
       
  8208  *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
       
  8209  *    alert('span is valid child of p.');
       
  8210  *
       
  8211  *  if (tinymce.activeEditor.schema.getElementRule('p'))
       
  8212  *    alert('P is a valid element.');
       
  8213  *
       
  8214  * @class tinymce.html.Schema
       
  8215  * @version 3.4
       
  8216  */
       
  8217 define("tinymce/html/Schema", [
       
  8218 	"tinymce/util/Tools"
       
  8219 ], function(Tools) {
       
  8220 	var mapCache = {}, dummyObj = {};
       
  8221 	var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
       
  8222 
       
  8223 	function split(items, delim) {
       
  8224 		return items ? items.split(delim || ' ') : [];
       
  8225 	}
       
  8226 
       
  8227 	/**
       
  8228 	 * Builds a schema lookup table
       
  8229 	 *
       
  8230 	 * @private
       
  8231 	 * @param {String} type html4, html5 or html5-strict schema type.
       
  8232 	 * @return {Object} Schema lookup table.
       
  8233 	 */
       
  8234 	function compileSchema(type) {
       
  8235 		var schema = {}, globalAttributes, blockContent;
       
  8236 		var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
       
  8237 
       
  8238 		function add(name, attributes, children) {
       
  8239 			var ni, i, attributesOrder, args = arguments;
       
  8240 
       
  8241 			function arrayToMap(array, obj) {
       
  8242 				var map = {}, i, l;
       
  8243 
       
  8244 				for (i = 0, l = array.length; i < l; i++) {
       
  8245 					map[array[i]] = obj || {};
       
  8246 				}
       
  8247 
       
  8248 				return map;
       
  8249 			}
       
  8250 
       
  8251 			children = children || [];
       
  8252 			attributes = attributes || "";
       
  8253 
       
  8254 			if (typeof children === "string") {
       
  8255 				children = split(children);
       
  8256 			}
       
  8257 
       
  8258 			// Split string children
       
  8259 			for (i = 3; i < args.length; i++) {
       
  8260 				if (typeof args[i] === "string") {
       
  8261 					args[i] = split(args[i]);
       
  8262 				}
       
  8263 
       
  8264 				children.push.apply(children, args[i]);
       
  8265 			}
       
  8266 
       
  8267 			name = split(name);
       
  8268 			ni = name.length;
       
  8269 			while (ni--) {
       
  8270 				attributesOrder = [].concat(globalAttributes, split(attributes));
       
  8271 				schema[name[ni]] = {
       
  8272 					attributes: arrayToMap(attributesOrder),
       
  8273 					attributesOrder: attributesOrder,
       
  8274 					children: arrayToMap(children, dummyObj)
       
  8275 				};
       
  8276 			}
       
  8277 		}
       
  8278 
       
  8279 		function addAttrs(name, attributes) {
       
  8280 			var ni, schemaItem, i, l;
       
  8281 
       
  8282 			name = split(name);
       
  8283 			ni = name.length;
       
  8284 			attributes = split(attributes);
       
  8285 			while (ni--) {
       
  8286 				schemaItem = schema[name[ni]];
       
  8287 				for (i = 0, l = attributes.length; i < l; i++) {
       
  8288 					schemaItem.attributes[attributes[i]] = {};
       
  8289 					schemaItem.attributesOrder.push(attributes[i]);
       
  8290 				}
       
  8291 			}
       
  8292 		}
       
  8293 
       
  8294 		// Use cached schema
       
  8295 		if (mapCache[type]) {
       
  8296 			return mapCache[type];
       
  8297 		}
       
  8298 
       
  8299 		// Attributes present on all elements
       
  8300 		globalAttributes = split("id accesskey class dir lang style tabindex title");
       
  8301 
       
  8302 		// Event attributes can be opt-in/opt-out
       
  8303 		/*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
       
  8304 				"ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
       
  8305 				"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
       
  8306 				"onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
       
  8307 				"onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
       
  8308 				"onwaiting"
       
  8309 		);*/
       
  8310 
       
  8311 		// Block content elements
       
  8312 		blockContent = split(
       
  8313 			"address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
       
  8314 		);
       
  8315 
       
  8316 		// Phrasing content elements from the HTML5 spec (inline)
       
  8317 		phrasingContent = split(
       
  8318 			"a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
       
  8319 			"label map noscript object q s samp script select small span strong sub sup " +
       
  8320 			"textarea u var #text #comment"
       
  8321 		);
       
  8322 
       
  8323 		// Add HTML5 items to globalAttributes, blockContent, phrasingContent
       
  8324 		if (type != "html4") {
       
  8325 			globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
       
  8326 				"hidden spellcheck translate"));
       
  8327 			blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
       
  8328 			phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output progress time wbr " +
       
  8329 				"video ruby bdi keygen"));
       
  8330 		}
       
  8331 
       
  8332 		// Add HTML4 elements unless it's html5-strict
       
  8333 		if (type != "html5-strict") {
       
  8334 			globalAttributes.push("xml:lang");
       
  8335 
       
  8336 			html4PhrasingContent = split("acronym applet basefont big font strike tt");
       
  8337 			phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
       
  8338 
       
  8339 			each(html4PhrasingContent, function(name) {
       
  8340 				add(name, "", phrasingContent);
       
  8341 			});
       
  8342 
       
  8343 			html4BlockContent = split("center dir isindex noframes");
       
  8344 			blockContent.push.apply(blockContent, html4BlockContent);
       
  8345 
       
  8346 			// Flow content elements from the HTML5 spec (block+inline)
       
  8347 			flowContent = [].concat(blockContent, phrasingContent);
       
  8348 
       
  8349 			each(html4BlockContent, function(name) {
       
  8350 				add(name, "", flowContent);
       
  8351 			});
       
  8352 		}
       
  8353 
       
  8354 		// Flow content elements from the HTML5 spec (block+inline)
       
  8355 		flowContent = flowContent || [].concat(blockContent, phrasingContent);
       
  8356 
       
  8357 		// HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
       
  8358 		// Schema items <element name>, <specific attributes>, <children ..>
       
  8359 		add("html", "manifest", "head body");
       
  8360 		add("head", "", "base command link meta noscript script style title");
       
  8361 		add("title hr noscript br");
       
  8362 		add("base", "href target");
       
  8363 		add("link", "href rel media hreflang type sizes hreflang");
       
  8364 		add("meta", "name http-equiv content charset");
       
  8365 		add("style", "media type scoped");
       
  8366 		add("script", "src async defer type charset");
       
  8367 		add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
       
  8368 				"onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
       
  8369 				"onpopstate onresize onscroll onstorage onunload", flowContent);
       
  8370 		add("address dt dd div caption", "", flowContent);
       
  8371 		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);
       
  8372 		add("blockquote", "cite", flowContent);
       
  8373 		add("ol", "reversed start type", "li");
       
  8374 		add("ul", "", "li");
       
  8375 		add("li", "value", flowContent);
       
  8376 		add("dl", "", "dt dd");
       
  8377 		add("a", "href target rel media hreflang type", phrasingContent);
       
  8378 		add("q", "cite", phrasingContent);
       
  8379 		add("ins del", "cite datetime", flowContent);
       
  8380 		add("img", "src sizes srcset alt usemap ismap width height");
       
  8381 		add("iframe", "src name width height", flowContent);
       
  8382 		add("embed", "src type width height");
       
  8383 		add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
       
  8384 		add("param", "name value");
       
  8385 		add("map", "name", flowContent, "area");
       
  8386 		add("area", "alt coords shape href target rel media hreflang type");
       
  8387 		add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
       
  8388 		add("colgroup", "span", "col");
       
  8389 		add("col", "span");
       
  8390 		add("tbody thead tfoot", "", "tr");
       
  8391 		add("tr", "", "td th");
       
  8392 		add("td", "colspan rowspan headers", flowContent);
       
  8393 		add("th", "colspan rowspan headers scope abbr", flowContent);
       
  8394 		add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
       
  8395 		add("fieldset", "disabled form name", flowContent, "legend");
       
  8396 		add("label", "form for", phrasingContent);
       
  8397 		add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
       
  8398 				"formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
       
  8399 		);
       
  8400 		add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
       
  8401 			type == "html4" ? flowContent : phrasingContent);
       
  8402 		add("select", "disabled form multiple name required size", "option optgroup");
       
  8403 		add("optgroup", "disabled label", "option");
       
  8404 		add("option", "disabled label selected value");
       
  8405 		add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
       
  8406 		add("menu", "type label", flowContent, "li");
       
  8407 		add("noscript", "", flowContent);
       
  8408 
       
  8409 		// Extend with HTML5 elements
       
  8410 		if (type != "html4") {
       
  8411 			add("wbr");
       
  8412 			add("ruby", "", phrasingContent, "rt rp");
       
  8413 			add("figcaption", "", flowContent);
       
  8414 			add("mark rt rp summary bdi", "", phrasingContent);
       
  8415 			add("canvas", "width height", flowContent);
       
  8416 			add("video", "src crossorigin poster preload autoplay mediagroup loop " +
       
  8417 				"muted controls width height buffered", flowContent, "track source");
       
  8418 			add("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", flowContent, "track source");
       
  8419 			add("picture", "", "img source");
       
  8420 			add("source", "src srcset type media sizes");
       
  8421 			add("track", "kind src srclang label default");
       
  8422 			add("datalist", "", phrasingContent, "option");
       
  8423 			add("article section nav aside header footer", "", flowContent);
       
  8424 			add("hgroup", "", "h1 h2 h3 h4 h5 h6");
       
  8425 			add("figure", "", flowContent, "figcaption");
       
  8426 			add("time", "datetime", phrasingContent);
       
  8427 			add("dialog", "open", flowContent);
       
  8428 			add("command", "type label icon disabled checked radiogroup command");
       
  8429 			add("output", "for form name", phrasingContent);
       
  8430 			add("progress", "value max", phrasingContent);
       
  8431 			add("meter", "value min max low high optimum", phrasingContent);
       
  8432 			add("details", "open", flowContent, "summary");
       
  8433 			add("keygen", "autofocus challenge disabled form keytype name");
       
  8434 		}
       
  8435 
       
  8436 		// Extend with HTML4 attributes unless it's html5-strict
       
  8437 		if (type != "html5-strict") {
       
  8438 			addAttrs("script", "language xml:space");
       
  8439 			addAttrs("style", "xml:space");
       
  8440 			addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
       
  8441 			addAttrs("embed", "align name hspace vspace");
       
  8442 			addAttrs("param", "valuetype type");
       
  8443 			addAttrs("a", "charset name rev shape coords");
       
  8444 			addAttrs("br", "clear");
       
  8445 			addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
       
  8446 			addAttrs("img", "name longdesc align border hspace vspace");
       
  8447 			addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
       
  8448 			addAttrs("font basefont", "size color face");
       
  8449 			addAttrs("input", "usemap align");
       
  8450 			addAttrs("select", "onchange");
       
  8451 			addAttrs("textarea");
       
  8452 			addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
       
  8453 			addAttrs("ul", "type compact");
       
  8454 			addAttrs("li", "type");
       
  8455 			addAttrs("ol dl menu dir", "compact");
       
  8456 			addAttrs("pre", "width xml:space");
       
  8457 			addAttrs("hr", "align noshade size width");
       
  8458 			addAttrs("isindex", "prompt");
       
  8459 			addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
       
  8460 			addAttrs("col", "width align char charoff valign");
       
  8461 			addAttrs("colgroup", "width align char charoff valign");
       
  8462 			addAttrs("thead", "align char charoff valign");
       
  8463 			addAttrs("tr", "align char charoff valign bgcolor");
       
  8464 			addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
       
  8465 			addAttrs("form", "accept");
       
  8466 			addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
       
  8467 			addAttrs("tfoot", "align char charoff valign");
       
  8468 			addAttrs("tbody", "align char charoff valign");
       
  8469 			addAttrs("area", "nohref");
       
  8470 			addAttrs("body", "background bgcolor text link vlink alink");
       
  8471 		}
       
  8472 
       
  8473 		// Extend with HTML5 attributes unless it's html4
       
  8474 		if (type != "html4") {
       
  8475 			addAttrs("input button select textarea", "autofocus");
       
  8476 			addAttrs("input textarea", "placeholder");
       
  8477 			addAttrs("a", "download");
       
  8478 			addAttrs("link script img", "crossorigin");
       
  8479 			addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc
       
  8480 		}
       
  8481 
       
  8482 		// Special: iframe, ruby, video, audio, label
       
  8483 
       
  8484 		// Delete children of the same name from it's parent
       
  8485 		// For example: form can't have a child of the name form
       
  8486 		each(split('a form meter progress dfn'), function(name) {
       
  8487 			if (schema[name]) {
       
  8488 				delete schema[name].children[name];
       
  8489 			}
       
  8490 		});
       
  8491 
       
  8492 		// Delete header, footer, sectioning and heading content descendants
       
  8493 		/*each('dt th address', function(name) {
       
  8494 			delete schema[name].children[name];
       
  8495 		});*/
       
  8496 
       
  8497 		// Caption can't have tables
       
  8498 		delete schema.caption.children.table;
       
  8499 
       
  8500 		// TODO: LI:s can only have value if parent is OL
       
  8501 
       
  8502 		// TODO: Handle transparent elements
       
  8503 		// a ins del canvas map
       
  8504 
       
  8505 		mapCache[type] = schema;
       
  8506 
       
  8507 		return schema;
       
  8508 	}
       
  8509 
       
  8510 	function compileElementMap(value, mode) {
       
  8511 		var styles;
       
  8512 
       
  8513 		if (value) {
       
  8514 			styles = {};
       
  8515 
       
  8516 			if (typeof value == 'string') {
       
  8517 				value = {
       
  8518 					'*': value
       
  8519 				};
       
  8520 			}
       
  8521 
       
  8522 			// Convert styles into a rule list
       
  8523 			each(value, function(value, key) {
       
  8524 				styles[key] = styles[key.toUpperCase()] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/);
       
  8525 			});
       
  8526 		}
       
  8527 
       
  8528 		return styles;
       
  8529 	}
       
  8530 
       
  8531 	/**
       
  8532 	 * Constructs a new Schema instance.
       
  8533 	 *
       
  8534 	 * @constructor
       
  8535 	 * @method Schema
       
  8536 	 * @param {Object} settings Name/value settings object.
       
  8537 	 */
       
  8538 	return function(settings) {
       
  8539 		var self = this, elements = {}, children = {}, patternElements = [], validStyles, invalidStyles, schemaItems;
       
  8540 		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses;
       
  8541 		var blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap;
       
  8542 		var customElementsMap = {}, specialElements = {};
       
  8543 
       
  8544 		// Creates an lookup table map object for the specified option or the default value
       
  8545 		function createLookupTable(option, default_value, extendWith) {
       
  8546 			var value = settings[option];
       
  8547 
       
  8548 			if (!value) {
       
  8549 				// Get cached default map or make it if needed
       
  8550 				value = mapCache[option];
       
  8551 
       
  8552 				if (!value) {
       
  8553 					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
       
  8554 					value = extend(value, extendWith);
       
  8555 
       
  8556 					mapCache[option] = value;
       
  8557 				}
       
  8558 			} else {
       
  8559 				// Create custom map
       
  8560 				value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/));
       
  8561 			}
       
  8562 
       
  8563 			return value;
       
  8564 		}
       
  8565 
       
  8566 		settings = settings || {};
       
  8567 		schemaItems = compileSchema(settings.schema);
       
  8568 
       
  8569 		// Allow all elements and attributes if verify_html is set to false
       
  8570 		if (settings.verify_html === false) {
       
  8571 			settings.valid_elements = '*[*]';
       
  8572 		}
       
  8573 
       
  8574 		validStyles = compileElementMap(settings.valid_styles);
       
  8575 		invalidStyles = compileElementMap(settings.invalid_styles, 'map');
       
  8576 		validClasses = compileElementMap(settings.valid_classes, 'map');
       
  8577 
       
  8578 		// Setup map objects
       
  8579 		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
       
  8580 		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
       
  8581 		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
       
  8582 			'meta param embed source wbr track');
       
  8583 		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
       
  8584 			'noshade nowrap readonly selected autoplay loop controls');
       
  8585 		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
       
  8586 		moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap);
       
  8587 		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
       
  8588 						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
       
  8589 		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
       
  8590 						'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
       
  8591 						'datalist select optgroup', textBlockElementsMap);
       
  8592 		textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
       
  8593 										'dfn code mark q sup sub samp');
       
  8594 
       
  8595 		each((settings.special || 'script noscript style textarea').split(' '), function(name) {
       
  8596 			specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
       
  8597 		});
       
  8598 
       
  8599 		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
       
  8600 		function patternToRegExp(str) {
       
  8601 			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
       
  8602 		}
       
  8603 
       
  8604 		// Parses the specified valid_elements string and adds to the current rules
       
  8605 		// This function is a bit hard to read since it's heavily optimized for speed
       
  8606 		function addValidElements(validElements) {
       
  8607 			var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
       
  8608 				prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
       
  8609 				elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
       
  8610 				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
       
  8611 				hasPatternsRegExp = /[*?+]/;
       
  8612 
       
  8613 			if (validElements) {
       
  8614 				// Split valid elements into an array with rules
       
  8615 				validElements = split(validElements, ',');
       
  8616 
       
  8617 				if (elements['@']) {
       
  8618 					globalAttributes = elements['@'].attributes;
       
  8619 					globalAttributesOrder = elements['@'].attributesOrder;
       
  8620 				}
       
  8621 
       
  8622 				// Loop all rules
       
  8623 				for (ei = 0, el = validElements.length; ei < el; ei++) {
       
  8624 					// Parse element rule
       
  8625 					matches = elementRuleRegExp.exec(validElements[ei]);
       
  8626 					if (matches) {
       
  8627 						// Setup local names for matches
       
  8628 						prefix = matches[1];
       
  8629 						elementName = matches[2];
       
  8630 						outputName = matches[3];
       
  8631 						attrData = matches[5];
       
  8632 
       
  8633 						// Create new attributes and attributesOrder
       
  8634 						attributes = {};
       
  8635 						attributesOrder = [];
       
  8636 
       
  8637 						// Create the new element
       
  8638 						element = {
       
  8639 							attributes: attributes,
       
  8640 							attributesOrder: attributesOrder
       
  8641 						};
       
  8642 
       
  8643 						// Padd empty elements prefix
       
  8644 						if (prefix === '#') {
       
  8645 							element.paddEmpty = true;
       
  8646 						}
       
  8647 
       
  8648 						// Remove empty elements prefix
       
  8649 						if (prefix === '-') {
       
  8650 							element.removeEmpty = true;
       
  8651 						}
       
  8652 
       
  8653 						if (matches[4] === '!') {
       
  8654 							element.removeEmptyAttrs = true;
       
  8655 						}
       
  8656 
       
  8657 						// Copy attributes from global rule into current rule
       
  8658 						if (globalAttributes) {
       
  8659 							for (key in globalAttributes) {
       
  8660 								attributes[key] = globalAttributes[key];
       
  8661 							}
       
  8662 
       
  8663 							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
       
  8664 						}
       
  8665 
       
  8666 						// Attributes defined
       
  8667 						if (attrData) {
       
  8668 							attrData = split(attrData, '|');
       
  8669 							for (ai = 0, al = attrData.length; ai < al; ai++) {
       
  8670 								matches = attrRuleRegExp.exec(attrData[ai]);
       
  8671 								if (matches) {
       
  8672 									attr = {};
       
  8673 									attrType = matches[1];
       
  8674 									attrName = matches[2].replace(/::/g, ':');
       
  8675 									prefix = matches[3];
       
  8676 									value = matches[4];
       
  8677 
       
  8678 									// Required
       
  8679 									if (attrType === '!') {
       
  8680 										element.attributesRequired = element.attributesRequired || [];
       
  8681 										element.attributesRequired.push(attrName);
       
  8682 										attr.required = true;
       
  8683 									}
       
  8684 
       
  8685 									// Denied from global
       
  8686 									if (attrType === '-') {
       
  8687 										delete attributes[attrName];
       
  8688 										attributesOrder.splice(inArray(attributesOrder, attrName), 1);
       
  8689 										continue;
       
  8690 									}
       
  8691 
       
  8692 									// Default value
       
  8693 									if (prefix) {
       
  8694 										// Default value
       
  8695 										if (prefix === '=') {
       
  8696 											element.attributesDefault = element.attributesDefault || [];
       
  8697 											element.attributesDefault.push({name: attrName, value: value});
       
  8698 											attr.defaultValue = value;
       
  8699 										}
       
  8700 
       
  8701 										// Forced value
       
  8702 										if (prefix === ':') {
       
  8703 											element.attributesForced = element.attributesForced || [];
       
  8704 											element.attributesForced.push({name: attrName, value: value});
       
  8705 											attr.forcedValue = value;
       
  8706 										}
       
  8707 
       
  8708 										// Required values
       
  8709 										if (prefix === '<') {
       
  8710 											attr.validValues = makeMap(value, '?');
       
  8711 										}
       
  8712 									}
       
  8713 
       
  8714 									// Check for attribute patterns
       
  8715 									if (hasPatternsRegExp.test(attrName)) {
       
  8716 										element.attributePatterns = element.attributePatterns || [];
       
  8717 										attr.pattern = patternToRegExp(attrName);
       
  8718 										element.attributePatterns.push(attr);
       
  8719 									} else {
       
  8720 										// Add attribute to order list if it doesn't already exist
       
  8721 										if (!attributes[attrName]) {
       
  8722 											attributesOrder.push(attrName);
       
  8723 										}
       
  8724 
       
  8725 										attributes[attrName] = attr;
       
  8726 									}
       
  8727 								}
       
  8728 							}
       
  8729 						}
       
  8730 
       
  8731 						// Global rule, store away these for later usage
       
  8732 						if (!globalAttributes && elementName == '@') {
       
  8733 							globalAttributes = attributes;
       
  8734 							globalAttributesOrder = attributesOrder;
       
  8735 						}
       
  8736 
       
  8737 						// Handle substitute elements such as b/strong
       
  8738 						if (outputName) {
       
  8739 							element.outputName = elementName;
       
  8740 							elements[outputName] = element;
       
  8741 						}
       
  8742 
       
  8743 						// Add pattern or exact element
       
  8744 						if (hasPatternsRegExp.test(elementName)) {
       
  8745 							element.pattern = patternToRegExp(elementName);
       
  8746 							patternElements.push(element);
       
  8747 						} else {
       
  8748 							elements[elementName] = element;
       
  8749 						}
       
  8750 					}
       
  8751 				}
       
  8752 			}
       
  8753 		}
       
  8754 
       
  8755 		function setValidElements(validElements) {
       
  8756 			elements = {};
       
  8757 			patternElements = [];
       
  8758 
       
  8759 			addValidElements(validElements);
       
  8760 
       
  8761 			each(schemaItems, function(element, name) {
       
  8762 				children[name] = element.children;
       
  8763 			});
       
  8764 		}
       
  8765 
       
  8766 		// Adds custom non HTML elements to the schema
       
  8767 		function addCustomElements(customElements) {
       
  8768 			var customElementRegExp = /^(~)?(.+)$/;
       
  8769 
       
  8770 			if (customElements) {
       
  8771 				// Flush cached items since we are altering the default maps
       
  8772 				mapCache.text_block_elements = mapCache.block_elements = null;
       
  8773 
       
  8774 				each(split(customElements, ','), function(rule) {
       
  8775 					var matches = customElementRegExp.exec(rule),
       
  8776 						inline = matches[1] === '~',
       
  8777 						cloneName = inline ? 'span' : 'div',
       
  8778 						name = matches[2];
       
  8779 
       
  8780 					children[name] = children[cloneName];
       
  8781 					customElementsMap[name] = cloneName;
       
  8782 
       
  8783 					// If it's not marked as inline then add it to valid block elements
       
  8784 					if (!inline) {
       
  8785 						blockElementsMap[name.toUpperCase()] = {};
       
  8786 						blockElementsMap[name] = {};
       
  8787 					}
       
  8788 
       
  8789 					// Add elements clone if needed
       
  8790 					if (!elements[name]) {
       
  8791 						var customRule = elements[cloneName];
       
  8792 
       
  8793 						customRule = extend({}, customRule);
       
  8794 						delete customRule.removeEmptyAttrs;
       
  8795 						delete customRule.removeEmpty;
       
  8796 
       
  8797 						elements[name] = customRule;
       
  8798 					}
       
  8799 
       
  8800 					// Add custom elements at span/div positions
       
  8801 					each(children, function(element, elmName) {
       
  8802 						if (element[cloneName]) {
       
  8803 							children[elmName] = element = extend({}, children[elmName]);
       
  8804 							element[name] = element[cloneName];
       
  8805 						}
       
  8806 					});
       
  8807 				});
       
  8808 			}
       
  8809 		}
       
  8810 
       
  8811 		// Adds valid children to the schema object
       
  8812 		function addValidChildren(validChildren) {
       
  8813 			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
       
  8814 
       
  8815 			if (validChildren) {
       
  8816 				each(split(validChildren, ','), function(rule) {
       
  8817 					var matches = childRuleRegExp.exec(rule), parent, prefix;
       
  8818 
       
  8819 					if (matches) {
       
  8820 						prefix = matches[1];
       
  8821 
       
  8822 						// Add/remove items from default
       
  8823 						if (prefix) {
       
  8824 							parent = children[matches[2]];
       
  8825 						} else {
       
  8826 							parent = children[matches[2]] = {'#comment': {}};
       
  8827 						}
       
  8828 
       
  8829 						parent = children[matches[2]];
       
  8830 
       
  8831 						each(split(matches[3], '|'), function(child) {
       
  8832 							if (prefix === '-') {
       
  8833 								// Clone the element before we delete
       
  8834 								// things in it to not mess up default schemas
       
  8835 								children[matches[2]] = parent = extend({}, children[matches[2]]);
       
  8836 
       
  8837 								delete parent[child];
       
  8838 							} else {
       
  8839 								parent[child] = {};
       
  8840 							}
       
  8841 						});
       
  8842 					}
       
  8843 				});
       
  8844 			}
       
  8845 		}
       
  8846 
       
  8847 		function getElementRule(name) {
       
  8848 			var element = elements[name], i;
       
  8849 
       
  8850 			// Exact match found
       
  8851 			if (element) {
       
  8852 				return element;
       
  8853 			}
       
  8854 
       
  8855 			// No exact match then try the patterns
       
  8856 			i = patternElements.length;
       
  8857 			while (i--) {
       
  8858 				element = patternElements[i];
       
  8859 
       
  8860 				if (element.pattern.test(name)) {
       
  8861 					return element;
       
  8862 				}
       
  8863 			}
       
  8864 		}
       
  8865 
       
  8866 		if (!settings.valid_elements) {
       
  8867 			// No valid elements defined then clone the elements from the schema spec
       
  8868 			each(schemaItems, function(element, name) {
       
  8869 				elements[name] = {
       
  8870 					attributes: element.attributes,
       
  8871 					attributesOrder: element.attributesOrder
       
  8872 				};
       
  8873 
       
  8874 				children[name] = element.children;
       
  8875 			});
       
  8876 
       
  8877 			// Switch these on HTML4
       
  8878 			if (settings.schema != "html5") {
       
  8879 				each(split('strong/b em/i'), function(item) {
       
  8880 					item = split(item, '/');
       
  8881 					elements[item[1]].outputName = item[0];
       
  8882 				});
       
  8883 			}
       
  8884 
       
  8885 			// Add default alt attribute for images
       
  8886 			elements.img.attributesDefault = [{name: 'alt', value: ''}];
       
  8887 
       
  8888 			// Remove these if they are empty by default
       
  8889 			each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
       
  8890 				if (elements[name]) {
       
  8891 					elements[name].removeEmpty = true;
       
  8892 				}
       
  8893 			});
       
  8894 
       
  8895 			// Padd these by default
       
  8896 			each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
       
  8897 				elements[name].paddEmpty = true;
       
  8898 			});
       
  8899 
       
  8900 			// Remove these if they have no attributes
       
  8901 			each(split('span'), function(name) {
       
  8902 				elements[name].removeEmptyAttrs = true;
       
  8903 			});
       
  8904 
       
  8905 			// Remove these by default
       
  8906 			// TODO: Reenable in 4.1
       
  8907 			/*each(split('script style'), function(name) {
       
  8908 				delete elements[name];
       
  8909 			});*/
       
  8910 		} else {
       
  8911 			setValidElements(settings.valid_elements);
       
  8912 		}
       
  8913 
       
  8914 		addCustomElements(settings.custom_elements);
       
  8915 		addValidChildren(settings.valid_children);
       
  8916 		addValidElements(settings.extended_valid_elements);
       
  8917 
       
  8918 		// Todo: Remove this when we fix list handling to be valid
       
  8919 		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
       
  8920 
       
  8921 		// Delete invalid elements
       
  8922 		if (settings.invalid_elements) {
       
  8923 			each(explode(settings.invalid_elements), function(item) {
       
  8924 				if (elements[item]) {
       
  8925 					delete elements[item];
       
  8926 				}
       
  8927 			});
       
  8928 		}
       
  8929 
       
  8930 		// If the user didn't allow span only allow internal spans
       
  8931 		if (!getElementRule('span')) {
       
  8932 			addValidElements('span[!data-mce-type|*]');
       
  8933 		}
       
  8934 
       
  8935 		/**
       
  8936 		 * Name/value map object with valid parents and children to those parents.
       
  8937 		 *
       
  8938 		 * @example
       
  8939 		 * children = {
       
  8940 		 *    div:{p:{}, h1:{}}
       
  8941 		 * };
       
  8942 		 * @field children
       
  8943 		 * @type Object
       
  8944 		 */
       
  8945 		self.children = children;
       
  8946 
       
  8947 		/**
       
  8948 		 * Name/value map object with valid styles for each element.
       
  8949 		 *
       
  8950 		 * @method getValidStyles
       
  8951 		 * @type Object
       
  8952 		 */
       
  8953 		self.getValidStyles = function() {
       
  8954 			return validStyles;
       
  8955 		};
       
  8956 
       
  8957 		/**
       
  8958 		 * Name/value map object with valid styles for each element.
       
  8959 		 *
       
  8960 		 * @method getInvalidStyles
       
  8961 		 * @type Object
       
  8962 		 */
       
  8963 		self.getInvalidStyles = function() {
       
  8964 			return invalidStyles;
       
  8965 		};
       
  8966 
       
  8967 		/**
       
  8968 		 * Name/value map object with valid classes for each element.
       
  8969 		 *
       
  8970 		 * @method getValidClasses
       
  8971 		 * @type Object
       
  8972 		 */
       
  8973 		self.getValidClasses = function() {
       
  8974 			return validClasses;
       
  8975 		};
       
  8976 
       
  8977 		/**
       
  8978 		 * Returns a map with boolean attributes.
       
  8979 		 *
       
  8980 		 * @method getBoolAttrs
       
  8981 		 * @return {Object} Name/value lookup map for boolean attributes.
       
  8982 		 */
       
  8983 		self.getBoolAttrs = function() {
       
  8984 			return boolAttrMap;
       
  8985 		};
       
  8986 
       
  8987 		/**
       
  8988 		 * Returns a map with block elements.
       
  8989 		 *
       
  8990 		 * @method getBlockElements
       
  8991 		 * @return {Object} Name/value lookup map for block elements.
       
  8992 		 */
       
  8993 		self.getBlockElements = function() {
       
  8994 			return blockElementsMap;
       
  8995 		};
       
  8996 
       
  8997 		/**
       
  8998 		 * Returns a map with text block elements. Such as: p,h1-h6,div,address
       
  8999 		 *
       
  9000 		 * @method getTextBlockElements
       
  9001 		 * @return {Object} Name/value lookup map for block elements.
       
  9002 		 */
       
  9003 		self.getTextBlockElements = function() {
       
  9004 			return textBlockElementsMap;
       
  9005 		};
       
  9006 
       
  9007 		/**
       
  9008 		 * Returns a map of inline text format nodes for example strong/span or ins.
       
  9009 		 *
       
  9010 		 * @method getTextInlineElements
       
  9011 		 * @return {Object} Name/value lookup map for text format elements.
       
  9012 		 */
       
  9013 		self.getTextInlineElements = function() {
       
  9014 			return textInlineElementsMap;
       
  9015 		};
       
  9016 
       
  9017 		/**
       
  9018 		 * Returns a map with short ended elements such as BR or IMG.
       
  9019 		 *
       
  9020 		 * @method getShortEndedElements
       
  9021 		 * @return {Object} Name/value lookup map for short ended elements.
       
  9022 		 */
       
  9023 		self.getShortEndedElements = function() {
       
  9024 			return shortEndedElementsMap;
       
  9025 		};
       
  9026 
       
  9027 		/**
       
  9028 		 * Returns a map with self closing tags such as <li>.
       
  9029 		 *
       
  9030 		 * @method getSelfClosingElements
       
  9031 		 * @return {Object} Name/value lookup map for self closing tags elements.
       
  9032 		 */
       
  9033 		self.getSelfClosingElements = function() {
       
  9034 			return selfClosingElementsMap;
       
  9035 		};
       
  9036 
       
  9037 		/**
       
  9038 		 * Returns a map with elements that should be treated as contents regardless if it has text
       
  9039 		 * content in them or not such as TD, VIDEO or IMG.
       
  9040 		 *
       
  9041 		 * @method getNonEmptyElements
       
  9042 		 * @return {Object} Name/value lookup map for non empty elements.
       
  9043 		 */
       
  9044 		self.getNonEmptyElements = function() {
       
  9045 			return nonEmptyElementsMap;
       
  9046 		};
       
  9047 
       
  9048 		/**
       
  9049 		 * Returns a map with elements that the caret should be moved in front of after enter is
       
  9050 		 * pressed
       
  9051 		 *
       
  9052 		 * @method getMoveCaretBeforeOnEnterElements
       
  9053 		 * @return {Object} Name/value lookup map for elements to place the caret in front of.
       
  9054 		 */
       
  9055 		self.getMoveCaretBeforeOnEnterElements = function() {
       
  9056 			return moveCaretBeforeOnEnterElementsMap;
       
  9057 		};
       
  9058 
       
  9059 		/**
       
  9060 		 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
       
  9061 		 *
       
  9062 		 * @method getWhiteSpaceElements
       
  9063 		 * @return {Object} Name/value lookup map for white space elements.
       
  9064 		 */
       
  9065 		self.getWhiteSpaceElements = function() {
       
  9066 			return whiteSpaceElementsMap;
       
  9067 		};
       
  9068 
       
  9069 		/**
       
  9070 		 * Returns a map with special elements. These are elements that needs to be parsed
       
  9071 		 * in a special way such as script, style, textarea etc. The map object values
       
  9072 		 * are regexps used to find the end of the element.
       
  9073 		 *
       
  9074 		 * @method getSpecialElements
       
  9075 		 * @return {Object} Name/value lookup map for special elements.
       
  9076 		 */
       
  9077 		self.getSpecialElements = function() {
       
  9078 			return specialElements;
       
  9079 		};
       
  9080 
       
  9081 		/**
       
  9082 		 * Returns true/false if the specified element and it's child is valid or not
       
  9083 		 * according to the schema.
       
  9084 		 *
       
  9085 		 * @method isValidChild
       
  9086 		 * @param {String} name Element name to check for.
       
  9087 		 * @param {String} child Element child to verify.
       
  9088 		 * @return {Boolean} True/false if the element is a valid child of the specified parent.
       
  9089 		 */
       
  9090 		self.isValidChild = function(name, child) {
       
  9091 			var parent = children[name];
       
  9092 
       
  9093 			return !!(parent && parent[child]);
       
  9094 		};
       
  9095 
       
  9096 		/**
       
  9097 		 * Returns true/false if the specified element name and optional attribute is
       
  9098 		 * valid according to the schema.
       
  9099 		 *
       
  9100 		 * @method isValid
       
  9101 		 * @param {String} name Name of element to check.
       
  9102 		 * @param {String} attr Optional attribute name to check for.
       
  9103 		 * @return {Boolean} True/false if the element and attribute is valid.
       
  9104 		 */
       
  9105 		self.isValid = function(name, attr) {
       
  9106 			var attrPatterns, i, rule = getElementRule(name);
       
  9107 
       
  9108 			// Check if it's a valid element
       
  9109 			if (rule) {
       
  9110 				if (attr) {
       
  9111 					// Check if attribute name exists
       
  9112 					if (rule.attributes[attr]) {
       
  9113 						return true;
       
  9114 					}
       
  9115 
       
  9116 					// Check if attribute matches a regexp pattern
       
  9117 					attrPatterns = rule.attributePatterns;
       
  9118 					if (attrPatterns) {
       
  9119 						i = attrPatterns.length;
       
  9120 						while (i--) {
       
  9121 							if (attrPatterns[i].pattern.test(name)) {
       
  9122 								return true;
       
  9123 							}
       
  9124 						}
       
  9125 					}
       
  9126 				} else {
       
  9127 					return true;
       
  9128 				}
       
  9129 			}
       
  9130 
       
  9131 			// No match
       
  9132 			return false;
       
  9133 		};
       
  9134 
       
  9135 		/**
       
  9136 		 * Returns true/false if the specified element is valid or not
       
  9137 		 * according to the schema.
       
  9138 		 *
       
  9139 		 * @method getElementRule
       
  9140 		 * @param {String} name Element name to check for.
       
  9141 		 * @return {Object} Element object or undefined if the element isn't valid.
       
  9142 		 */
       
  9143 		self.getElementRule = getElementRule;
       
  9144 
       
  9145 		/**
       
  9146 		 * Returns an map object of all custom elements.
       
  9147 		 *
       
  9148 		 * @method getCustomElements
       
  9149 		 * @return {Object} Name/value map object of all custom elements.
       
  9150 		 */
       
  9151 		self.getCustomElements = function() {
       
  9152 			return customElementsMap;
       
  9153 		};
       
  9154 
       
  9155 		/**
       
  9156 		 * Parses a valid elements string and adds it to the schema. The valid elements
       
  9157 		 * format is for example "element[attr=default|otherattr]".
       
  9158 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
       
  9159 		 *
       
  9160 		 * @method addValidElements
       
  9161 		 * @param {String} valid_elements String in the valid elements format to be parsed.
       
  9162 		 */
       
  9163 		self.addValidElements = addValidElements;
       
  9164 
       
  9165 		/**
       
  9166 		 * Parses a valid elements string and sets it to the schema. The valid elements
       
  9167 		 * format is for example "element[attr=default|otherattr]".
       
  9168 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
       
  9169 		 *
       
  9170 		 * @method setValidElements
       
  9171 		 * @param {String} valid_elements String in the valid elements format to be parsed.
       
  9172 		 */
       
  9173 		self.setValidElements = setValidElements;
       
  9174 
       
  9175 		/**
       
  9176 		 * Adds custom non HTML elements to the schema.
       
  9177 		 *
       
  9178 		 * @method addCustomElements
       
  9179 		 * @param {String} custom_elements Comma separated list of custom elements to add.
       
  9180 		 */
       
  9181 		self.addCustomElements = addCustomElements;
       
  9182 
       
  9183 		/**
       
  9184 		 * Parses a valid children string and adds them to the schema structure. The valid children
       
  9185 		 * format is for example: "element[child1|child2]".
       
  9186 		 *
       
  9187 		 * @method addValidChildren
       
  9188 		 * @param {String} valid_children Valid children elements string to parse
       
  9189 		 */
       
  9190 		self.addValidChildren = addValidChildren;
       
  9191 
       
  9192 		self.elements = elements;
       
  9193 	};
       
  9194 });
       
  9195 
       
  9196 // Included from: js/tinymce/classes/html/SaxParser.js
       
  9197 
       
  9198 /**
       
  9199  * SaxParser.js
       
  9200  *
       
  9201  * Copyright, Moxiecode Systems AB
       
  9202  * Released under LGPL License.
       
  9203  *
       
  9204  * License: http://www.tinymce.com/license
       
  9205  * Contributing: http://www.tinymce.com/contributing
       
  9206  */
       
  9207 
       
  9208 /*eslint max-depth:[2, 9] */
       
  9209 
       
  9210 /**
       
  9211  * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
       
  9212  * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
       
  9213  * and attributes that doesn't fit the schema if the validate setting is enabled.
       
  9214  *
       
  9215  * @example
       
  9216  * var parser = new tinymce.html.SaxParser({
       
  9217  *     validate: true,
       
  9218  *
       
  9219  *     comment: function(text) {
       
  9220  *         console.log('Comment:', text);
       
  9221  *     },
       
  9222  *
       
  9223  *     cdata: function(text) {
       
  9224  *         console.log('CDATA:', text);
       
  9225  *     },
       
  9226  *
       
  9227  *     text: function(text, raw) {
       
  9228  *         console.log('Text:', text, 'Raw:', raw);
       
  9229  *     },
       
  9230  *
       
  9231  *     start: function(name, attrs, empty) {
       
  9232  *         console.log('Start:', name, attrs, empty);
       
  9233  *     },
       
  9234  *
       
  9235  *     end: function(name) {
       
  9236  *         console.log('End:', name);
       
  9237  *     },
       
  9238  *
       
  9239  *     pi: function(name, text) {
       
  9240  *         console.log('PI:', name, text);
       
  9241  *     },
       
  9242  *
       
  9243  *     doctype: function(text) {
       
  9244  *         console.log('DocType:', text);
       
  9245  *     }
       
  9246  * }, schema);
       
  9247  * @class tinymce.html.SaxParser
       
  9248  * @version 3.4
       
  9249  */
       
  9250 define("tinymce/html/SaxParser", [
       
  9251 	"tinymce/html/Schema",
       
  9252 	"tinymce/html/Entities",
       
  9253 	"tinymce/util/Tools"
       
  9254 ], function(Schema, Entities, Tools) {
       
  9255 	var each = Tools.each;
       
  9256 
       
  9257 	/**
       
  9258 	 * Returns the index of the end tag for a specific start tag. This can be
       
  9259 	 * used to skip all children of a parent element from being processed.
       
  9260 	 *
       
  9261 	 * @private
       
  9262 	 * @method findEndTag
       
  9263 	 * @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements.
       
  9264 	 * @param {String} html HTML string to find the end tag in.
       
  9265 	 * @param {Number} startIndex Indext to start searching at should be after the start tag.
       
  9266 	 * @return {Number} Index of the end tag.
       
  9267 	 */
       
  9268 	function findEndTag(schema, html, startIndex) {
       
  9269 		var count = 1, index, matches, tokenRegExp, shortEndedElements;
       
  9270 
       
  9271 		shortEndedElements = schema.getShortEndedElements();
       
  9272 		tokenRegExp = /<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
       
  9273 		tokenRegExp.lastIndex = index = startIndex;
       
  9274 
       
  9275 		while ((matches = tokenRegExp.exec(html))) {
       
  9276 			index = tokenRegExp.lastIndex;
       
  9277 
       
  9278 			if (matches[1] === '/') { // End element
       
  9279 				count--;
       
  9280 			} else if (!matches[1]) { // Start element
       
  9281 				if (matches[2] in shortEndedElements) {
       
  9282 					continue;
       
  9283 				}
       
  9284 
       
  9285 				count++;
       
  9286 			}
       
  9287 
       
  9288 			if (count === 0) {
       
  9289 				break;
       
  9290 			}
       
  9291 		}
       
  9292 
       
  9293 		return index;
       
  9294 	}
       
  9295 
       
  9296 	/**
       
  9297 	 * Constructs a new SaxParser instance.
       
  9298 	 *
       
  9299 	 * @constructor
       
  9300 	 * @method SaxParser
       
  9301 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
       
  9302 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
       
  9303 	 */
       
  9304 	function SaxParser(settings, schema) {
       
  9305 		var self = this;
       
  9306 
       
  9307 		function noop() {}
       
  9308 
       
  9309 		settings = settings || {};
       
  9310 		self.schema = schema = schema || new Schema();
       
  9311 
       
  9312 		if (settings.fix_self_closing !== false) {
       
  9313 			settings.fix_self_closing = true;
       
  9314 		}
       
  9315 
       
  9316 		// Add handler functions from settings and setup default handlers
       
  9317 		each('comment cdata text start end pi doctype'.split(' '), function(name) {
       
  9318 			if (name) {
       
  9319 				self[name] = settings[name] || noop;
       
  9320 			}
       
  9321 		});
       
  9322 
       
  9323 		/**
       
  9324 		 * Parses the specified HTML string and executes the callbacks for each item it finds.
       
  9325 		 *
       
  9326 		 * @example
       
  9327 		 * new SaxParser({...}).parse('<b>text</b>');
       
  9328 		 * @method parse
       
  9329 		 * @param {String} html Html string to sax parse.
       
  9330 		 */
       
  9331 		self.parse = function(html) {
       
  9332 			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
       
  9333 			var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
       
  9334 			var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
       
  9335 			var attributesRequired, attributesDefault, attributesForced;
       
  9336 			var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
       
  9337 			var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster');
       
  9338 			var scriptUriRegExp = /((java|vb)script|mhtml):/i, dataUriRegExp = /^data:/i;
       
  9339 
       
  9340 			function processEndTag(name) {
       
  9341 				var pos, i;
       
  9342 
       
  9343 				// Find position of parent of the same type
       
  9344 				pos = stack.length;
       
  9345 				while (pos--) {
       
  9346 					if (stack[pos].name === name) {
       
  9347 						break;
       
  9348 					}
       
  9349 				}
       
  9350 
       
  9351 				// Found parent
       
  9352 				if (pos >= 0) {
       
  9353 					// Close all the open elements
       
  9354 					for (i = stack.length - 1; i >= pos; i--) {
       
  9355 						name = stack[i];
       
  9356 
       
  9357 						if (name.valid) {
       
  9358 							self.end(name.name);
       
  9359 						}
       
  9360 					}
       
  9361 
       
  9362 					// Remove the open elements from the stack
       
  9363 					stack.length = pos;
       
  9364 				}
       
  9365 			}
       
  9366 
       
  9367 			function parseAttribute(match, name, value, val2, val3) {
       
  9368 				var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
       
  9369 
       
  9370 				name = name.toLowerCase();
       
  9371 				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
       
  9372 
       
  9373 				// Validate name and value pass through all data- attributes
       
  9374 				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
       
  9375 					attrRule = validAttributesMap[name];
       
  9376 
       
  9377 					// Find rule by pattern matching
       
  9378 					if (!attrRule && validAttributePatterns) {
       
  9379 						i = validAttributePatterns.length;
       
  9380 						while (i--) {
       
  9381 							attrRule = validAttributePatterns[i];
       
  9382 							if (attrRule.pattern.test(name)) {
       
  9383 								break;
       
  9384 							}
       
  9385 						}
       
  9386 
       
  9387 						// No rule matched
       
  9388 						if (i === -1) {
       
  9389 							attrRule = null;
       
  9390 						}
       
  9391 					}
       
  9392 
       
  9393 					// No attribute rule found
       
  9394 					if (!attrRule) {
       
  9395 						return;
       
  9396 					}
       
  9397 
       
  9398 					// Validate value
       
  9399 					if (attrRule.validValues && !(value in attrRule.validValues)) {
       
  9400 						return;
       
  9401 					}
       
  9402 				}
       
  9403 
       
  9404 				// Block any javascript: urls or non image data uris
       
  9405 				if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
       
  9406 					var uri = value.replace(trimRegExp, '');
       
  9407 
       
  9408 					try {
       
  9409 						// Might throw malformed URI sequence
       
  9410 						uri = decodeURIComponent(uri);
       
  9411 					} catch (ex) {
       
  9412 						// Fallback to non UTF-8 decoder
       
  9413 						uri = unescape(uri);
       
  9414 					}
       
  9415 
       
  9416 					if (scriptUriRegExp.test(uri)) {
       
  9417 						return;
       
  9418 					}
       
  9419 
       
  9420 					if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) {
       
  9421 						return;
       
  9422 					}
       
  9423 				}
       
  9424 
       
  9425 				// Add attribute to list and map
       
  9426 				attrList.map[name] = value;
       
  9427 				attrList.push({
       
  9428 					name: name,
       
  9429 					value: value
       
  9430 				});
       
  9431 			}
       
  9432 
       
  9433 			// Precompile RegExps and map objects
       
  9434 			tokenRegExp = new RegExp('<(?:' +
       
  9435 				'(?:!--([\\w\\W]*?)-->)|' + // Comment
       
  9436 				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
       
  9437 				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
       
  9438 				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
       
  9439 				'(?:\\/([^>]+)>)|' + // End element
       
  9440 				'(?:([A-Za-z0-9\\-_\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
       
  9441 			')', 'g');
       
  9442 
       
  9443 			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
       
  9444 
       
  9445 			// Setup lookup tables for empty elements and boolean attributes
       
  9446 			shortEndedElements = schema.getShortEndedElements();
       
  9447 			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
       
  9448 			fillAttrsMap = schema.getBoolAttrs();
       
  9449 			validate = settings.validate;
       
  9450 			removeInternalElements = settings.remove_internals;
       
  9451 			fixSelfClosing = settings.fix_self_closing;
       
  9452 			specialElements = schema.getSpecialElements();
       
  9453 
       
  9454 			while ((matches = tokenRegExp.exec(html))) {
       
  9455 				// Text
       
  9456 				if (index < matches.index) {
       
  9457 					self.text(decode(html.substr(index, matches.index - index)));
       
  9458 				}
       
  9459 
       
  9460 				if ((value = matches[6])) { // End element
       
  9461 					value = value.toLowerCase();
       
  9462 
       
  9463 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
       
  9464 					if (value.charAt(0) === ':') {
       
  9465 						value = value.substr(1);
       
  9466 					}
       
  9467 
       
  9468 					processEndTag(value);
       
  9469 				} else if ((value = matches[7])) { // Start element
       
  9470 					value = value.toLowerCase();
       
  9471 
       
  9472 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
       
  9473 					if (value.charAt(0) === ':') {
       
  9474 						value = value.substr(1);
       
  9475 					}
       
  9476 
       
  9477 					isShortEnded = value in shortEndedElements;
       
  9478 
       
  9479 					// Is self closing tag for example an <li> after an open <li>
       
  9480 					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
       
  9481 						processEndTag(value);
       
  9482 					}
       
  9483 
       
  9484 					// Validate element
       
  9485 					if (!validate || (elementRule = schema.getElementRule(value))) {
       
  9486 						isValidElement = true;
       
  9487 
       
  9488 						// Grab attributes map and patters when validation is enabled
       
  9489 						if (validate) {
       
  9490 							validAttributesMap = elementRule.attributes;
       
  9491 							validAttributePatterns = elementRule.attributePatterns;
       
  9492 						}
       
  9493 
       
  9494 						// Parse attributes
       
  9495 						if ((attribsValue = matches[8])) {
       
  9496 							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
       
  9497 
       
  9498 							// If the element has internal attributes then remove it if we are told to do so
       
  9499 							if (isInternalElement && removeInternalElements) {
       
  9500 								isValidElement = false;
       
  9501 							}
       
  9502 
       
  9503 							attrList = [];
       
  9504 							attrList.map = {};
       
  9505 
       
  9506 							attribsValue.replace(attrRegExp, parseAttribute);
       
  9507 						} else {
       
  9508 							attrList = [];
       
  9509 							attrList.map = {};
       
  9510 						}
       
  9511 
       
  9512 						// Process attributes if validation is enabled
       
  9513 						if (validate && !isInternalElement) {
       
  9514 							attributesRequired = elementRule.attributesRequired;
       
  9515 							attributesDefault = elementRule.attributesDefault;
       
  9516 							attributesForced = elementRule.attributesForced;
       
  9517 							anyAttributesRequired = elementRule.removeEmptyAttrs;
       
  9518 
       
  9519 							// Check if any attribute exists
       
  9520 							if (anyAttributesRequired && !attrList.length) {
       
  9521 								isValidElement = false;
       
  9522 							}
       
  9523 
       
  9524 							// Handle forced attributes
       
  9525 							if (attributesForced) {
       
  9526 								i = attributesForced.length;
       
  9527 								while (i--) {
       
  9528 									attr = attributesForced[i];
       
  9529 									name = attr.name;
       
  9530 									attrValue = attr.value;
       
  9531 
       
  9532 									if (attrValue === '{$uid}') {
       
  9533 										attrValue = 'mce_' + idCount++;
       
  9534 									}
       
  9535 
       
  9536 									attrList.map[name] = attrValue;
       
  9537 									attrList.push({name: name, value: attrValue});
       
  9538 								}
       
  9539 							}
       
  9540 
       
  9541 							// Handle default attributes
       
  9542 							if (attributesDefault) {
       
  9543 								i = attributesDefault.length;
       
  9544 								while (i--) {
       
  9545 									attr = attributesDefault[i];
       
  9546 									name = attr.name;
       
  9547 
       
  9548 									if (!(name in attrList.map)) {
       
  9549 										attrValue = attr.value;
       
  9550 
       
  9551 										if (attrValue === '{$uid}') {
       
  9552 											attrValue = 'mce_' + idCount++;
       
  9553 										}
       
  9554 
       
  9555 										attrList.map[name] = attrValue;
       
  9556 										attrList.push({name: name, value: attrValue});
       
  9557 									}
       
  9558 								}
       
  9559 							}
       
  9560 
       
  9561 							// Handle required attributes
       
  9562 							if (attributesRequired) {
       
  9563 								i = attributesRequired.length;
       
  9564 								while (i--) {
       
  9565 									if (attributesRequired[i] in attrList.map) {
       
  9566 										break;
       
  9567 									}
       
  9568 								}
       
  9569 
       
  9570 								// None of the required attributes where found
       
  9571 								if (i === -1) {
       
  9572 									isValidElement = false;
       
  9573 								}
       
  9574 							}
       
  9575 
       
  9576 							// Invalidate element if it's marked as bogus
       
  9577 							if ((attr = attrList.map['data-mce-bogus'])) {
       
  9578 								if (attr === 'all') {
       
  9579 									index = findEndTag(schema, html, tokenRegExp.lastIndex);
       
  9580 									tokenRegExp.lastIndex = index;
       
  9581 									continue;
       
  9582 								}
       
  9583 
       
  9584 								isValidElement = false;
       
  9585 							}
       
  9586 						}
       
  9587 
       
  9588 						if (isValidElement) {
       
  9589 							self.start(value, attrList, isShortEnded);
       
  9590 						}
       
  9591 					} else {
       
  9592 						isValidElement = false;
       
  9593 					}
       
  9594 
       
  9595 					// Treat script, noscript and style a bit different since they may include code that looks like elements
       
  9596 					if ((endRegExp = specialElements[value])) {
       
  9597 						endRegExp.lastIndex = index = matches.index + matches[0].length;
       
  9598 
       
  9599 						if ((matches = endRegExp.exec(html))) {
       
  9600 							if (isValidElement) {
       
  9601 								text = html.substr(index, matches.index - index);
       
  9602 							}
       
  9603 
       
  9604 							index = matches.index + matches[0].length;
       
  9605 						} else {
       
  9606 							text = html.substr(index);
       
  9607 							index = html.length;
       
  9608 						}
       
  9609 
       
  9610 						if (isValidElement) {
       
  9611 							if (text.length > 0) {
       
  9612 								self.text(text, true);
       
  9613 							}
       
  9614 
       
  9615 							self.end(value);
       
  9616 						}
       
  9617 
       
  9618 						tokenRegExp.lastIndex = index;
       
  9619 						continue;
       
  9620 					}
       
  9621 
       
  9622 					// Push value on to stack
       
  9623 					if (!isShortEnded) {
       
  9624 						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
       
  9625 							stack.push({name: value, valid: isValidElement});
       
  9626 						} else if (isValidElement) {
       
  9627 							self.end(value);
       
  9628 						}
       
  9629 					}
       
  9630 				} else if ((value = matches[1])) { // Comment
       
  9631 					// Padd comment value to avoid browsers from parsing invalid comments as HTML
       
  9632 					if (value.charAt(0) === '>') {
       
  9633 						value = ' ' + value;
       
  9634 					}
       
  9635 
       
  9636 					if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
       
  9637 						value = ' ' + value;
       
  9638 					}
       
  9639 
       
  9640 					self.comment(value);
       
  9641 				} else if ((value = matches[2])) { // CDATA
       
  9642 					self.cdata(value);
       
  9643 				} else if ((value = matches[3])) { // DOCTYPE
       
  9644 					self.doctype(value);
       
  9645 				} else if ((value = matches[4])) { // PI
       
  9646 					self.pi(value, matches[5]);
       
  9647 				}
       
  9648 
       
  9649 				index = matches.index + matches[0].length;
       
  9650 			}
       
  9651 
       
  9652 			// Text
       
  9653 			if (index < html.length) {
       
  9654 				self.text(decode(html.substr(index)));
       
  9655 			}
       
  9656 
       
  9657 			// Close any open elements
       
  9658 			for (i = stack.length - 1; i >= 0; i--) {
       
  9659 				value = stack[i];
       
  9660 
       
  9661 				if (value.valid) {
       
  9662 					self.end(value.name);
       
  9663 				}
       
  9664 			}
       
  9665 		};
       
  9666 	}
       
  9667 
       
  9668 	SaxParser.findEndTag = findEndTag;
       
  9669 
       
  9670 	return SaxParser;
       
  9671 });
       
  9672 
       
  9673 // Included from: js/tinymce/classes/html/DomParser.js
       
  9674 
       
  9675 /**
       
  9676  * DomParser.js
       
  9677  *
       
  9678  * Copyright, Moxiecode Systems AB
       
  9679  * Released under LGPL License.
       
  9680  *
       
  9681  * License: http://www.tinymce.com/license
       
  9682  * Contributing: http://www.tinymce.com/contributing
       
  9683  */
       
  9684 
       
  9685 /**
       
  9686  * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
       
  9687  * sure that the node tree is valid according to the specified schema.
       
  9688  * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
       
  9689  *
       
  9690  * @example
       
  9691  * var parser = new tinymce.html.DomParser({validate: true}, schema);
       
  9692  * var rootNode = parser.parse('<h1>content</h1>');
       
  9693  *
       
  9694  * @class tinymce.html.DomParser
       
  9695  * @version 3.4
       
  9696  */
       
  9697 define("tinymce/html/DomParser", [
       
  9698 	"tinymce/html/Node",
       
  9699 	"tinymce/html/Schema",
       
  9700 	"tinymce/html/SaxParser",
       
  9701 	"tinymce/util/Tools"
       
  9702 ], function(Node, Schema, SaxParser, Tools) {
       
  9703 	var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
       
  9704 
       
  9705 	/**
       
  9706 	 * Constructs a new DomParser instance.
       
  9707 	 *
       
  9708 	 * @constructor
       
  9709 	 * @method DomParser
       
  9710 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
       
  9711 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
       
  9712 	 */
       
  9713 	return function(settings, schema) {
       
  9714 		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
       
  9715 
       
  9716 		settings = settings || {};
       
  9717 		settings.validate = "validate" in settings ? settings.validate : true;
       
  9718 		settings.root_name = settings.root_name || 'body';
       
  9719 		self.schema = schema = schema || new Schema();
       
  9720 
       
  9721 		function fixInvalidChildren(nodes) {
       
  9722 			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
       
  9723 			var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
       
  9724 
       
  9725 			nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
       
  9726 			nonEmptyElements = schema.getNonEmptyElements();
       
  9727 			textBlockElements = schema.getTextBlockElements();
       
  9728 
       
  9729 			for (ni = 0; ni < nodes.length; ni++) {
       
  9730 				node = nodes[ni];
       
  9731 
       
  9732 				// Already removed or fixed
       
  9733 				if (!node.parent || node.fixed) {
       
  9734 					continue;
       
  9735 				}
       
  9736 
       
  9737 				// If the invalid element is a text block and the text block is within a parent LI element
       
  9738 				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
       
  9739 				if (textBlockElements[node.name] && node.parent.name == 'li') {
       
  9740 					// Move sibling text blocks after LI element
       
  9741 					sibling = node.next;
       
  9742 					while (sibling) {
       
  9743 						if (textBlockElements[sibling.name]) {
       
  9744 							sibling.name = 'li';
       
  9745 							sibling.fixed = true;
       
  9746 							node.parent.insert(sibling, node.parent);
       
  9747 						} else {
       
  9748 							break;
       
  9749 						}
       
  9750 
       
  9751 						sibling = sibling.next;
       
  9752 					}
       
  9753 
       
  9754 					// Unwrap current text block
       
  9755 					node.unwrap(node);
       
  9756 					continue;
       
  9757 				}
       
  9758 
       
  9759 				// Get list of all parent nodes until we find a valid parent to stick the child into
       
  9760 				parents = [node];
       
  9761 				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
       
  9762 					!nonSplitableElements[parent.name]; parent = parent.parent) {
       
  9763 					parents.push(parent);
       
  9764 				}
       
  9765 
       
  9766 				// Found a suitable parent
       
  9767 				if (parent && parents.length > 1) {
       
  9768 					// Reverse the array since it makes looping easier
       
  9769 					parents.reverse();
       
  9770 
       
  9771 					// Clone the related parent and insert that after the moved node
       
  9772 					newParent = currentNode = self.filterNode(parents[0].clone());
       
  9773 
       
  9774 					// Start cloning and moving children on the left side of the target node
       
  9775 					for (i = 0; i < parents.length - 1; i++) {
       
  9776 						if (schema.isValidChild(currentNode.name, parents[i].name)) {
       
  9777 							tempNode = self.filterNode(parents[i].clone());
       
  9778 							currentNode.append(tempNode);
       
  9779 						} else {
       
  9780 							tempNode = currentNode;
       
  9781 						}
       
  9782 
       
  9783 						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) {
       
  9784 							nextNode = childNode.next;
       
  9785 							tempNode.append(childNode);
       
  9786 							childNode = nextNode;
       
  9787 						}
       
  9788 
       
  9789 						currentNode = tempNode;
       
  9790 					}
       
  9791 
       
  9792 					if (!newParent.isEmpty(nonEmptyElements)) {
       
  9793 						parent.insert(newParent, parents[0], true);
       
  9794 						parent.insert(node, newParent);
       
  9795 					} else {
       
  9796 						parent.insert(node, parents[0], true);
       
  9797 					}
       
  9798 
       
  9799 					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
       
  9800 					parent = parents[0];
       
  9801 					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
       
  9802 						parent.empty().remove();
       
  9803 					}
       
  9804 				} else if (node.parent) {
       
  9805 					// If it's an LI try to find a UL/OL for it or wrap it
       
  9806 					if (node.name === 'li') {
       
  9807 						sibling = node.prev;
       
  9808 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
       
  9809 							sibling.append(node);
       
  9810 							continue;
       
  9811 						}
       
  9812 
       
  9813 						sibling = node.next;
       
  9814 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
       
  9815 							sibling.insert(node, sibling.firstChild, true);
       
  9816 							continue;
       
  9817 						}
       
  9818 
       
  9819 						node.wrap(self.filterNode(new Node('ul', 1)));
       
  9820 						continue;
       
  9821 					}
       
  9822 
       
  9823 					// Try wrapping the element in a DIV
       
  9824 					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
       
  9825 						node.wrap(self.filterNode(new Node('div', 1)));
       
  9826 					} else {
       
  9827 						// We failed wrapping it, then remove or unwrap it
       
  9828 						if (node.name === 'style' || node.name === 'script') {
       
  9829 							node.empty().remove();
       
  9830 						} else {
       
  9831 							node.unwrap();
       
  9832 						}
       
  9833 					}
       
  9834 				}
       
  9835 			}
       
  9836 		}
       
  9837 
       
  9838 		/**
       
  9839 		 * Runs the specified node though the element and attributes filters.
       
  9840 		 *
       
  9841 		 * @method filterNode
       
  9842 		 * @param {tinymce.html.Node} Node the node to run filters on.
       
  9843 		 * @return {tinymce.html.Node} The passed in node.
       
  9844 		 */
       
  9845 		self.filterNode = function(node) {
       
  9846 			var i, name, list;
       
  9847 
       
  9848 			// Run element filters
       
  9849 			if (name in nodeFilters) {
       
  9850 				list = matchedNodes[name];
       
  9851 
       
  9852 				if (list) {
       
  9853 					list.push(node);
       
  9854 				} else {
       
  9855 					matchedNodes[name] = [node];
       
  9856 				}
       
  9857 			}
       
  9858 
       
  9859 			// Run attribute filters
       
  9860 			i = attributeFilters.length;
       
  9861 			while (i--) {
       
  9862 				name = attributeFilters[i].name;
       
  9863 
       
  9864 				if (name in node.attributes.map) {
       
  9865 					list = matchedAttributes[name];
       
  9866 
       
  9867 					if (list) {
       
  9868 						list.push(node);
       
  9869 					} else {
       
  9870 						matchedAttributes[name] = [node];
       
  9871 					}
       
  9872 				}
       
  9873 			}
       
  9874 
       
  9875 			return node;
       
  9876 		};
       
  9877 
       
  9878 		/**
       
  9879 		 * Adds a node filter function to the parser, the parser will collect the specified nodes by name
       
  9880 		 * and then execute the callback ones it has finished parsing the document.
       
  9881 		 *
       
  9882 		 * @example
       
  9883 		 * parser.addNodeFilter('p,h1', function(nodes, name) {
       
  9884 		 *		for (var i = 0; i < nodes.length; i++) {
       
  9885 		 *			console.log(nodes[i].name);
       
  9886 		 *		}
       
  9887 		 * });
       
  9888 		 * @method addNodeFilter
       
  9889 		 * @method {String} name Comma separated list of nodes to collect.
       
  9890 		 * @param {function} callback Callback function to execute once it has collected nodes.
       
  9891 		 */
       
  9892 		self.addNodeFilter = function(name, callback) {
       
  9893 			each(explode(name), function(name) {
       
  9894 				var list = nodeFilters[name];
       
  9895 
       
  9896 				if (!list) {
       
  9897 					nodeFilters[name] = list = [];
       
  9898 				}
       
  9899 
       
  9900 				list.push(callback);
       
  9901 			});
       
  9902 		};
       
  9903 
       
  9904 		/**
       
  9905 		 * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
       
  9906 		 * and then execute the callback ones it has finished parsing the document.
       
  9907 		 *
       
  9908 		 * @example
       
  9909 		 * parser.addAttributeFilter('src,href', function(nodes, name) {
       
  9910 		 *		for (var i = 0; i < nodes.length; i++) {
       
  9911 		 *			console.log(nodes[i].name);
       
  9912 		 *		}
       
  9913 		 * });
       
  9914 		 * @method addAttributeFilter
       
  9915 		 * @method {String} name Comma separated list of nodes to collect.
       
  9916 		 * @param {function} callback Callback function to execute once it has collected nodes.
       
  9917 		 */
       
  9918 		self.addAttributeFilter = function(name, callback) {
       
  9919 			each(explode(name), function(name) {
       
  9920 				var i;
       
  9921 
       
  9922 				for (i = 0; i < attributeFilters.length; i++) {
       
  9923 					if (attributeFilters[i].name === name) {
       
  9924 						attributeFilters[i].callbacks.push(callback);
       
  9925 						return;
       
  9926 					}
       
  9927 				}
       
  9928 
       
  9929 				attributeFilters.push({name: name, callbacks: [callback]});
       
  9930 			});
       
  9931 		};
       
  9932 
       
  9933 		/**
       
  9934 		 * Parses the specified HTML string into a DOM like node tree and returns the result.
       
  9935 		 *
       
  9936 		 * @example
       
  9937 		 * var rootNode = new DomParser({...}).parse('<b>text</b>');
       
  9938 		 * @method parse
       
  9939 		 * @param {String} html Html string to sax parse.
       
  9940 		 * @param {Object} args Optional args object that gets passed to all filter functions.
       
  9941 		 * @return {tinymce.html.Node} Root node containing the tree.
       
  9942 		 */
       
  9943 		self.parse = function(html, args) {
       
  9944 			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
       
  9945 			var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
       
  9946 			var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
       
  9947 			var children, nonEmptyElements, rootBlockName;
       
  9948 
       
  9949 			args = args || {};
       
  9950 			matchedNodes = {};
       
  9951 			matchedAttributes = {};
       
  9952 			blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
       
  9953 			nonEmptyElements = schema.getNonEmptyElements();
       
  9954 			children = schema.children;
       
  9955 			validate = settings.validate;
       
  9956 			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
       
  9957 
       
  9958 			whiteSpaceElements = schema.getWhiteSpaceElements();
       
  9959 			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
       
  9960 			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
       
  9961 			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
       
  9962 			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
       
  9963 
       
  9964 			function addRootBlocks() {
       
  9965 				var node = rootNode.firstChild, next, rootBlockNode;
       
  9966 
       
  9967 				// Removes whitespace at beginning and end of block so:
       
  9968 				// <p> x </p> -> <p>x</p>
       
  9969 				function trim(rootBlockNode) {
       
  9970 					if (rootBlockNode) {
       
  9971 						node = rootBlockNode.firstChild;
       
  9972 						if (node && node.type == 3) {
       
  9973 							node.value = node.value.replace(startWhiteSpaceRegExp, '');
       
  9974 						}
       
  9975 
       
  9976 						node = rootBlockNode.lastChild;
       
  9977 						if (node && node.type == 3) {
       
  9978 							node.value = node.value.replace(endWhiteSpaceRegExp, '');
       
  9979 						}
       
  9980 					}
       
  9981 				}
       
  9982 
       
  9983 				// Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
       
  9984 				if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
       
  9985 					return;
       
  9986 				}
       
  9987 
       
  9988 				while (node) {
       
  9989 					next = node.next;
       
  9990 
       
  9991 					if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
       
  9992 						!blockElements[node.name] && !node.attr('data-mce-type'))) {
       
  9993 						if (!rootBlockNode) {
       
  9994 							// Create a new root block element
       
  9995 							rootBlockNode = createNode(rootBlockName, 1);
       
  9996 							rootBlockNode.attr(settings.forced_root_block_attrs);
       
  9997 							rootNode.insert(rootBlockNode, node);
       
  9998 							rootBlockNode.append(node);
       
  9999 						} else {
       
 10000 							rootBlockNode.append(node);
       
 10001 						}
       
 10002 					} else {
       
 10003 						trim(rootBlockNode);
       
 10004 						rootBlockNode = null;
       
 10005 					}
       
 10006 
       
 10007 					node = next;
       
 10008 				}
       
 10009 
       
 10010 				trim(rootBlockNode);
       
 10011 			}
       
 10012 
       
 10013 			function createNode(name, type) {
       
 10014 				var node = new Node(name, type), list;
       
 10015 
       
 10016 				if (name in nodeFilters) {
       
 10017 					list = matchedNodes[name];
       
 10018 
       
 10019 					if (list) {
       
 10020 						list.push(node);
       
 10021 					} else {
       
 10022 						matchedNodes[name] = [node];
       
 10023 					}
       
 10024 				}
       
 10025 
       
 10026 				return node;
       
 10027 			}
       
 10028 
       
 10029 			function removeWhitespaceBefore(node) {
       
 10030 				var textNode, textVal, sibling;
       
 10031 
       
 10032 				for (textNode = node.prev; textNode && textNode.type === 3;) {
       
 10033 					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
       
 10034 
       
 10035 					if (textVal.length > 0) {
       
 10036 						textNode.value = textVal;
       
 10037 						textNode = textNode.prev;
       
 10038 					} else {
       
 10039 						sibling = textNode.prev;
       
 10040 						textNode.remove();
       
 10041 						textNode = sibling;
       
 10042 					}
       
 10043 				}
       
 10044 			}
       
 10045 
       
 10046 			function cloneAndExcludeBlocks(input) {
       
 10047 				var name, output = {};
       
 10048 
       
 10049 				for (name in input) {
       
 10050 					if (name !== 'li' && name != 'p') {
       
 10051 						output[name] = input[name];
       
 10052 					}
       
 10053 				}
       
 10054 
       
 10055 				return output;
       
 10056 			}
       
 10057 
       
 10058 			parser = new SaxParser({
       
 10059 				validate: validate,
       
 10060 				allow_script_urls: settings.allow_script_urls,
       
 10061 				allow_conditional_comments: settings.allow_conditional_comments,
       
 10062 
       
 10063 				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
       
 10064 				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
       
 10065 
       
 10066 				cdata: function(text) {
       
 10067 					node.append(createNode('#cdata', 4)).value = text;
       
 10068 				},
       
 10069 
       
 10070 				text: function(text, raw) {
       
 10071 					var textNode;
       
 10072 
       
 10073 					// Trim all redundant whitespace on non white space elements
       
 10074 					if (!isInWhiteSpacePreservedElement) {
       
 10075 						text = text.replace(allWhiteSpaceRegExp, ' ');
       
 10076 
       
 10077 						if (node.lastChild && blockElements[node.lastChild.name]) {
       
 10078 							text = text.replace(startWhiteSpaceRegExp, '');
       
 10079 						}
       
 10080 					}
       
 10081 
       
 10082 					// Do we need to create the node
       
 10083 					if (text.length !== 0) {
       
 10084 						textNode = createNode('#text', 3);
       
 10085 						textNode.raw = !!raw;
       
 10086 						node.append(textNode).value = text;
       
 10087 					}
       
 10088 				},
       
 10089 
       
 10090 				comment: function(text) {
       
 10091 					node.append(createNode('#comment', 8)).value = text;
       
 10092 				},
       
 10093 
       
 10094 				pi: function(name, text) {
       
 10095 					node.append(createNode(name, 7)).value = text;
       
 10096 					removeWhitespaceBefore(node);
       
 10097 				},
       
 10098 
       
 10099 				doctype: function(text) {
       
 10100 					var newNode;
       
 10101 
       
 10102 					newNode = node.append(createNode('#doctype', 10));
       
 10103 					newNode.value = text;
       
 10104 					removeWhitespaceBefore(node);
       
 10105 				},
       
 10106 
       
 10107 				start: function(name, attrs, empty) {
       
 10108 					var newNode, attrFiltersLen, elementRule, attrName, parent;
       
 10109 
       
 10110 					elementRule = validate ? schema.getElementRule(name) : {};
       
 10111 					if (elementRule) {
       
 10112 						newNode = createNode(elementRule.outputName || name, 1);
       
 10113 						newNode.attributes = attrs;
       
 10114 						newNode.shortEnded = empty;
       
 10115 
       
 10116 						node.append(newNode);
       
 10117 
       
 10118 						// Check if node is valid child of the parent node is the child is
       
 10119 						// unknown we don't collect it since it's probably a custom element
       
 10120 						parent = children[node.name];
       
 10121 						if (parent && children[newNode.name] && !parent[newNode.name]) {
       
 10122 							invalidChildren.push(newNode);
       
 10123 						}
       
 10124 
       
 10125 						attrFiltersLen = attributeFilters.length;
       
 10126 						while (attrFiltersLen--) {
       
 10127 							attrName = attributeFilters[attrFiltersLen].name;
       
 10128 
       
 10129 							if (attrName in attrs.map) {
       
 10130 								list = matchedAttributes[attrName];
       
 10131 
       
 10132 								if (list) {
       
 10133 									list.push(newNode);
       
 10134 								} else {
       
 10135 									matchedAttributes[attrName] = [newNode];
       
 10136 								}
       
 10137 							}
       
 10138 						}
       
 10139 
       
 10140 						// Trim whitespace before block
       
 10141 						if (blockElements[name]) {
       
 10142 							removeWhitespaceBefore(newNode);
       
 10143 						}
       
 10144 
       
 10145 						// Change current node if the element wasn't empty i.e not <br /> or <img />
       
 10146 						if (!empty) {
       
 10147 							node = newNode;
       
 10148 						}
       
 10149 
       
 10150 						// Check if we are inside a whitespace preserved element
       
 10151 						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
       
 10152 							isInWhiteSpacePreservedElement = true;
       
 10153 						}
       
 10154 					}
       
 10155 				},
       
 10156 
       
 10157 				end: function(name) {
       
 10158 					var textNode, elementRule, text, sibling, tempNode;
       
 10159 
       
 10160 					elementRule = validate ? schema.getElementRule(name) : {};
       
 10161 					if (elementRule) {
       
 10162 						if (blockElements[name]) {
       
 10163 							if (!isInWhiteSpacePreservedElement) {
       
 10164 								// Trim whitespace of the first node in a block
       
 10165 								textNode = node.firstChild;
       
 10166 								if (textNode && textNode.type === 3) {
       
 10167 									text = textNode.value.replace(startWhiteSpaceRegExp, '');
       
 10168 
       
 10169 									// Any characters left after trim or should we remove it
       
 10170 									if (text.length > 0) {
       
 10171 										textNode.value = text;
       
 10172 										textNode = textNode.next;
       
 10173 									} else {
       
 10174 										sibling = textNode.next;
       
 10175 										textNode.remove();
       
 10176 										textNode = sibling;
       
 10177 
       
 10178 										// Remove any pure whitespace siblings
       
 10179 										while (textNode && textNode.type === 3) {
       
 10180 											text = textNode.value;
       
 10181 											sibling = textNode.next;
       
 10182 
       
 10183 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
       
 10184 												textNode.remove();
       
 10185 												textNode = sibling;
       
 10186 											}
       
 10187 
       
 10188 											textNode = sibling;
       
 10189 										}
       
 10190 									}
       
 10191 								}
       
 10192 
       
 10193 								// Trim whitespace of the last node in a block
       
 10194 								textNode = node.lastChild;
       
 10195 								if (textNode && textNode.type === 3) {
       
 10196 									text = textNode.value.replace(endWhiteSpaceRegExp, '');
       
 10197 
       
 10198 									// Any characters left after trim or should we remove it
       
 10199 									if (text.length > 0) {
       
 10200 										textNode.value = text;
       
 10201 										textNode = textNode.prev;
       
 10202 									} else {
       
 10203 										sibling = textNode.prev;
       
 10204 										textNode.remove();
       
 10205 										textNode = sibling;
       
 10206 
       
 10207 										// Remove any pure whitespace siblings
       
 10208 										while (textNode && textNode.type === 3) {
       
 10209 											text = textNode.value;
       
 10210 											sibling = textNode.prev;
       
 10211 
       
 10212 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
       
 10213 												textNode.remove();
       
 10214 												textNode = sibling;
       
 10215 											}
       
 10216 
       
 10217 											textNode = sibling;
       
 10218 										}
       
 10219 									}
       
 10220 								}
       
 10221 							}
       
 10222 
       
 10223 							// Trim start white space
       
 10224 							// Removed due to: #5424
       
 10225 							/*textNode = node.prev;
       
 10226 							if (textNode && textNode.type === 3) {
       
 10227 								text = textNode.value.replace(startWhiteSpaceRegExp, '');
       
 10228 
       
 10229 								if (text.length > 0)
       
 10230 									textNode.value = text;
       
 10231 								else
       
 10232 									textNode.remove();
       
 10233 							}*/
       
 10234 						}
       
 10235 
       
 10236 						// Check if we exited a whitespace preserved element
       
 10237 						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
       
 10238 							isInWhiteSpacePreservedElement = false;
       
 10239 						}
       
 10240 
       
 10241 						// Handle empty nodes
       
 10242 						if (elementRule.removeEmpty || elementRule.paddEmpty) {
       
 10243 							if (node.isEmpty(nonEmptyElements)) {
       
 10244 								if (elementRule.paddEmpty) {
       
 10245 									node.empty().append(new Node('#text', '3')).value = '\u00a0';
       
 10246 								} else {
       
 10247 									// Leave nodes that have a name like <a name="name">
       
 10248 									if (!node.attributes.map.name && !node.attributes.map.id) {
       
 10249 										tempNode = node.parent;
       
 10250 
       
 10251 										if (blockElements[node.name]) {
       
 10252 											node.empty().remove();
       
 10253 										} else {
       
 10254 											node.unwrap();
       
 10255 										}
       
 10256 
       
 10257 										node = tempNode;
       
 10258 										return;
       
 10259 									}
       
 10260 								}
       
 10261 							}
       
 10262 						}
       
 10263 
       
 10264 						node = node.parent;
       
 10265 					}
       
 10266 				}
       
 10267 			}, schema);
       
 10268 
       
 10269 			rootNode = node = new Node(args.context || settings.root_name, 11);
       
 10270 
       
 10271 			parser.parse(html);
       
 10272 
       
 10273 			// Fix invalid children or report invalid children in a contextual parsing
       
 10274 			if (validate && invalidChildren.length) {
       
 10275 				if (!args.context) {
       
 10276 					fixInvalidChildren(invalidChildren);
       
 10277 				} else {
       
 10278 					args.invalid = true;
       
 10279 				}
       
 10280 			}
       
 10281 
       
 10282 			// Wrap nodes in the root into block elements if the root is body
       
 10283 			if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
       
 10284 				addRootBlocks();
       
 10285 			}
       
 10286 
       
 10287 			// Run filters only when the contents is valid
       
 10288 			if (!args.invalid) {
       
 10289 				// Run node filters
       
 10290 				for (name in matchedNodes) {
       
 10291 					list = nodeFilters[name];
       
 10292 					nodes = matchedNodes[name];
       
 10293 
       
 10294 					// Remove already removed children
       
 10295 					fi = nodes.length;
       
 10296 					while (fi--) {
       
 10297 						if (!nodes[fi].parent) {
       
 10298 							nodes.splice(fi, 1);
       
 10299 						}
       
 10300 					}
       
 10301 
       
 10302 					for (i = 0, l = list.length; i < l; i++) {
       
 10303 						list[i](nodes, name, args);
       
 10304 					}
       
 10305 				}
       
 10306 
       
 10307 				// Run attribute filters
       
 10308 				for (i = 0, l = attributeFilters.length; i < l; i++) {
       
 10309 					list = attributeFilters[i];
       
 10310 
       
 10311 					if (list.name in matchedAttributes) {
       
 10312 						nodes = matchedAttributes[list.name];
       
 10313 
       
 10314 						// Remove already removed children
       
 10315 						fi = nodes.length;
       
 10316 						while (fi--) {
       
 10317 							if (!nodes[fi].parent) {
       
 10318 								nodes.splice(fi, 1);
       
 10319 							}
       
 10320 						}
       
 10321 
       
 10322 						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
       
 10323 							list.callbacks[fi](nodes, list.name, args);
       
 10324 						}
       
 10325 					}
       
 10326 				}
       
 10327 			}
       
 10328 
       
 10329 			return rootNode;
       
 10330 		};
       
 10331 
       
 10332 		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
       
 10333 		// make it possible to place the caret inside empty blocks. This logic tries to remove
       
 10334 		// these elements and keep br elements that where intended to be there intact
       
 10335 		if (settings.remove_trailing_brs) {
       
 10336 			self.addNodeFilter('br', function(nodes) {
       
 10337 				var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
       
 10338 				var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
       
 10339 				var elementRule, textNode;
       
 10340 
       
 10341 				// Remove brs from body element as well
       
 10342 				blockElements.body = 1;
       
 10343 
       
 10344 				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
       
 10345 				for (i = 0; i < l; i++) {
       
 10346 					node = nodes[i];
       
 10347 					parent = node.parent;
       
 10348 
       
 10349 					if (blockElements[node.parent.name] && node === parent.lastChild) {
       
 10350 						// Loop all nodes to the left of the current node and check for other BR elements
       
 10351 						// excluding bookmarks since they are invisible
       
 10352 						prev = node.prev;
       
 10353 						while (prev) {
       
 10354 							prevName = prev.name;
       
 10355 
       
 10356 							// Ignore bookmarks
       
 10357 							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
       
 10358 								// Found a non BR element
       
 10359 								if (prevName !== "br") {
       
 10360 									break;
       
 10361 								}
       
 10362 
       
 10363 								// Found another br it's a <br><br> structure then don't remove anything
       
 10364 								if (prevName === 'br') {
       
 10365 									node = null;
       
 10366 									break;
       
 10367 								}
       
 10368 							}
       
 10369 
       
 10370 							prev = prev.prev;
       
 10371 						}
       
 10372 
       
 10373 						if (node) {
       
 10374 							node.remove();
       
 10375 
       
 10376 							// Is the parent to be considered empty after we removed the BR
       
 10377 							if (parent.isEmpty(nonEmptyElements)) {
       
 10378 								elementRule = schema.getElementRule(parent.name);
       
 10379 
       
 10380 								// Remove or padd the element depending on schema rule
       
 10381 								if (elementRule) {
       
 10382 									if (elementRule.removeEmpty) {
       
 10383 										parent.remove();
       
 10384 									} else if (elementRule.paddEmpty) {
       
 10385 										parent.empty().append(new Node('#text', 3)).value = '\u00a0';
       
 10386 									}
       
 10387 								}
       
 10388 							}
       
 10389 						}
       
 10390 					} else {
       
 10391 						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
       
 10392 						// so they become <p><b><i>&nbsp;</i></b></p>
       
 10393 						lastParent = node;
       
 10394 						while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
       
 10395 							lastParent = parent;
       
 10396 
       
 10397 							if (blockElements[parent.name]) {
       
 10398 								break;
       
 10399 							}
       
 10400 
       
 10401 							parent = parent.parent;
       
 10402 						}
       
 10403 
       
 10404 						if (lastParent === parent) {
       
 10405 							textNode = new Node('#text', 3);
       
 10406 							textNode.value = '\u00a0';
       
 10407 							node.replace(textNode);
       
 10408 						}
       
 10409 					}
       
 10410 				}
       
 10411 			});
       
 10412 		}
       
 10413 
       
 10414 		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
       
 10415 		if (!settings.allow_html_in_named_anchor) {
       
 10416 			self.addAttributeFilter('id,name', function(nodes) {
       
 10417 				var i = nodes.length, sibling, prevSibling, parent, node;
       
 10418 
       
 10419 				while (i--) {
       
 10420 					node = nodes[i];
       
 10421 					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
       
 10422 						parent = node.parent;
       
 10423 
       
 10424 						// Move children after current node
       
 10425 						sibling = node.lastChild;
       
 10426 						do {
       
 10427 							prevSibling = sibling.prev;
       
 10428 							parent.insert(sibling, node);
       
 10429 							sibling = prevSibling;
       
 10430 						} while (sibling);
       
 10431 					}
       
 10432 				}
       
 10433 			});
       
 10434 		}
       
 10435 
       
 10436 		if (settings.validate && schema.getValidClasses()) {
       
 10437 			self.addAttributeFilter('class', function(nodes) {
       
 10438 				var i = nodes.length, node, classList, ci, className, classValue;
       
 10439 				var validClasses = schema.getValidClasses(), validClassesMap, valid;
       
 10440 
       
 10441 				while (i--) {
       
 10442 					node = nodes[i];
       
 10443 					classList = node.attr('class').split(' ');
       
 10444 					classValue = '';
       
 10445 
       
 10446 					for (ci = 0; ci < classList.length; ci++) {
       
 10447 						className = classList[ci];
       
 10448 						valid = false;
       
 10449 
       
 10450 						validClassesMap = validClasses['*'];
       
 10451 						if (validClassesMap && validClassesMap[className]) {
       
 10452 							valid = true;
       
 10453 						}
       
 10454 
       
 10455 						validClassesMap = validClasses[node.name];
       
 10456 						if (!valid && validClassesMap && validClassesMap[className]) {
       
 10457 							valid = true;
       
 10458 						}
       
 10459 
       
 10460 						if (valid) {
       
 10461 							if (classValue) {
       
 10462 								classValue += ' ';
       
 10463 							}
       
 10464 
       
 10465 							classValue += className;
       
 10466 						}
       
 10467 					}
       
 10468 
       
 10469 					if (!classValue.length) {
       
 10470 						classValue = null;
       
 10471 					}
       
 10472 
       
 10473 					node.attr('class', classValue);
       
 10474 				}
       
 10475 			});
       
 10476 		}
       
 10477 	};
       
 10478 });
       
 10479 
       
 10480 // Included from: js/tinymce/classes/html/Writer.js
       
 10481 
       
 10482 /**
       
 10483  * Writer.js
       
 10484  *
       
 10485  * Copyright, Moxiecode Systems AB
       
 10486  * Released under LGPL License.
       
 10487  *
       
 10488  * License: http://www.tinymce.com/license
       
 10489  * Contributing: http://www.tinymce.com/contributing
       
 10490  */
       
 10491 
       
 10492 /**
       
 10493  * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
       
 10494  *
       
 10495  * @class tinymce.html.Writer
       
 10496  * @example
       
 10497  * var writer = new tinymce.html.Writer({indent: true});
       
 10498  * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
       
 10499  * console.log(writer.getContent());
       
 10500  *
       
 10501  * @class tinymce.html.Writer
       
 10502  * @version 3.4
       
 10503  */
       
 10504 define("tinymce/html/Writer", [
       
 10505 	"tinymce/html/Entities",
       
 10506 	"tinymce/util/Tools"
       
 10507 ], function(Entities, Tools) {
       
 10508 	var makeMap = Tools.makeMap;
       
 10509 
       
 10510 	/**
       
 10511 	 * Constructs a new Writer instance.
       
 10512 	 *
       
 10513 	 * @constructor
       
 10514 	 * @method Writer
       
 10515 	 * @param {Object} settings Name/value settings object.
       
 10516 	 */
       
 10517 	return function(settings) {
       
 10518 		var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
       
 10519 
       
 10520 		settings = settings || {};
       
 10521 		indent = settings.indent;
       
 10522 		indentBefore = makeMap(settings.indent_before || '');
       
 10523 		indentAfter = makeMap(settings.indent_after || '');
       
 10524 		encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
       
 10525 		htmlOutput = settings.element_format == "html";
       
 10526 
       
 10527 		return {
       
 10528 			/**
       
 10529 			 * Writes the a start element such as <p id="a">.
       
 10530 			 *
       
 10531 			 * @method start
       
 10532 			 * @param {String} name Name of the element.
       
 10533 			 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
       
 10534 			 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
       
 10535 			 */
       
 10536 			start: function(name, attrs, empty) {
       
 10537 				var i, l, attr, value;
       
 10538 
       
 10539 				if (indent && indentBefore[name] && html.length > 0) {
       
 10540 					value = html[html.length - 1];
       
 10541 
       
 10542 					if (value.length > 0 && value !== '\n') {
       
 10543 						html.push('\n');
       
 10544 					}
       
 10545 				}
       
 10546 
       
 10547 				html.push('<', name);
       
 10548 
       
 10549 				if (attrs) {
       
 10550 					for (i = 0, l = attrs.length; i < l; i++) {
       
 10551 						attr = attrs[i];
       
 10552 						html.push(' ', attr.name, '="', encode(attr.value, true), '"');
       
 10553 					}
       
 10554 				}
       
 10555 
       
 10556 				if (!empty || htmlOutput) {
       
 10557 					html[html.length] = '>';
       
 10558 				} else {
       
 10559 					html[html.length] = ' />';
       
 10560 				}
       
 10561 
       
 10562 				if (empty && indent && indentAfter[name] && html.length > 0) {
       
 10563 					value = html[html.length - 1];
       
 10564 
       
 10565 					if (value.length > 0 && value !== '\n') {
       
 10566 						html.push('\n');
       
 10567 					}
       
 10568 				}
       
 10569 			},
       
 10570 
       
 10571 			/**
       
 10572 			 * Writes the a end element such as </p>.
       
 10573 			 *
       
 10574 			 * @method end
       
 10575 			 * @param {String} name Name of the element.
       
 10576 			 */
       
 10577 			end: function(name) {
       
 10578 				var value;
       
 10579 
       
 10580 				/*if (indent && indentBefore[name] && html.length > 0) {
       
 10581 					value = html[html.length - 1];
       
 10582 
       
 10583 					if (value.length > 0 && value !== '\n')
       
 10584 						html.push('\n');
       
 10585 				}*/
       
 10586 
       
 10587 				html.push('</', name, '>');
       
 10588 
       
 10589 				if (indent && indentAfter[name] && html.length > 0) {
       
 10590 					value = html[html.length - 1];
       
 10591 
       
 10592 					if (value.length > 0 && value !== '\n') {
       
 10593 						html.push('\n');
       
 10594 					}
       
 10595 				}
       
 10596 			},
       
 10597 
       
 10598 			/**
       
 10599 			 * Writes a text node.
       
 10600 			 *
       
 10601 			 * @method text
       
 10602 			 * @param {String} text String to write out.
       
 10603 			 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
       
 10604 			 */
       
 10605 			text: function(text, raw) {
       
 10606 				if (text.length > 0) {
       
 10607 					html[html.length] = raw ? text : encode(text);
       
 10608 				}
       
 10609 			},
       
 10610 
       
 10611 			/**
       
 10612 			 * Writes a cdata node such as <![CDATA[data]]>.
       
 10613 			 *
       
 10614 			 * @method cdata
       
 10615 			 * @param {String} text String to write out inside the cdata.
       
 10616 			 */
       
 10617 			cdata: function(text) {
       
 10618 				html.push('<![CDATA[', text, ']]>');
       
 10619 			},
       
 10620 
       
 10621 			/**
       
 10622 			 * Writes a comment node such as <!-- Comment -->.
       
 10623 			 *
       
 10624 			 * @method cdata
       
 10625 			 * @param {String} text String to write out inside the comment.
       
 10626 			 */
       
 10627 			comment: function(text) {
       
 10628 				html.push('<!--', text, '-->');
       
 10629 			},
       
 10630 
       
 10631 			/**
       
 10632 			 * Writes a PI node such as <?xml attr="value" ?>.
       
 10633 			 *
       
 10634 			 * @method pi
       
 10635 			 * @param {String} name Name of the pi.
       
 10636 			 * @param {String} text String to write out inside the pi.
       
 10637 			 */
       
 10638 			pi: function(name, text) {
       
 10639 				if (text) {
       
 10640 					html.push('<?', name, ' ', encode(text), '?>');
       
 10641 				} else {
       
 10642 					html.push('<?', name, '?>');
       
 10643 				}
       
 10644 
       
 10645 				if (indent) {
       
 10646 					html.push('\n');
       
 10647 				}
       
 10648 			},
       
 10649 
       
 10650 			/**
       
 10651 			 * Writes a doctype node such as <!DOCTYPE data>.
       
 10652 			 *
       
 10653 			 * @method doctype
       
 10654 			 * @param {String} text String to write out inside the doctype.
       
 10655 			 */
       
 10656 			doctype: function(text) {
       
 10657 				html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
       
 10658 			},
       
 10659 
       
 10660 			/**
       
 10661 			 * Resets the internal buffer if one wants to reuse the writer.
       
 10662 			 *
       
 10663 			 * @method reset
       
 10664 			 */
       
 10665 			reset: function() {
       
 10666 				html.length = 0;
       
 10667 			},
       
 10668 
       
 10669 			/**
       
 10670 			 * Returns the contents that got serialized.
       
 10671 			 *
       
 10672 			 * @method getContent
       
 10673 			 * @return {String} HTML contents that got written down.
       
 10674 			 */
       
 10675 			getContent: function() {
       
 10676 				return html.join('').replace(/\n$/, '');
       
 10677 			}
       
 10678 		};
       
 10679 	};
       
 10680 });
       
 10681 
       
 10682 // Included from: js/tinymce/classes/html/Serializer.js
       
 10683 
       
 10684 /**
       
 10685  * Serializer.js
       
 10686  *
       
 10687  * Copyright, Moxiecode Systems AB
       
 10688  * Released under LGPL License.
       
 10689  *
       
 10690  * License: http://www.tinymce.com/license
       
 10691  * Contributing: http://www.tinymce.com/contributing
       
 10692  */
       
 10693 
       
 10694 /**
       
 10695  * This class is used to serialize down the DOM tree into a string using a Writer instance.
       
 10696  *
       
 10697  *
       
 10698  * @example
       
 10699  * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
       
 10700  * @class tinymce.html.Serializer
       
 10701  * @version 3.4
       
 10702  */
       
 10703 define("tinymce/html/Serializer", [
       
 10704 	"tinymce/html/Writer",
       
 10705 	"tinymce/html/Schema"
       
 10706 ], function(Writer, Schema) {
       
 10707 	/**
       
 10708 	 * Constructs a new Serializer instance.
       
 10709 	 *
       
 10710 	 * @constructor
       
 10711 	 * @method Serializer
       
 10712 	 * @param {Object} settings Name/value settings object.
       
 10713 	 * @param {tinymce.html.Schema} schema Schema instance to use.
       
 10714 	 */
       
 10715 	return function(settings, schema) {
       
 10716 		var self = this, writer = new Writer(settings);
       
 10717 
       
 10718 		settings = settings || {};
       
 10719 		settings.validate = "validate" in settings ? settings.validate : true;
       
 10720 
       
 10721 		self.schema = schema = schema || new Schema();
       
 10722 		self.writer = writer;
       
 10723 
       
 10724 		/**
       
 10725 		 * Serializes the specified node into a string.
       
 10726 		 *
       
 10727 		 * @example
       
 10728 		 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
       
 10729 		 * @method serialize
       
 10730 		 * @param {tinymce.html.Node} node Node instance to serialize.
       
 10731 		 * @return {String} String with HTML based on DOM tree.
       
 10732 		 */
       
 10733 		self.serialize = function(node) {
       
 10734 			var handlers, validate;
       
 10735 
       
 10736 			validate = settings.validate;
       
 10737 
       
 10738 			handlers = {
       
 10739 				// #text
       
 10740 				3: function(node) {
       
 10741 					writer.text(node.value, node.raw);
       
 10742 				},
       
 10743 
       
 10744 				// #comment
       
 10745 				8: function(node) {
       
 10746 					writer.comment(node.value);
       
 10747 				},
       
 10748 
       
 10749 				// Processing instruction
       
 10750 				7: function(node) {
       
 10751 					writer.pi(node.name, node.value);
       
 10752 				},
       
 10753 
       
 10754 				// Doctype
       
 10755 				10: function(node) {
       
 10756 					writer.doctype(node.value);
       
 10757 				},
       
 10758 
       
 10759 				// CDATA
       
 10760 				4: function(node) {
       
 10761 					writer.cdata(node.value);
       
 10762 				},
       
 10763 
       
 10764 				// Document fragment
       
 10765 				11: function(node) {
       
 10766 					if ((node = node.firstChild)) {
       
 10767 						do {
       
 10768 							walk(node);
       
 10769 						} while ((node = node.next));
       
 10770 					}
       
 10771 				}
       
 10772 			};
       
 10773 
       
 10774 			writer.reset();
       
 10775 
       
 10776 			function walk(node) {
       
 10777 				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
       
 10778 
       
 10779 				if (!handler) {
       
 10780 					name = node.name;
       
 10781 					isEmpty = node.shortEnded;
       
 10782 					attrs = node.attributes;
       
 10783 
       
 10784 					// Sort attributes
       
 10785 					if (validate && attrs && attrs.length > 1) {
       
 10786 						sortedAttrs = [];
       
 10787 						sortedAttrs.map = {};
       
 10788 
       
 10789 						elementRule = schema.getElementRule(node.name);
       
 10790 						for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
       
 10791 							attrName = elementRule.attributesOrder[i];
       
 10792 
       
 10793 							if (attrName in attrs.map) {
       
 10794 								attrValue = attrs.map[attrName];
       
 10795 								sortedAttrs.map[attrName] = attrValue;
       
 10796 								sortedAttrs.push({name: attrName, value: attrValue});
       
 10797 							}
       
 10798 						}
       
 10799 
       
 10800 						for (i = 0, l = attrs.length; i < l; i++) {
       
 10801 							attrName = attrs[i].name;
       
 10802 
       
 10803 							if (!(attrName in sortedAttrs.map)) {
       
 10804 								attrValue = attrs.map[attrName];
       
 10805 								sortedAttrs.map[attrName] = attrValue;
       
 10806 								sortedAttrs.push({name: attrName, value: attrValue});
       
 10807 							}
       
 10808 						}
       
 10809 
       
 10810 						attrs = sortedAttrs;
       
 10811 					}
       
 10812 
       
 10813 					writer.start(node.name, attrs, isEmpty);
       
 10814 
       
 10815 					if (!isEmpty) {
       
 10816 						if ((node = node.firstChild)) {
       
 10817 							do {
       
 10818 								walk(node);
       
 10819 							} while ((node = node.next));
       
 10820 						}
       
 10821 
       
 10822 						writer.end(name);
       
 10823 					}
       
 10824 				} else {
       
 10825 					handler(node);
       
 10826 				}
       
 10827 			}
       
 10828 
       
 10829 			// Serialize element and treat all non elements as fragments
       
 10830 			if (node.type == 1 && !settings.inner) {
       
 10831 				walk(node);
       
 10832 			} else {
       
 10833 				handlers[11](node);
       
 10834 			}
       
 10835 
       
 10836 			return writer.getContent();
       
 10837 		};
       
 10838 	};
       
 10839 });
       
 10840 
       
 10841 // Included from: js/tinymce/classes/dom/Serializer.js
       
 10842 
       
 10843 /**
       
 10844  * Serializer.js
       
 10845  *
       
 10846  * Copyright, Moxiecode Systems AB
       
 10847  * Released under LGPL License.
       
 10848  *
       
 10849  * License: http://www.tinymce.com/license
       
 10850  * Contributing: http://www.tinymce.com/contributing
       
 10851  */
       
 10852 
       
 10853 /**
       
 10854  * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
       
 10855  * more details and examples on how to use this class.
       
 10856  *
       
 10857  * @class tinymce.dom.Serializer
       
 10858  */
       
 10859 define("tinymce/dom/Serializer", [
       
 10860 	"tinymce/dom/DOMUtils",
       
 10861 	"tinymce/html/DomParser",
       
 10862 	"tinymce/html/Entities",
       
 10863 	"tinymce/html/Serializer",
       
 10864 	"tinymce/html/Node",
       
 10865 	"tinymce/html/Schema",
       
 10866 	"tinymce/Env",
       
 10867 	"tinymce/util/Tools"
       
 10868 ], function(DOMUtils, DomParser, Entities, Serializer, Node, Schema, Env, Tools) {
       
 10869 	var each = Tools.each, trim = Tools.trim;
       
 10870 	var DOM = DOMUtils.DOM;
       
 10871 
       
 10872 	/**
       
 10873 	 * Constructs a new DOM serializer class.
       
 10874 	 *
       
 10875 	 * @constructor
       
 10876 	 * @method Serializer
       
 10877 	 * @param {Object} settings Serializer settings object.
       
 10878 	 * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
       
 10879 	 */
       
 10880 	return function(settings, editor) {
       
 10881 		var dom, schema, htmlParser;
       
 10882 
       
 10883 		if (editor) {
       
 10884 			dom = editor.dom;
       
 10885 			schema = editor.schema;
       
 10886 		}
       
 10887 
       
 10888 		// Default DOM and Schema if they are undefined
       
 10889 		dom = dom || DOM;
       
 10890 		schema = schema || new Schema(settings);
       
 10891 		settings.entity_encoding = settings.entity_encoding || 'named';
       
 10892 		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
       
 10893 
       
 10894 		htmlParser = new DomParser(settings, schema);
       
 10895 
       
 10896 		// Convert tabindex back to elements when serializing contents
       
 10897 		htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) {
       
 10898 			var i = nodes.length, node;
       
 10899 
       
 10900 			while (i--) {
       
 10901 				node = nodes[i];
       
 10902 				node.attr('tabindex', node.attributes.map['data-mce-tabindex']);
       
 10903 				node.attr(name, null);
       
 10904 			}
       
 10905 		});
       
 10906 
       
 10907 		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
       
 10908 		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
       
 10909 			var i = nodes.length, node, value, internalName = 'data-mce-' + name;
       
 10910 			var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
       
 10911 
       
 10912 			while (i--) {
       
 10913 				node = nodes[i];
       
 10914 
       
 10915 				value = node.attributes.map[internalName];
       
 10916 				if (value !== undef) {
       
 10917 					// Set external name to internal value and remove internal
       
 10918 					node.attr(name, value.length > 0 ? value : null);
       
 10919 					node.attr(internalName, null);
       
 10920 				} else {
       
 10921 					// No internal attribute found then convert the value we have in the DOM
       
 10922 					value = node.attributes.map[name];
       
 10923 
       
 10924 					if (name === "style") {
       
 10925 						value = dom.serializeStyle(dom.parseStyle(value), node.name);
       
 10926 					} else if (urlConverter) {
       
 10927 						value = urlConverter.call(urlConverterScope, value, name, node.name);
       
 10928 					}
       
 10929 
       
 10930 					node.attr(name, value.length > 0 ? value : null);
       
 10931 				}
       
 10932 			}
       
 10933 		});
       
 10934 
       
 10935 		// Remove internal classes mceItem<..> or mceSelected
       
 10936 		htmlParser.addAttributeFilter('class', function(nodes) {
       
 10937 			var i = nodes.length, node, value;
       
 10938 
       
 10939 			while (i--) {
       
 10940 				node = nodes[i];
       
 10941 				value = node.attr('class');
       
 10942 
       
 10943 				if (value) {
       
 10944 					value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
       
 10945 					node.attr('class', value.length > 0 ? value : null);
       
 10946 				}
       
 10947 			}
       
 10948 		});
       
 10949 
       
 10950 		// Remove bookmark elements
       
 10951 		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
       
 10952 			var i = nodes.length, node;
       
 10953 
       
 10954 			while (i--) {
       
 10955 				node = nodes[i];
       
 10956 
       
 10957 				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
       
 10958 					node.remove();
       
 10959 				}
       
 10960 			}
       
 10961 		});
       
 10962 
       
 10963 		htmlParser.addNodeFilter('noscript', function(nodes) {
       
 10964 			var i = nodes.length, node;
       
 10965 
       
 10966 			while (i--) {
       
 10967 				node = nodes[i].firstChild;
       
 10968 
       
 10969 				if (node) {
       
 10970 					node.value = Entities.decode(node.value);
       
 10971 				}
       
 10972 			}
       
 10973 		});
       
 10974 
       
 10975 		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
       
 10976 		htmlParser.addNodeFilter('script,style', function(nodes, name) {
       
 10977 			var i = nodes.length, node, value, type;
       
 10978 
       
 10979 			function trim(value) {
       
 10980 				/*jshint maxlen:255 */
       
 10981 				/*eslint max-len:0 */
       
 10982 				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
       
 10983 						.replace(/^[\r\n]*|[\r\n]*$/g, '')
       
 10984 						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
       
 10985 						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
       
 10986 			}
       
 10987 
       
 10988 			while (i--) {
       
 10989 				node = nodes[i];
       
 10990 				value = node.firstChild ? node.firstChild.value : '';
       
 10991 
       
 10992 				if (name === "script") {
       
 10993 					// Remove mce- prefix from script elements and remove default type since the user specified
       
 10994 					// a script element without type attribute
       
 10995 					type = node.attr('type');
       
 10996 					if (type) {
       
 10997 						node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, ''));
       
 10998 					}
       
 10999 
       
 11000 					if (value.length > 0) {
       
 11001 						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
       
 11002 					}
       
 11003 				} else {
       
 11004 					if (value.length > 0) {
       
 11005 						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
       
 11006 					}
       
 11007 				}
       
 11008 			}
       
 11009 		});
       
 11010 
       
 11011 		// Convert comments to cdata and handle protected comments
       
 11012 		htmlParser.addNodeFilter('#comment', function(nodes) {
       
 11013 			var i = nodes.length, node;
       
 11014 
       
 11015 			while (i--) {
       
 11016 				node = nodes[i];
       
 11017 
       
 11018 				if (node.value.indexOf('[CDATA[') === 0) {
       
 11019 					node.name = '#cdata';
       
 11020 					node.type = 4;
       
 11021 					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
       
 11022 				} else if (node.value.indexOf('mce:protected ') === 0) {
       
 11023 					node.name = "#text";
       
 11024 					node.type = 3;
       
 11025 					node.raw = true;
       
 11026 					node.value = unescape(node.value).substr(14);
       
 11027 				}
       
 11028 			}
       
 11029 		});
       
 11030 
       
 11031 		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
       
 11032 			var i = nodes.length, node;
       
 11033 
       
 11034 			while (i--) {
       
 11035 				node = nodes[i];
       
 11036 				if (node.type === 7) {
       
 11037 					node.remove();
       
 11038 				} else if (node.type === 1) {
       
 11039 					if (name === "input" && !("type" in node.attributes.map)) {
       
 11040 						node.attr('type', 'text');
       
 11041 					}
       
 11042 				}
       
 11043 			}
       
 11044 		});
       
 11045 
       
 11046 		// Fix list elements, TODO: Replace this later
       
 11047 		if (settings.fix_list_elements) {
       
 11048 			htmlParser.addNodeFilter('ul,ol', function(nodes) {
       
 11049 				var i = nodes.length, node, parentNode;
       
 11050 
       
 11051 				while (i--) {
       
 11052 					node = nodes[i];
       
 11053 					parentNode = node.parent;
       
 11054 
       
 11055 					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
       
 11056 						if (node.prev && node.prev.name === 'li') {
       
 11057 							node.prev.append(node);
       
 11058 						}
       
 11059 					}
       
 11060 				}
       
 11061 			});
       
 11062 		}
       
 11063 
       
 11064 		// Remove internal data attributes
       
 11065 		htmlParser.addAttributeFilter(
       
 11066 			'data-mce-src,data-mce-href,data-mce-style,' +
       
 11067 			'data-mce-selected,data-mce-expando,' +
       
 11068 			'data-mce-type,data-mce-resize',
       
 11069 
       
 11070 			function(nodes, name) {
       
 11071 				var i = nodes.length;
       
 11072 
       
 11073 				while (i--) {
       
 11074 					nodes[i].attr(name, null);
       
 11075 				}
       
 11076 			}
       
 11077 		);
       
 11078 
       
 11079 		// Return public methods
       
 11080 		return {
       
 11081 			/**
       
 11082 			 * Schema instance that was used to when the Serializer was constructed.
       
 11083 			 *
       
 11084 			 * @field {tinymce.html.Schema} schema
       
 11085 			 */
       
 11086 			schema: schema,
       
 11087 
       
 11088 			/**
       
 11089 			 * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
       
 11090 			 * and then execute the callback ones it has finished parsing the document.
       
 11091 			 *
       
 11092 			 * @example
       
 11093 			 * parser.addNodeFilter('p,h1', function(nodes, name) {
       
 11094 			 *		for (var i = 0; i < nodes.length; i++) {
       
 11095 			 *			console.log(nodes[i].name);
       
 11096 			 *		}
       
 11097 			 * });
       
 11098 			 * @method addNodeFilter
       
 11099 			 * @method {String} name Comma separated list of nodes to collect.
       
 11100 			 * @param {function} callback Callback function to execute once it has collected nodes.
       
 11101 			 */
       
 11102 			addNodeFilter: htmlParser.addNodeFilter,
       
 11103 
       
 11104 			/**
       
 11105 			 * Adds a attribute filter function to the parser used by the serializer, the parser will
       
 11106 			 * collect nodes that has the specified attributes
       
 11107 			 * and then execute the callback ones it has finished parsing the document.
       
 11108 			 *
       
 11109 			 * @example
       
 11110 			 * parser.addAttributeFilter('src,href', function(nodes, name) {
       
 11111 			 *		for (var i = 0; i < nodes.length; i++) {
       
 11112 			 *			console.log(nodes[i].name);
       
 11113 			 *		}
       
 11114 			 * });
       
 11115 			 * @method addAttributeFilter
       
 11116 			 * @method {String} name Comma separated list of nodes to collect.
       
 11117 			 * @param {function} callback Callback function to execute once it has collected nodes.
       
 11118 			 */
       
 11119 			addAttributeFilter: htmlParser.addAttributeFilter,
       
 11120 
       
 11121 			/**
       
 11122 			 * Serializes the specified browser DOM node into a HTML string.
       
 11123 			 *
       
 11124 			 * @method serialize
       
 11125 			 * @param {DOMNode} node DOM node to serialize.
       
 11126 			 * @param {Object} args Arguments option that gets passed to event handlers.
       
 11127 			 */
       
 11128 			serialize: function(node, args) {
       
 11129 				var self = this, impl, doc, oldDoc, htmlSerializer, content;
       
 11130 
       
 11131 				// Explorer won't clone contents of script and style and the
       
 11132 				// selected index of select elements are cleared on a clone operation.
       
 11133 				if (Env.ie && dom.select('script,style,select,map').length > 0) {
       
 11134 					content = node.innerHTML;
       
 11135 					node = node.cloneNode(false);
       
 11136 					dom.setHTML(node, content);
       
 11137 				} else {
       
 11138 					node = node.cloneNode(true);
       
 11139 				}
       
 11140 
       
 11141 				// Nodes needs to be attached to something in WebKit/Opera
       
 11142 				// This fix will make DOM ranges and make Sizzle happy!
       
 11143 				impl = node.ownerDocument.implementation;
       
 11144 				if (impl.createHTMLDocument) {
       
 11145 					// Create an empty HTML document
       
 11146 					doc = impl.createHTMLDocument("");
       
 11147 
       
 11148 					// Add the element or it's children if it's a body element to the new document
       
 11149 					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
       
 11150 						doc.body.appendChild(doc.importNode(node, true));
       
 11151 					});
       
 11152 
       
 11153 					// Grab first child or body element for serialization
       
 11154 					if (node.nodeName != 'BODY') {
       
 11155 						node = doc.body.firstChild;
       
 11156 					} else {
       
 11157 						node = doc.body;
       
 11158 					}
       
 11159 
       
 11160 					// set the new document in DOMUtils so createElement etc works
       
 11161 					oldDoc = dom.doc;
       
 11162 					dom.doc = doc;
       
 11163 				}
       
 11164 
       
 11165 				args = args || {};
       
 11166 				args.format = args.format || 'html';
       
 11167 
       
 11168 				// Don't wrap content if we want selected html
       
 11169 				if (args.selection) {
       
 11170 					args.forced_root_block = '';
       
 11171 				}
       
 11172 
       
 11173 				// Pre process
       
 11174 				if (!args.no_events) {
       
 11175 					args.node = node;
       
 11176 					self.onPreProcess(args);
       
 11177 				}
       
 11178 
       
 11179 				// Setup serializer
       
 11180 				htmlSerializer = new Serializer(settings, schema);
       
 11181 
       
 11182 				// Parse and serialize HTML
       
 11183 				args.content = htmlSerializer.serialize(
       
 11184 					htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
       
 11185 				);
       
 11186 
       
 11187 				// Replace all BOM characters for now until we can find a better solution
       
 11188 				if (!args.cleanup) {
       
 11189 					args.content = args.content.replace(/\uFEFF/g, '');
       
 11190 				}
       
 11191 
       
 11192 				// Post process
       
 11193 				if (!args.no_events) {
       
 11194 					self.onPostProcess(args);
       
 11195 				}
       
 11196 
       
 11197 				// Restore the old document if it was changed
       
 11198 				if (oldDoc) {
       
 11199 					dom.doc = oldDoc;
       
 11200 				}
       
 11201 
       
 11202 				args.node = null;
       
 11203 
       
 11204 				return args.content;
       
 11205 			},
       
 11206 
       
 11207 			/**
       
 11208 			 * Adds valid elements rules to the serializers schema instance this enables you to specify things
       
 11209 			 * like what elements should be outputted and what attributes specific elements might have.
       
 11210 			 * Consult the Wiki for more details on this format.
       
 11211 			 *
       
 11212 			 * @method addRules
       
 11213 			 * @param {String} rules Valid elements rules string to add to schema.
       
 11214 			 */
       
 11215 			addRules: function(rules) {
       
 11216 				schema.addValidElements(rules);
       
 11217 			},
       
 11218 
       
 11219 			/**
       
 11220 			 * Sets the valid elements rules to the serializers schema instance this enables you to specify things
       
 11221 			 * like what elements should be outputted and what attributes specific elements might have.
       
 11222 			 * Consult the Wiki for more details on this format.
       
 11223 			 *
       
 11224 			 * @method setRules
       
 11225 			 * @param {String} rules Valid elements rules string.
       
 11226 			 */
       
 11227 			setRules: function(rules) {
       
 11228 				schema.setValidElements(rules);
       
 11229 			},
       
 11230 
       
 11231 			onPreProcess: function(args) {
       
 11232 				if (editor) {
       
 11233 					editor.fire('PreProcess', args);
       
 11234 				}
       
 11235 			},
       
 11236 
       
 11237 			onPostProcess: function(args) {
       
 11238 				if (editor) {
       
 11239 					editor.fire('PostProcess', args);
       
 11240 				}
       
 11241 			}
       
 11242 		};
       
 11243 	};
       
 11244 });
       
 11245 
       
 11246 // Included from: js/tinymce/classes/dom/TridentSelection.js
       
 11247 
       
 11248 /**
       
 11249  * TridentSelection.js
       
 11250  *
       
 11251  * Copyright, Moxiecode Systems AB
       
 11252  * Released under LGPL License.
       
 11253  *
       
 11254  * License: http://www.tinymce.com/license
       
 11255  * Contributing: http://www.tinymce.com/contributing
       
 11256  */
       
 11257 
       
 11258 /**
       
 11259  * Selection class for old explorer versions. This one fakes the
       
 11260  * native selection object available on modern browsers.
       
 11261  *
       
 11262  * @class tinymce.dom.TridentSelection
       
 11263  */
       
 11264 define("tinymce/dom/TridentSelection", [], function() {
       
 11265 	function Selection(selection) {
       
 11266 		var self = this, dom = selection.dom, FALSE = false;
       
 11267 
       
 11268 		function getPosition(rng, start) {
       
 11269 			var checkRng, startIndex = 0, endIndex, inside,
       
 11270 				children, child, offset, index, position = -1, parent;
       
 11271 
       
 11272 			// Setup test range, collapse it and get the parent
       
 11273 			checkRng = rng.duplicate();
       
 11274 			checkRng.collapse(start);
       
 11275 			parent = checkRng.parentElement();
       
 11276 
       
 11277 			// Check if the selection is within the right document
       
 11278 			if (parent.ownerDocument !== selection.dom.doc) {
       
 11279 				return;
       
 11280 			}
       
 11281 
       
 11282 			// IE will report non editable elements as it's parent so look for an editable one
       
 11283 			while (parent.contentEditable === "false") {
       
 11284 				parent = parent.parentNode;
       
 11285 			}
       
 11286 
       
 11287 			// If parent doesn't have any children then return that we are inside the element
       
 11288 			if (!parent.hasChildNodes()) {
       
 11289 				return {node: parent, inside: 1};
       
 11290 			}
       
 11291 
       
 11292 			// Setup node list and endIndex
       
 11293 			children = parent.children;
       
 11294 			endIndex = children.length - 1;
       
 11295 
       
 11296 			// Perform a binary search for the position
       
 11297 			while (startIndex <= endIndex) {
       
 11298 				index = Math.floor((startIndex + endIndex) / 2);
       
 11299 
       
 11300 				// Move selection to node and compare the ranges
       
 11301 				child = children[index];
       
 11302 				checkRng.moveToElementText(child);
       
 11303 				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
       
 11304 
       
 11305 				// Before/after or an exact match
       
 11306 				if (position > 0) {
       
 11307 					endIndex = index - 1;
       
 11308 				} else if (position < 0) {
       
 11309 					startIndex = index + 1;
       
 11310 				} else {
       
 11311 					return {node: child};
       
 11312 				}
       
 11313 			}
       
 11314 
       
 11315 			// Check if child position is before or we didn't find a position
       
 11316 			if (position < 0) {
       
 11317 				// No element child was found use the parent element and the offset inside that
       
 11318 				if (!child) {
       
 11319 					checkRng.moveToElementText(parent);
       
 11320 					checkRng.collapse(true);
       
 11321 					child = parent;
       
 11322 					inside = true;
       
 11323 				} else {
       
 11324 					checkRng.collapse(false);
       
 11325 				}
       
 11326 
       
 11327 				// Walk character by character in text node until we hit the selected range endpoint,
       
 11328 				// hit the end of document or parent isn't the right one
       
 11329 				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
       
 11330 				offset = 0;
       
 11331 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
       
 11332 					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
       
 11333 						break;
       
 11334 					}
       
 11335 
       
 11336 					offset++;
       
 11337 				}
       
 11338 			} else {
       
 11339 				// Child position is after the selection endpoint
       
 11340 				checkRng.collapse(true);
       
 11341 
       
 11342 				// Walk character by character in text node until we hit the selected range endpoint, hit
       
 11343 				// the end of document or parent isn't the right one
       
 11344 				offset = 0;
       
 11345 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
       
 11346 					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
       
 11347 						break;
       
 11348 					}
       
 11349 
       
 11350 					offset++;
       
 11351 				}
       
 11352 			}
       
 11353 
       
 11354 			return {node: child, position: position, offset: offset, inside: inside};
       
 11355 		}
       
 11356 
       
 11357 		// Returns a W3C DOM compatible range object by using the IE Range API
       
 11358 		function getRange() {
       
 11359 			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
       
 11360 
       
 11361 			// If selection is outside the current document just return an empty range
       
 11362 			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
       
 11363 			if (element.ownerDocument != dom.doc) {
       
 11364 				return domRange;
       
 11365 			}
       
 11366 
       
 11367 			collapsed = selection.isCollapsed();
       
 11368 
       
 11369 			// Handle control selection
       
 11370 			if (ieRange.item) {
       
 11371 				domRange.setStart(element.parentNode, dom.nodeIndex(element));
       
 11372 				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
       
 11373 
       
 11374 				return domRange;
       
 11375 			}
       
 11376 
       
 11377 			function findEndPoint(start) {
       
 11378 				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
       
 11379 
       
 11380 				container = endPoint.node;
       
 11381 				offset = endPoint.offset;
       
 11382 
       
 11383 				if (endPoint.inside && !container.hasChildNodes()) {
       
 11384 					domRange[start ? 'setStart' : 'setEnd'](container, 0);
       
 11385 					return;
       
 11386 				}
       
 11387 
       
 11388 				if (offset === undef) {
       
 11389 					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
       
 11390 					return;
       
 11391 				}
       
 11392 
       
 11393 				if (endPoint.position < 0) {
       
 11394 					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
       
 11395 
       
 11396 					if (!sibling) {
       
 11397 						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
       
 11398 						return;
       
 11399 					}
       
 11400 
       
 11401 					if (!offset) {
       
 11402 						if (sibling.nodeType == 3) {
       
 11403 							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
       
 11404 						} else {
       
 11405 							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
       
 11406 						}
       
 11407 
       
 11408 						return;
       
 11409 					}
       
 11410 
       
 11411 					// Find the text node and offset
       
 11412 					while (sibling) {
       
 11413 						if (sibling.nodeType == 3) {
       
 11414 							nodeValue = sibling.nodeValue;
       
 11415 							textNodeOffset += nodeValue.length;
       
 11416 
       
 11417 							// We are at or passed the position we where looking for
       
 11418 							if (textNodeOffset >= offset) {
       
 11419 								container = sibling;
       
 11420 								textNodeOffset -= offset;
       
 11421 								textNodeOffset = nodeValue.length - textNodeOffset;
       
 11422 								break;
       
 11423 							}
       
 11424 						}
       
 11425 
       
 11426 						sibling = sibling.nextSibling;
       
 11427 					}
       
 11428 				} else {
       
 11429 					// Find the text node and offset
       
 11430 					sibling = container.previousSibling;
       
 11431 
       
 11432 					if (!sibling) {
       
 11433 						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
       
 11434 					}
       
 11435 
       
 11436 					// If there isn't any text to loop then use the first position
       
 11437 					if (!offset) {
       
 11438 						if (container.nodeType == 3) {
       
 11439 							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
       
 11440 						} else {
       
 11441 							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
       
 11442 						}
       
 11443 
       
 11444 						return;
       
 11445 					}
       
 11446 
       
 11447 					while (sibling) {
       
 11448 						if (sibling.nodeType == 3) {
       
 11449 							textNodeOffset += sibling.nodeValue.length;
       
 11450 
       
 11451 							// We are at or passed the position we where looking for
       
 11452 							if (textNodeOffset >= offset) {
       
 11453 								container = sibling;
       
 11454 								textNodeOffset -= offset;
       
 11455 								break;
       
 11456 							}
       
 11457 						}
       
 11458 
       
 11459 						sibling = sibling.previousSibling;
       
 11460 					}
       
 11461 				}
       
 11462 
       
 11463 				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
       
 11464 			}
       
 11465 
       
 11466 			try {
       
 11467 				// Find start point
       
 11468 				findEndPoint(true);
       
 11469 
       
 11470 				// Find end point if needed
       
 11471 				if (!collapsed) {
       
 11472 					findEndPoint();
       
 11473 				}
       
 11474 			} catch (ex) {
       
 11475 				// IE has a nasty bug where text nodes might throw "invalid argument" when you
       
 11476 				// access the nodeValue or other properties of text nodes. This seems to happend when
       
 11477 				// text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
       
 11478 				if (ex.number == -2147024809) {
       
 11479 					// Get the current selection
       
 11480 					bookmark = self.getBookmark(2);
       
 11481 
       
 11482 					// Get start element
       
 11483 					tmpRange = ieRange.duplicate();
       
 11484 					tmpRange.collapse(true);
       
 11485 					element = tmpRange.parentElement();
       
 11486 
       
 11487 					// Get end element
       
 11488 					if (!collapsed) {
       
 11489 						tmpRange = ieRange.duplicate();
       
 11490 						tmpRange.collapse(false);
       
 11491 						element2 = tmpRange.parentElement();
       
 11492 						element2.innerHTML = element2.innerHTML;
       
 11493 					}
       
 11494 
       
 11495 					// Remove the broken elements
       
 11496 					element.innerHTML = element.innerHTML;
       
 11497 
       
 11498 					// Restore the selection
       
 11499 					self.moveToBookmark(bookmark);
       
 11500 
       
 11501 					// Since the range has moved we need to re-get it
       
 11502 					ieRange = selection.getRng();
       
 11503 
       
 11504 					// Find start point
       
 11505 					findEndPoint(true);
       
 11506 
       
 11507 					// Find end point if needed
       
 11508 					if (!collapsed) {
       
 11509 						findEndPoint();
       
 11510 					}
       
 11511 				} else {
       
 11512 					throw ex; // Throw other errors
       
 11513 				}
       
 11514 			}
       
 11515 
       
 11516 			return domRange;
       
 11517 		}
       
 11518 
       
 11519 		this.getBookmark = function(type) {
       
 11520 			var rng = selection.getRng(), bookmark = {};
       
 11521 
       
 11522 			function getIndexes(node) {
       
 11523 				var parent, root, children, i, indexes = [];
       
 11524 
       
 11525 				parent = node.parentNode;
       
 11526 				root = dom.getRoot().parentNode;
       
 11527 
       
 11528 				while (parent != root && parent.nodeType !== 9) {
       
 11529 					children = parent.children;
       
 11530 
       
 11531 					i = children.length;
       
 11532 					while (i--) {
       
 11533 						if (node === children[i]) {
       
 11534 							indexes.push(i);
       
 11535 							break;
       
 11536 						}
       
 11537 					}
       
 11538 
       
 11539 					node = parent;
       
 11540 					parent = parent.parentNode;
       
 11541 				}
       
 11542 
       
 11543 				return indexes;
       
 11544 			}
       
 11545 
       
 11546 			function getBookmarkEndPoint(start) {
       
 11547 				var position;
       
 11548 
       
 11549 				position = getPosition(rng, start);
       
 11550 				if (position) {
       
 11551 					return {
       
 11552 						position: position.position,
       
 11553 						offset: position.offset,
       
 11554 						indexes: getIndexes(position.node),
       
 11555 						inside: position.inside
       
 11556 					};
       
 11557 				}
       
 11558 			}
       
 11559 
       
 11560 			// Non ubstructive bookmark
       
 11561 			if (type === 2) {
       
 11562 				// Handle text selection
       
 11563 				if (!rng.item) {
       
 11564 					bookmark.start = getBookmarkEndPoint(true);
       
 11565 
       
 11566 					if (!selection.isCollapsed()) {
       
 11567 						bookmark.end = getBookmarkEndPoint();
       
 11568 					}
       
 11569 				} else {
       
 11570 					bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
       
 11571 				}
       
 11572 			}
       
 11573 
       
 11574 			return bookmark;
       
 11575 		};
       
 11576 
       
 11577 		this.moveToBookmark = function(bookmark) {
       
 11578 			var rng, body = dom.doc.body;
       
 11579 
       
 11580 			function resolveIndexes(indexes) {
       
 11581 				var node, i, idx, children;
       
 11582 
       
 11583 				node = dom.getRoot();
       
 11584 				for (i = indexes.length - 1; i >= 0; i--) {
       
 11585 					children = node.children;
       
 11586 					idx = indexes[i];
       
 11587 
       
 11588 					if (idx <= children.length - 1) {
       
 11589 						node = children[idx];
       
 11590 					}
       
 11591 				}
       
 11592 
       
 11593 				return node;
       
 11594 			}
       
 11595 
       
 11596 			function setBookmarkEndPoint(start) {
       
 11597 				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
       
 11598 
       
 11599 				if (endPoint) {
       
 11600 					moveLeft = endPoint.position > 0;
       
 11601 
       
 11602 					moveRng = body.createTextRange();
       
 11603 					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
       
 11604 
       
 11605 					offset = endPoint.offset;
       
 11606 					if (offset !== undef) {
       
 11607 						moveRng.collapse(endPoint.inside || moveLeft);
       
 11608 						moveRng.moveStart('character', moveLeft ? -offset : offset);
       
 11609 					} else {
       
 11610 						moveRng.collapse(start);
       
 11611 					}
       
 11612 
       
 11613 					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
       
 11614 
       
 11615 					if (start) {
       
 11616 						rng.collapse(true);
       
 11617 					}
       
 11618 				}
       
 11619 			}
       
 11620 
       
 11621 			if (bookmark.start) {
       
 11622 				if (bookmark.start.ctrl) {
       
 11623 					rng = body.createControlRange();
       
 11624 					rng.addElement(resolveIndexes(bookmark.start.indexes));
       
 11625 					rng.select();
       
 11626 				} else {
       
 11627 					rng = body.createTextRange();
       
 11628 					setBookmarkEndPoint(true);
       
 11629 					setBookmarkEndPoint();
       
 11630 					rng.select();
       
 11631 				}
       
 11632 			}
       
 11633 		};
       
 11634 
       
 11635 		this.addRange = function(rng) {
       
 11636 			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
       
 11637 				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
       
 11638 
       
 11639 			function setEndPoint(start) {
       
 11640 				var container, offset, marker, tmpRng, nodes;
       
 11641 
       
 11642 				marker = dom.create('a');
       
 11643 				container = start ? startContainer : endContainer;
       
 11644 				offset = start ? startOffset : endOffset;
       
 11645 				tmpRng = ieRng.duplicate();
       
 11646 
       
 11647 				if (container == doc || container == doc.documentElement) {
       
 11648 					container = body;
       
 11649 					offset = 0;
       
 11650 				}
       
 11651 
       
 11652 				if (container.nodeType == 3) {
       
 11653 					container.parentNode.insertBefore(marker, container);
       
 11654 					tmpRng.moveToElementText(marker);
       
 11655 					tmpRng.moveStart('character', offset);
       
 11656 					dom.remove(marker);
       
 11657 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
       
 11658 				} else {
       
 11659 					nodes = container.childNodes;
       
 11660 
       
 11661 					if (nodes.length) {
       
 11662 						if (offset >= nodes.length) {
       
 11663 							dom.insertAfter(marker, nodes[nodes.length - 1]);
       
 11664 						} else {
       
 11665 							container.insertBefore(marker, nodes[offset]);
       
 11666 						}
       
 11667 
       
 11668 						tmpRng.moveToElementText(marker);
       
 11669 					} else if (container.canHaveHTML) {
       
 11670 						// Empty node selection for example <div>|</div>
       
 11671 						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
       
 11672 						container.innerHTML = '<span>&#xFEFF;</span>';
       
 11673 						marker = container.firstChild;
       
 11674 						tmpRng.moveToElementText(marker);
       
 11675 						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
       
 11676 					}
       
 11677 
       
 11678 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
       
 11679 					dom.remove(marker);
       
 11680 				}
       
 11681 			}
       
 11682 
       
 11683 			// Setup some shorter versions
       
 11684 			startContainer = rng.startContainer;
       
 11685 			startOffset = rng.startOffset;
       
 11686 			endContainer = rng.endContainer;
       
 11687 			endOffset = rng.endOffset;
       
 11688 			ieRng = body.createTextRange();
       
 11689 
       
 11690 			// If single element selection then try making a control selection out of it
       
 11691 			if (startContainer == endContainer && startContainer.nodeType == 1) {
       
 11692 				// Trick to place the caret inside an empty block element like <p></p>
       
 11693 				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
       
 11694 					if (startContainer.canHaveHTML) {
       
 11695 						// Check if previous sibling is an empty block if it is then we need to render it
       
 11696 						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
       
 11697 						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
       
 11698 						sibling = startContainer.previousSibling;
       
 11699 						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
       
 11700 							sibling.innerHTML = '&#xFEFF;';
       
 11701 						} else {
       
 11702 							sibling = null;
       
 11703 						}
       
 11704 
       
 11705 						startContainer.innerHTML = '<span>&#xFEFF;</span><span>&#xFEFF;</span>';
       
 11706 						ieRng.moveToElementText(startContainer.lastChild);
       
 11707 						ieRng.select();
       
 11708 						dom.doc.selection.clear();
       
 11709 						startContainer.innerHTML = '';
       
 11710 
       
 11711 						if (sibling) {
       
 11712 							sibling.innerHTML = '';
       
 11713 						}
       
 11714 						return;
       
 11715 					} else {
       
 11716 						startOffset = dom.nodeIndex(startContainer);
       
 11717 						startContainer = startContainer.parentNode;
       
 11718 					}
       
 11719 				}
       
 11720 
       
 11721 				if (startOffset == endOffset - 1) {
       
 11722 					try {
       
 11723 						ctrlElm = startContainer.childNodes[startOffset];
       
 11724 						ctrlRng = body.createControlRange();
       
 11725 						ctrlRng.addElement(ctrlElm);
       
 11726 						ctrlRng.select();
       
 11727 
       
 11728 						// Check if the range produced is on the correct element and is a control range
       
 11729 						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
       
 11730 						nativeRng = selection.getRng();
       
 11731 						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
       
 11732 							return;
       
 11733 						}
       
 11734 					} catch (ex) {
       
 11735 						// Ignore
       
 11736 					}
       
 11737 				}
       
 11738 			}
       
 11739 
       
 11740 			// Set start/end point of selection
       
 11741 			setEndPoint(true);
       
 11742 			setEndPoint();
       
 11743 
       
 11744 			// Select the new range and scroll it into view
       
 11745 			ieRng.select();
       
 11746 		};
       
 11747 
       
 11748 		// Expose range method
       
 11749 		this.getRangeAt = getRange;
       
 11750 	}
       
 11751 
       
 11752 	return Selection;
       
 11753 });
       
 11754 
       
 11755 // Included from: js/tinymce/classes/util/VK.js
       
 11756 
       
 11757 /**
       
 11758  * VK.js
       
 11759  *
       
 11760  * Copyright, Moxiecode Systems AB
       
 11761  * Released under LGPL License.
       
 11762  *
       
 11763  * License: http://www.tinymce.com/license
       
 11764  * Contributing: http://www.tinymce.com/contributing
       
 11765  */
       
 11766 
       
 11767 /**
       
 11768  * This file exposes a set of the common KeyCodes for use.  Please grow it as needed.
       
 11769  */
       
 11770 define("tinymce/util/VK", [
       
 11771 	"tinymce/Env"
       
 11772 ], function(Env) {
       
 11773 	return {
       
 11774 		BACKSPACE: 8,
       
 11775 		DELETE: 46,
       
 11776 		DOWN: 40,
       
 11777 		ENTER: 13,
       
 11778 		LEFT: 37,
       
 11779 		RIGHT: 39,
       
 11780 		SPACEBAR: 32,
       
 11781 		TAB: 9,
       
 11782 		UP: 38,
       
 11783 
       
 11784 		modifierPressed: function(e) {
       
 11785 			return e.shiftKey || e.ctrlKey || e.altKey || this.metaKeyPressed(e);
       
 11786 		},
       
 11787 
       
 11788 		metaKeyPressed: function(e) {
       
 11789 			// Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states
       
 11790 			return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey);
       
 11791 		}
       
 11792 	};
       
 11793 });
       
 11794 
       
 11795 // Included from: js/tinymce/classes/dom/ControlSelection.js
       
 11796 
       
 11797 /**
       
 11798  * ControlSelection.js
       
 11799  *
       
 11800  * Copyright, Moxiecode Systems AB
       
 11801  * Released under LGPL License.
       
 11802  *
       
 11803  * License: http://www.tinymce.com/license
       
 11804  * Contributing: http://www.tinymce.com/contributing
       
 11805  */
       
 11806 
       
 11807 /**
       
 11808  * This class handles control selection of elements. Controls are elements
       
 11809  * that can be resized and needs to be selected as a whole. It adds custom resize handles
       
 11810  * to all browser engines that support properly disabling the built in resize logic.
       
 11811  *
       
 11812  * @class tinymce.dom.ControlSelection
       
 11813  */
       
 11814 define("tinymce/dom/ControlSelection", [
       
 11815 	"tinymce/util/VK",
       
 11816 	"tinymce/util/Tools",
       
 11817 	"tinymce/Env"
       
 11818 ], function(VK, Tools, Env) {
       
 11819 	return function(selection, editor) {
       
 11820 		var dom = editor.dom, each = Tools.each;
       
 11821 		var selectedElm, selectedElmGhost, resizeHelper, resizeHandles, selectedHandle, lastMouseDownEvent;
       
 11822 		var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
       
 11823 		var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
       
 11824 		var abs = Math.abs, round = Math.round, rootElement = editor.getBody(), startScrollWidth, startScrollHeight;
       
 11825 
       
 11826 		// Details about each resize handle how to scale etc
       
 11827 		resizeHandles = {
       
 11828 			// Name: x multiplier, y multiplier, delta size x, delta size y
       
 11829 			n: [0.5, 0, 0, -1],
       
 11830 			e: [1, 0.5, 1, 0],
       
 11831 			s: [0.5, 1, 0, 1],
       
 11832 			w: [0, 0.5, -1, 0],
       
 11833 			nw: [0, 0, -1, -1],
       
 11834 			ne: [1, 0, 1, -1],
       
 11835 			se: [1, 1, 1, 1],
       
 11836 			sw: [0, 1, -1, 1]
       
 11837 		};
       
 11838 
       
 11839 		// Add CSS for resize handles, cloned element and selected
       
 11840 		var rootClass = '.mce-content-body';
       
 11841 		editor.contentStyles.push(
       
 11842 			rootClass + ' div.mce-resizehandle {' +
       
 11843 				'position: absolute;' +
       
 11844 				'border: 1px solid black;' +
       
 11845 				'background: #FFF;' +
       
 11846 				'width: 5px;' +
       
 11847 				'height: 5px;' +
       
 11848 				'z-index: 10000' +
       
 11849 			'}' +
       
 11850 			rootClass + ' .mce-resizehandle:hover {' +
       
 11851 				'background: #000' +
       
 11852 			'}' +
       
 11853 			rootClass + ' img[data-mce-selected], hr[data-mce-selected] {' +
       
 11854 				'outline: 1px solid black;' +
       
 11855 				'resize: none' + // Have been talks about implementing this in browsers
       
 11856 			'}' +
       
 11857 			rootClass + ' .mce-clonedresizable {' +
       
 11858 				'position: absolute;' +
       
 11859 				(Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
       
 11860 				'opacity: .5;' +
       
 11861 				'filter: alpha(opacity=50);' +
       
 11862 				'z-index: 10000' +
       
 11863 			'}' +
       
 11864 			rootClass + ' .mce-resize-helper {' +
       
 11865 				'background: #555;' +
       
 11866 				'background: rgba(0,0,0,0.75);' +
       
 11867 				'border-radius: 3px;' +
       
 11868 				'border: 1px;' +
       
 11869 				'color: white;' +
       
 11870 				'display: none;' +
       
 11871 				'font-family: sans-serif;' +
       
 11872 				'font-size: 12px;' +
       
 11873 				'white-space: nowrap;' +
       
 11874 				'line-height: 14px;' +
       
 11875 				'margin: 5px 10px;' +
       
 11876 				'padding: 5px;' +
       
 11877 				'position: absolute;' +
       
 11878 				'z-index: 10001' +
       
 11879 			'}'
       
 11880 		);
       
 11881 
       
 11882 		function isResizable(elm) {
       
 11883 			var selector = editor.settings.object_resizing;
       
 11884 
       
 11885 			if (selector === false || Env.iOS) {
       
 11886 				return false;
       
 11887 			}
       
 11888 
       
 11889 			if (typeof selector != 'string') {
       
 11890 				selector = 'table,img,div';
       
 11891 			}
       
 11892 
       
 11893 			if (elm.getAttribute('data-mce-resize') === 'false') {
       
 11894 				return false;
       
 11895 			}
       
 11896 
       
 11897 			return editor.dom.is(elm, selector);
       
 11898 		}
       
 11899 
       
 11900 		function resizeGhostElement(e) {
       
 11901 			var deltaX, deltaY, proportional;
       
 11902 			var resizeHelperX, resizeHelperY;
       
 11903 
       
 11904 			// Calc new width/height
       
 11905 			deltaX = e.screenX - startX;
       
 11906 			deltaY = e.screenY - startY;
       
 11907 
       
 11908 			// Calc new size
       
 11909 			width = deltaX * selectedHandle[2] + startW;
       
 11910 			height = deltaY * selectedHandle[3] + startH;
       
 11911 
       
 11912 			// Never scale down lower than 5 pixels
       
 11913 			width = width < 5 ? 5 : width;
       
 11914 			height = height < 5 ? 5 : height;
       
 11915 
       
 11916 			if (selectedElm.nodeName == "IMG" && editor.settings.resize_img_proportional !== false) {
       
 11917 				proportional = !VK.modifierPressed(e);
       
 11918 			} else {
       
 11919 				proportional = VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0);
       
 11920 			}
       
 11921 
       
 11922 			// Constrain proportions
       
 11923 			if (proportional) {
       
 11924 				if (abs(deltaX) > abs(deltaY)) {
       
 11925 					height = round(width * ratio);
       
 11926 					width = round(height / ratio);
       
 11927 				} else {
       
 11928 					width = round(height / ratio);
       
 11929 					height = round(width * ratio);
       
 11930 				}
       
 11931 			}
       
 11932 
       
 11933 			// Update ghost size
       
 11934 			dom.setStyles(selectedElmGhost, {
       
 11935 				width: width,
       
 11936 				height: height
       
 11937 			});
       
 11938 
       
 11939 			// Update resize helper position
       
 11940 			resizeHelperX = selectedHandle.startPos.x + deltaX;
       
 11941 			resizeHelperY = selectedHandle.startPos.y + deltaY;
       
 11942 			resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0;
       
 11943 			resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0;
       
 11944 
       
 11945 			dom.setStyles(resizeHelper, {
       
 11946 				left: resizeHelperX,
       
 11947 				top: resizeHelperY,
       
 11948 				display: 'block'
       
 11949 			});
       
 11950 
       
 11951 			resizeHelper.innerHTML = width + ' &times; ' + height;
       
 11952 
       
 11953 			// Update ghost X position if needed
       
 11954 			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
       
 11955 				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
       
 11956 			}
       
 11957 
       
 11958 			// Update ghost Y position if needed
       
 11959 			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
       
 11960 				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
       
 11961 			}
       
 11962 
       
 11963 			// Calculate how must overflow we got
       
 11964 			deltaX = rootElement.scrollWidth - startScrollWidth;
       
 11965 			deltaY = rootElement.scrollHeight - startScrollHeight;
       
 11966 
       
 11967 			// Re-position the resize helper based on the overflow
       
 11968 			if (deltaX + deltaY !== 0) {
       
 11969 				dom.setStyles(resizeHelper, {
       
 11970 					left: resizeHelperX - deltaX,
       
 11971 					top: resizeHelperY - deltaY
       
 11972 				});
       
 11973 			}
       
 11974 
       
 11975 			if (!resizeStarted) {
       
 11976 				editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
       
 11977 				resizeStarted = true;
       
 11978 			}
       
 11979 		}
       
 11980 
       
 11981 		function endGhostResize() {
       
 11982 			resizeStarted = false;
       
 11983 
       
 11984 			function setSizeProp(name, value) {
       
 11985 				if (value) {
       
 11986 					// Resize by using style or attribute
       
 11987 					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
       
 11988 						dom.setStyle(selectedElm, name, value);
       
 11989 					} else {
       
 11990 						dom.setAttrib(selectedElm, name, value);
       
 11991 					}
       
 11992 				}
       
 11993 			}
       
 11994 
       
 11995 			// Set width/height properties
       
 11996 			setSizeProp('width', width);
       
 11997 			setSizeProp('height', height);
       
 11998 
       
 11999 			dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
       
 12000 			dom.unbind(editableDoc, 'mouseup', endGhostResize);
       
 12001 
       
 12002 			if (rootDocument != editableDoc) {
       
 12003 				dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
       
 12004 				dom.unbind(rootDocument, 'mouseup', endGhostResize);
       
 12005 			}
       
 12006 
       
 12007 			// Remove ghost/helper and update resize handle positions
       
 12008 			dom.remove(selectedElmGhost);
       
 12009 			dom.remove(resizeHelper);
       
 12010 
       
 12011 			if (!isIE || selectedElm.nodeName == "TABLE") {
       
 12012 				showResizeRect(selectedElm);
       
 12013 			}
       
 12014 
       
 12015 			editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
       
 12016 			dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));
       
 12017 			editor.nodeChanged();
       
 12018 		}
       
 12019 
       
 12020 		function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
       
 12021 			var position, targetWidth, targetHeight, e, rect;
       
 12022 
       
 12023 			unbindResizeHandleEvents();
       
 12024 
       
 12025 			// Get position and size of target
       
 12026 			position = dom.getPos(targetElm, rootElement);
       
 12027 			selectedElmX = position.x;
       
 12028 			selectedElmY = position.y;
       
 12029 			rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
       
 12030 			targetWidth = rect.width || (rect.right - rect.left);
       
 12031 			targetHeight = rect.height || (rect.bottom - rect.top);
       
 12032 
       
 12033 			// Reset width/height if user selects a new image/table
       
 12034 			if (selectedElm != targetElm) {
       
 12035 				detachResizeStartListener();
       
 12036 				selectedElm = targetElm;
       
 12037 				width = height = 0;
       
 12038 			}
       
 12039 
       
 12040 			// Makes it possible to disable resizing
       
 12041 			e = editor.fire('ObjectSelected', {target: targetElm});
       
 12042 
       
 12043 			if (isResizable(targetElm) && !e.isDefaultPrevented()) {
       
 12044 				each(resizeHandles, function(handle, name) {
       
 12045 					var handleElm, handlerContainerElm;
       
 12046 
       
 12047 					function startDrag(e) {
       
 12048 						startX = e.screenX;
       
 12049 						startY = e.screenY;
       
 12050 						startW = selectedElm.clientWidth;
       
 12051 						startH = selectedElm.clientHeight;
       
 12052 						ratio = startH / startW;
       
 12053 						selectedHandle = handle;
       
 12054 
       
 12055 						handle.startPos = {
       
 12056 							x: targetWidth * handle[0] + selectedElmX,
       
 12057 							y: targetHeight * handle[1] + selectedElmY
       
 12058 						};
       
 12059 
       
 12060 						startScrollWidth = rootElement.scrollWidth;
       
 12061 						startScrollHeight = rootElement.scrollHeight;
       
 12062 
       
 12063 						selectedElmGhost = selectedElm.cloneNode(true);
       
 12064 						dom.addClass(selectedElmGhost, 'mce-clonedresizable');
       
 12065 						dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all');
       
 12066 						selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
       
 12067 						selectedElmGhost.unSelectabe = true;
       
 12068 						dom.setStyles(selectedElmGhost, {
       
 12069 							left: selectedElmX,
       
 12070 							top: selectedElmY,
       
 12071 							margin: 0
       
 12072 						});
       
 12073 
       
 12074 						selectedElmGhost.removeAttribute('data-mce-selected');
       
 12075 						rootElement.appendChild(selectedElmGhost);
       
 12076 
       
 12077 						dom.bind(editableDoc, 'mousemove', resizeGhostElement);
       
 12078 						dom.bind(editableDoc, 'mouseup', endGhostResize);
       
 12079 
       
 12080 						if (rootDocument != editableDoc) {
       
 12081 							dom.bind(rootDocument, 'mousemove', resizeGhostElement);
       
 12082 							dom.bind(rootDocument, 'mouseup', endGhostResize);
       
 12083 						}
       
 12084 
       
 12085 						resizeHelper = dom.add(rootElement, 'div', {
       
 12086 							'class': 'mce-resize-helper',
       
 12087 							'data-mce-bogus': 'all'
       
 12088 						}, startW + ' &times; ' + startH);
       
 12089 					}
       
 12090 
       
 12091 					if (mouseDownHandleName) {
       
 12092 						// Drag started by IE native resizestart
       
 12093 						if (name == mouseDownHandleName) {
       
 12094 							startDrag(mouseDownEvent);
       
 12095 						}
       
 12096 
       
 12097 						return;
       
 12098 					}
       
 12099 
       
 12100 					// Get existing or render resize handle
       
 12101 					handleElm = dom.get('mceResizeHandle' + name);
       
 12102 					if (!handleElm) {
       
 12103 						handlerContainerElm = rootElement;
       
 12104 
       
 12105 						handleElm = dom.add(handlerContainerElm, 'div', {
       
 12106 							id: 'mceResizeHandle' + name,
       
 12107 							'data-mce-bogus': 'all',
       
 12108 							'class': 'mce-resizehandle',
       
 12109 							unselectable: true,
       
 12110 							style: 'cursor:' + name + '-resize; margin:0; padding:0'
       
 12111 						});
       
 12112 
       
 12113 						// Hides IE move layer cursor
       
 12114 						// If we set it on Chrome we get this wounderful bug: #6725
       
 12115 						if (Env.ie) {
       
 12116 							handleElm.contentEditable = false;
       
 12117 						}
       
 12118 					} else {
       
 12119 						dom.show(handleElm);
       
 12120 					}
       
 12121 
       
 12122 					if (!handle.elm) {
       
 12123 						dom.bind(handleElm, 'mousedown', function(e) {
       
 12124 							e.stopImmediatePropagation();
       
 12125 							e.preventDefault();
       
 12126 							startDrag(e);
       
 12127 						});
       
 12128 
       
 12129 						handle.elm = handleElm;
       
 12130 					}
       
 12131 
       
 12132 					// Position element
       
 12133 					dom.setStyles(handleElm, {
       
 12134 						left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
       
 12135 						top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
       
 12136 					});
       
 12137 				});
       
 12138 			} else {
       
 12139 				hideResizeRect();
       
 12140 			}
       
 12141 
       
 12142 			selectedElm.setAttribute('data-mce-selected', '1');
       
 12143 		}
       
 12144 
       
 12145 		function hideResizeRect() {
       
 12146 			var name, handleElm;
       
 12147 
       
 12148 			unbindResizeHandleEvents();
       
 12149 
       
 12150 			if (selectedElm) {
       
 12151 				selectedElm.removeAttribute('data-mce-selected');
       
 12152 			}
       
 12153 
       
 12154 			for (name in resizeHandles) {
       
 12155 				handleElm = dom.get('mceResizeHandle' + name);
       
 12156 				if (handleElm) {
       
 12157 					dom.unbind(handleElm);
       
 12158 					dom.remove(handleElm);
       
 12159 				}
       
 12160 			}
       
 12161 		}
       
 12162 
       
 12163 		function updateResizeRect(e) {
       
 12164 			var startElm, controlElm;
       
 12165 
       
 12166 			function isChildOrEqual(node, parent) {
       
 12167 				if (node) {
       
 12168 					do {
       
 12169 						if (node === parent) {
       
 12170 							return true;
       
 12171 						}
       
 12172 					} while ((node = node.parentNode));
       
 12173 				}
       
 12174 			}
       
 12175 
       
 12176 			// Ignore all events while resizing
       
 12177 			if (resizeStarted) {
       
 12178 				return;
       
 12179 			}
       
 12180 
       
 12181 			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
       
 12182 			each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
       
 12183 				img.removeAttribute('data-mce-selected');
       
 12184 			});
       
 12185 
       
 12186 			controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
       
 12187 			controlElm = dom.$(controlElm).closest(isIE ? 'table' : 'table,img,hr')[0];
       
 12188 
       
 12189 			if (isChildOrEqual(controlElm, rootElement)) {
       
 12190 				disableGeckoResize();
       
 12191 				startElm = selection.getStart(true);
       
 12192 
       
 12193 				if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
       
 12194 					if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) {
       
 12195 						showResizeRect(controlElm);
       
 12196 						return;
       
 12197 					}
       
 12198 				}
       
 12199 			}
       
 12200 
       
 12201 			hideResizeRect();
       
 12202 		}
       
 12203 
       
 12204 		function attachEvent(elm, name, func) {
       
 12205 			if (elm && elm.attachEvent) {
       
 12206 				elm.attachEvent('on' + name, func);
       
 12207 			}
       
 12208 		}
       
 12209 
       
 12210 		function detachEvent(elm, name, func) {
       
 12211 			if (elm && elm.detachEvent) {
       
 12212 				elm.detachEvent('on' + name, func);
       
 12213 			}
       
 12214 		}
       
 12215 
       
 12216 		function resizeNativeStart(e) {
       
 12217 			var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
       
 12218 
       
 12219 			pos = target.getBoundingClientRect();
       
 12220 			relativeX = lastMouseDownEvent.clientX - pos.left;
       
 12221 			relativeY = lastMouseDownEvent.clientY - pos.top;
       
 12222 
       
 12223 			// Figure out what corner we are draging on
       
 12224 			for (name in resizeHandles) {
       
 12225 				corner = resizeHandles[name];
       
 12226 
       
 12227 				cornerX = target.offsetWidth * corner[0];
       
 12228 				cornerY = target.offsetHeight * corner[1];
       
 12229 
       
 12230 				if (abs(cornerX - relativeX) < 8 && abs(cornerY - relativeY) < 8) {
       
 12231 					selectedHandle = corner;
       
 12232 					break;
       
 12233 				}
       
 12234 			}
       
 12235 
       
 12236 			// Remove native selection and let the magic begin
       
 12237 			resizeStarted = true;
       
 12238 			editor.fire('ObjectResizeStart', {
       
 12239 				target: selectedElm,
       
 12240 				width: selectedElm.clientWidth,
       
 12241 				height: selectedElm.clientHeight
       
 12242 			});
       
 12243 			editor.getDoc().selection.empty();
       
 12244 			showResizeRect(target, name, lastMouseDownEvent);
       
 12245 		}
       
 12246 
       
 12247 		function nativeControlSelect(e) {
       
 12248 			var target = e.srcElement;
       
 12249 
       
 12250 			if (target != selectedElm) {
       
 12251 				editor.fire('ObjectSelected', {target: target});
       
 12252 				detachResizeStartListener();
       
 12253 
       
 12254 				if (target.id.indexOf('mceResizeHandle') === 0) {
       
 12255 					e.returnValue = false;
       
 12256 					return;
       
 12257 				}
       
 12258 
       
 12259 				if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
       
 12260 					hideResizeRect();
       
 12261 					selectedElm = target;
       
 12262 					attachEvent(target, 'resizestart', resizeNativeStart);
       
 12263 				}
       
 12264 			}
       
 12265 		}
       
 12266 
       
 12267 		function detachResizeStartListener() {
       
 12268 			detachEvent(selectedElm, 'resizestart', resizeNativeStart);
       
 12269 		}
       
 12270 
       
 12271 		function unbindResizeHandleEvents() {
       
 12272 			for (var name in resizeHandles) {
       
 12273 				var handle = resizeHandles[name];
       
 12274 
       
 12275 				if (handle.elm) {
       
 12276 					dom.unbind(handle.elm);
       
 12277 					delete handle.elm;
       
 12278 				}
       
 12279 			}
       
 12280 		}
       
 12281 
       
 12282 		function disableGeckoResize() {
       
 12283 			try {
       
 12284 				// Disable object resizing on Gecko
       
 12285 				editor.getDoc().execCommand('enableObjectResizing', false, false);
       
 12286 			} catch (ex) {
       
 12287 				// Ignore
       
 12288 			}
       
 12289 		}
       
 12290 
       
 12291 		function controlSelect(elm) {
       
 12292 			var ctrlRng;
       
 12293 
       
 12294 			if (!isIE) {
       
 12295 				return;
       
 12296 			}
       
 12297 
       
 12298 			ctrlRng = editableDoc.body.createControlRange();
       
 12299 
       
 12300 			try {
       
 12301 				ctrlRng.addElement(elm);
       
 12302 				ctrlRng.select();
       
 12303 				return true;
       
 12304 			} catch (ex) {
       
 12305 				// Ignore since the element can't be control selected for example a P tag
       
 12306 			}
       
 12307 		}
       
 12308 
       
 12309 		editor.on('init', function() {
       
 12310 			if (isIE) {
       
 12311 				// Hide the resize rect on resize and reselect the image
       
 12312 				editor.on('ObjectResized', function(e) {
       
 12313 					if (e.target.nodeName != 'TABLE') {
       
 12314 						hideResizeRect();
       
 12315 						controlSelect(e.target);
       
 12316 					}
       
 12317 				});
       
 12318 
       
 12319 				attachEvent(rootElement, 'controlselect', nativeControlSelect);
       
 12320 
       
 12321 				editor.on('mousedown', function(e) {
       
 12322 					lastMouseDownEvent = e;
       
 12323 				});
       
 12324 			} else {
       
 12325 				disableGeckoResize();
       
 12326 
       
 12327 				if (Env.ie >= 11) {
       
 12328 					// TODO: Drag/drop doesn't work
       
 12329 					editor.on('mouseup', function(e) {
       
 12330 						var nodeName = e.target.nodeName;
       
 12331 
       
 12332 						if (!resizeStarted && /^(TABLE|IMG|HR)$/.test(nodeName)) {
       
 12333 							editor.selection.select(e.target, nodeName == 'TABLE');
       
 12334 							editor.nodeChanged();
       
 12335 						}
       
 12336 					});
       
 12337 
       
 12338 					editor.dom.bind(rootElement, 'mscontrolselect', function(e) {
       
 12339 						if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
       
 12340 							e.preventDefault();
       
 12341 
       
 12342 							// This moves the selection from being a control selection to a text like selection like in WebKit #6753
       
 12343 							// TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections.
       
 12344 							if (e.target.tagName == 'IMG') {
       
 12345 								window.setTimeout(function() {
       
 12346 									editor.selection.select(e.target);
       
 12347 								}, 0);
       
 12348 							}
       
 12349 						}
       
 12350 					});
       
 12351 				}
       
 12352 			}
       
 12353 
       
 12354 			editor.on('nodechange ResizeEditor', updateResizeRect);
       
 12355 
       
 12356 			// Update resize rect while typing in a table
       
 12357 			editor.on('keydown keyup', function(e) {
       
 12358 				if (selectedElm && selectedElm.nodeName == "TABLE") {
       
 12359 					updateResizeRect(e);
       
 12360 				}
       
 12361 			});
       
 12362 
       
 12363 			editor.on('hide', hideResizeRect);
       
 12364 
       
 12365 			// Hide rect on focusout since it would float on top of windows otherwise
       
 12366 			//editor.on('focusout', hideResizeRect);
       
 12367 		});
       
 12368 
       
 12369 		editor.on('remove', unbindResizeHandleEvents);
       
 12370 
       
 12371 		function destroy() {
       
 12372 			selectedElm = selectedElmGhost = null;
       
 12373 
       
 12374 			if (isIE) {
       
 12375 				detachResizeStartListener();
       
 12376 				detachEvent(rootElement, 'controlselect', nativeControlSelect);
       
 12377 			}
       
 12378 		}
       
 12379 
       
 12380 		return {
       
 12381 			isResizable: isResizable,
       
 12382 			showResizeRect: showResizeRect,
       
 12383 			hideResizeRect: hideResizeRect,
       
 12384 			updateResizeRect: updateResizeRect,
       
 12385 			controlSelect: controlSelect,
       
 12386 			destroy: destroy
       
 12387 		};
       
 12388 	};
       
 12389 });
       
 12390 
       
 12391 // Included from: js/tinymce/classes/dom/BookmarkManager.js
       
 12392 
       
 12393 /**
       
 12394  * BookmarkManager.js
       
 12395  *
       
 12396  * Copyright, Moxiecode Systems AB
       
 12397  * Released under LGPL License.
       
 12398  *
       
 12399  * License: http://www.tinymce.com/license
       
 12400  * Contributing: http://www.tinymce.com/contributing
       
 12401  */
       
 12402 
       
 12403 /**
       
 12404  * This class handles selection bookmarks.
       
 12405  *
       
 12406  * @class tinymce.dom.BookmarkManager
       
 12407  */
       
 12408 define("tinymce/dom/BookmarkManager", [
       
 12409 	"tinymce/Env",
       
 12410 	"tinymce/util/Tools"
       
 12411 ], function(Env, Tools) {
       
 12412 	/**
       
 12413 	 * Constructs a new BookmarkManager instance for a specific selection instance.
       
 12414 	 *
       
 12415 	 * @constructor
       
 12416 	 * @method BookmarkManager
       
 12417 	 * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for.
       
 12418 	 */
       
 12419 	function BookmarkManager(selection) {
       
 12420 		var dom = selection.dom;
       
 12421 
       
 12422 		/**
       
 12423 		 * Returns a bookmark location for the current selection. This bookmark object
       
 12424 		 * can then be used to restore the selection after some content modification to the document.
       
 12425 		 *
       
 12426 		 * @method getBookmark
       
 12427 		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
       
 12428 		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
       
 12429 		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
       
 12430 		 * @example
       
 12431 		 * // Stores a bookmark of the current selection
       
 12432 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 12433 		 *
       
 12434 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 12435 		 *
       
 12436 		 * // Restore the selection bookmark
       
 12437 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 12438 		 */
       
 12439 		this.getBookmark = function(type, normalized) {
       
 12440 			var rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
       
 12441 
       
 12442 			function findIndex(name, element) {
       
 12443 				var index = 0;
       
 12444 
       
 12445 				Tools.each(dom.select(name), function(node, i) {
       
 12446 					if (node == element) {
       
 12447 						index = i;
       
 12448 					}
       
 12449 				});
       
 12450 
       
 12451 				return index;
       
 12452 			}
       
 12453 
       
 12454 			function normalizeTableCellSelection(rng) {
       
 12455 				function moveEndPoint(start) {
       
 12456 					var container, offset, childNodes, prefix = start ? 'start' : 'end';
       
 12457 
       
 12458 					container = rng[prefix + 'Container'];
       
 12459 					offset = rng[prefix + 'Offset'];
       
 12460 
       
 12461 					if (container.nodeType == 1 && container.nodeName == "TR") {
       
 12462 						childNodes = container.childNodes;
       
 12463 						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
       
 12464 						if (container) {
       
 12465 							offset = start ? 0 : container.childNodes.length;
       
 12466 							rng['set' + (start ? 'Start' : 'End')](container, offset);
       
 12467 						}
       
 12468 					}
       
 12469 				}
       
 12470 
       
 12471 				moveEndPoint(true);
       
 12472 				moveEndPoint();
       
 12473 
       
 12474 				return rng;
       
 12475 			}
       
 12476 
       
 12477 			function getLocation() {
       
 12478 				var rng = selection.getRng(true), root = dom.getRoot(), bookmark = {};
       
 12479 
       
 12480 				function getPoint(rng, start) {
       
 12481 					var container = rng[start ? 'startContainer' : 'endContainer'],
       
 12482 						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
       
 12483 
       
 12484 					if (container.nodeType == 3) {
       
 12485 						if (normalized) {
       
 12486 							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
       
 12487 								offset += node.nodeValue.length;
       
 12488 							}
       
 12489 						}
       
 12490 
       
 12491 						point.push(offset);
       
 12492 					} else {
       
 12493 						childNodes = container.childNodes;
       
 12494 
       
 12495 						if (offset >= childNodes.length && childNodes.length) {
       
 12496 							after = 1;
       
 12497 							offset = Math.max(0, childNodes.length - 1);
       
 12498 						}
       
 12499 
       
 12500 						point.push(dom.nodeIndex(childNodes[offset], normalized) + after);
       
 12501 					}
       
 12502 
       
 12503 					for (; container && container != root; container = container.parentNode) {
       
 12504 						point.push(dom.nodeIndex(container, normalized));
       
 12505 					}
       
 12506 
       
 12507 					return point;
       
 12508 				}
       
 12509 
       
 12510 				bookmark.start = getPoint(rng, true);
       
 12511 
       
 12512 				if (!selection.isCollapsed()) {
       
 12513 					bookmark.end = getPoint(rng);
       
 12514 				}
       
 12515 
       
 12516 				return bookmark;
       
 12517 			}
       
 12518 
       
 12519 			if (type == 2) {
       
 12520 				element = selection.getNode();
       
 12521 				name = element ? element.nodeName : null;
       
 12522 
       
 12523 				if (name == 'IMG') {
       
 12524 					return {name: name, index: findIndex(name, element)};
       
 12525 				}
       
 12526 
       
 12527 				if (selection.tridentSel) {
       
 12528 					return selection.tridentSel.getBookmark(type);
       
 12529 				}
       
 12530 
       
 12531 				return getLocation();
       
 12532 			}
       
 12533 
       
 12534 			// Handle simple range
       
 12535 			if (type) {
       
 12536 				return {rng: selection.getRng()};
       
 12537 			}
       
 12538 
       
 12539 			rng = selection.getRng();
       
 12540 			id = dom.uniqueId();
       
 12541 			collapsed = selection.isCollapsed();
       
 12542 			styles = 'overflow:hidden;line-height:0px';
       
 12543 
       
 12544 			// Explorer method
       
 12545 			if (rng.duplicate || rng.item) {
       
 12546 				// Text selection
       
 12547 				if (!rng.item) {
       
 12548 					rng2 = rng.duplicate();
       
 12549 
       
 12550 					try {
       
 12551 						// Insert start marker
       
 12552 						rng.collapse();
       
 12553 						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
       
 12554 
       
 12555 						// Insert end marker
       
 12556 						if (!collapsed) {
       
 12557 							rng2.collapse(false);
       
 12558 
       
 12559 							// Detect the empty space after block elements in IE and move the
       
 12560 							// end back one character <p></p>] becomes <p>]</p>
       
 12561 							rng.moveToElementText(rng2.parentElement());
       
 12562 							if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
       
 12563 								rng2.move('character', -1);
       
 12564 							}
       
 12565 
       
 12566 							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
       
 12567 						}
       
 12568 					} catch (ex) {
       
 12569 						// IE might throw unspecified error so lets ignore it
       
 12570 						return null;
       
 12571 					}
       
 12572 				} else {
       
 12573 					// Control selection
       
 12574 					element = rng.item(0);
       
 12575 					name = element.nodeName;
       
 12576 
       
 12577 					return {name: name, index: findIndex(name, element)};
       
 12578 				}
       
 12579 			} else {
       
 12580 				element = selection.getNode();
       
 12581 				name = element.nodeName;
       
 12582 				if (name == 'IMG') {
       
 12583 					return {name: name, index: findIndex(name, element)};
       
 12584 				}
       
 12585 
       
 12586 				// W3C method
       
 12587 				rng2 = normalizeTableCellSelection(rng.cloneRange());
       
 12588 
       
 12589 				// Insert end marker
       
 12590 				if (!collapsed) {
       
 12591 					rng2.collapse(false);
       
 12592 					rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
       
 12593 				}
       
 12594 
       
 12595 				rng = normalizeTableCellSelection(rng);
       
 12596 				rng.collapse(true);
       
 12597 				rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
       
 12598 			}
       
 12599 
       
 12600 			selection.moveToBookmark({id: id, keep: 1});
       
 12601 
       
 12602 			return {id: id};
       
 12603 		};
       
 12604 
       
 12605 		/**
       
 12606 		 * Restores the selection to the specified bookmark.
       
 12607 		 *
       
 12608 		 * @method moveToBookmark
       
 12609 		 * @param {Object} bookmark Bookmark to restore selection from.
       
 12610 		 * @return {Boolean} true/false if it was successful or not.
       
 12611 		 * @example
       
 12612 		 * // Stores a bookmark of the current selection
       
 12613 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 12614 		 *
       
 12615 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 12616 		 *
       
 12617 		 * // Restore the selection bookmark
       
 12618 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 12619 		 */
       
 12620 		this.moveToBookmark = function(bookmark) {
       
 12621 			var rng, root, startContainer, endContainer, startOffset, endOffset;
       
 12622 
       
 12623 			function setEndPoint(start) {
       
 12624 				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
       
 12625 
       
 12626 				if (point) {
       
 12627 					offset = point[0];
       
 12628 
       
 12629 					// Find container node
       
 12630 					for (node = root, i = point.length - 1; i >= 1; i--) {
       
 12631 						children = node.childNodes;
       
 12632 
       
 12633 						if (point[i] > children.length - 1) {
       
 12634 							return;
       
 12635 						}
       
 12636 
       
 12637 						node = children[point[i]];
       
 12638 					}
       
 12639 
       
 12640 					// Move text offset to best suitable location
       
 12641 					if (node.nodeType === 3) {
       
 12642 						offset = Math.min(point[0], node.nodeValue.length);
       
 12643 					}
       
 12644 
       
 12645 					// Move element offset to best suitable location
       
 12646 					if (node.nodeType === 1) {
       
 12647 						offset = Math.min(point[0], node.childNodes.length);
       
 12648 					}
       
 12649 
       
 12650 					// Set offset within container node
       
 12651 					if (start) {
       
 12652 						rng.setStart(node, offset);
       
 12653 					} else {
       
 12654 						rng.setEnd(node, offset);
       
 12655 					}
       
 12656 				}
       
 12657 
       
 12658 				return true;
       
 12659 			}
       
 12660 
       
 12661 			function restoreEndPoint(suffix) {
       
 12662 				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
       
 12663 
       
 12664 				if (marker) {
       
 12665 					node = marker.parentNode;
       
 12666 
       
 12667 					if (suffix == 'start') {
       
 12668 						if (!keep) {
       
 12669 							idx = dom.nodeIndex(marker);
       
 12670 						} else {
       
 12671 							node = marker.firstChild;
       
 12672 							idx = 1;
       
 12673 						}
       
 12674 
       
 12675 						startContainer = endContainer = node;
       
 12676 						startOffset = endOffset = idx;
       
 12677 					} else {
       
 12678 						if (!keep) {
       
 12679 							idx = dom.nodeIndex(marker);
       
 12680 						} else {
       
 12681 							node = marker.firstChild;
       
 12682 							idx = 1;
       
 12683 						}
       
 12684 
       
 12685 						endContainer = node;
       
 12686 						endOffset = idx;
       
 12687 					}
       
 12688 
       
 12689 					if (!keep) {
       
 12690 						prev = marker.previousSibling;
       
 12691 						next = marker.nextSibling;
       
 12692 
       
 12693 						// Remove all marker text nodes
       
 12694 						Tools.each(Tools.grep(marker.childNodes), function(node) {
       
 12695 							if (node.nodeType == 3) {
       
 12696 								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
       
 12697 							}
       
 12698 						});
       
 12699 
       
 12700 						// Remove marker but keep children if for example contents where inserted into the marker
       
 12701 						// Also remove duplicated instances of the marker for example by a
       
 12702 						// split operation or by WebKit auto split on paste feature
       
 12703 						while ((marker = dom.get(bookmark.id + '_' + suffix))) {
       
 12704 							dom.remove(marker, 1);
       
 12705 						}
       
 12706 
       
 12707 						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
       
 12708 						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market
       
 12709 						// isn't worth the effort. Sorry, Opera but it's just a fact
       
 12710 						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) {
       
 12711 							idx = prev.nodeValue.length;
       
 12712 							prev.appendData(next.nodeValue);
       
 12713 							dom.remove(next);
       
 12714 
       
 12715 							if (suffix == 'start') {
       
 12716 								startContainer = endContainer = prev;
       
 12717 								startOffset = endOffset = idx;
       
 12718 							} else {
       
 12719 								endContainer = prev;
       
 12720 								endOffset = idx;
       
 12721 							}
       
 12722 						}
       
 12723 					}
       
 12724 				}
       
 12725 			}
       
 12726 
       
 12727 			function addBogus(node) {
       
 12728 				// Adds a bogus BR element for empty block elements
       
 12729 				if (dom.isBlock(node) && !node.innerHTML && !Env.ie) {
       
 12730 					node.innerHTML = '<br data-mce-bogus="1" />';
       
 12731 				}
       
 12732 
       
 12733 				return node;
       
 12734 			}
       
 12735 
       
 12736 			if (bookmark) {
       
 12737 				if (bookmark.start) {
       
 12738 					rng = dom.createRng();
       
 12739 					root = dom.getRoot();
       
 12740 
       
 12741 					if (selection.tridentSel) {
       
 12742 						return selection.tridentSel.moveToBookmark(bookmark);
       
 12743 					}
       
 12744 
       
 12745 					if (setEndPoint(true) && setEndPoint()) {
       
 12746 						selection.setRng(rng);
       
 12747 					}
       
 12748 				} else if (bookmark.id) {
       
 12749 					// Restore start/end points
       
 12750 					restoreEndPoint('start');
       
 12751 					restoreEndPoint('end');
       
 12752 
       
 12753 					if (startContainer) {
       
 12754 						rng = dom.createRng();
       
 12755 						rng.setStart(addBogus(startContainer), startOffset);
       
 12756 						rng.setEnd(addBogus(endContainer), endOffset);
       
 12757 						selection.setRng(rng);
       
 12758 					}
       
 12759 				} else if (bookmark.name) {
       
 12760 					selection.select(dom.select(bookmark.name)[bookmark.index]);
       
 12761 				} else if (bookmark.rng) {
       
 12762 					selection.setRng(bookmark.rng);
       
 12763 				}
       
 12764 			}
       
 12765 		};
       
 12766 	}
       
 12767 
       
 12768 	/**
       
 12769 	 * Returns true/false if the specified node is a bookmark node or not.
       
 12770 	 *
       
 12771 	 * @static
       
 12772 	 * @method isBookmarkNode
       
 12773 	 * @param {DOMNode} node DOM Node to check if it's a bookmark node or not.
       
 12774 	 * @return {Boolean} true/false if the node is a bookmark node or not.
       
 12775 	 */
       
 12776 	BookmarkManager.isBookmarkNode = function(node) {
       
 12777 		return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark';
       
 12778 	};
       
 12779 
       
 12780 	return BookmarkManager;
       
 12781 });
       
 12782 
       
 12783 // Included from: js/tinymce/classes/dom/Selection.js
       
 12784 
       
 12785 /**
       
 12786  * Selection.js
       
 12787  *
       
 12788  * Copyright, Moxiecode Systems AB
       
 12789  * Released under LGPL License.
       
 12790  *
       
 12791  * License: http://www.tinymce.com/license
       
 12792  * Contributing: http://www.tinymce.com/contributing
       
 12793  */
       
 12794 
       
 12795 /**
       
 12796  * This class handles text and control selection it's an crossbrowser utility class.
       
 12797  * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
       
 12798  *
       
 12799  * @class tinymce.dom.Selection
       
 12800  * @example
       
 12801  * // Getting the currently selected node for the active editor
       
 12802  * alert(tinymce.activeEditor.selection.getNode().nodeName);
       
 12803  */
       
 12804 define("tinymce/dom/Selection", [
       
 12805 	"tinymce/dom/TreeWalker",
       
 12806 	"tinymce/dom/TridentSelection",
       
 12807 	"tinymce/dom/ControlSelection",
       
 12808 	"tinymce/dom/RangeUtils",
       
 12809 	"tinymce/dom/BookmarkManager",
       
 12810 	"tinymce/Env",
       
 12811 	"tinymce/util/Tools"
       
 12812 ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, Env, Tools) {
       
 12813 	var each = Tools.each, trim = Tools.trim;
       
 12814 	var isIE = Env.ie;
       
 12815 
       
 12816 	/**
       
 12817 	 * Constructs a new selection instance.
       
 12818 	 *
       
 12819 	 * @constructor
       
 12820 	 * @method Selection
       
 12821 	 * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
       
 12822 	 * @param {Window} win Window to bind the selection object to.
       
 12823 	 * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
       
 12824 	 */
       
 12825 	function Selection(dom, win, serializer, editor) {
       
 12826 		var self = this;
       
 12827 
       
 12828 		self.dom = dom;
       
 12829 		self.win = win;
       
 12830 		self.serializer = serializer;
       
 12831 		self.editor = editor;
       
 12832 		self.bookmarkManager = new BookmarkManager(self);
       
 12833 		self.controlSelection = new ControlSelection(self, editor);
       
 12834 
       
 12835 		// No W3C Range support
       
 12836 		if (!self.win.getSelection) {
       
 12837 			self.tridentSel = new TridentSelection(self);
       
 12838 		}
       
 12839 	}
       
 12840 
       
 12841 	Selection.prototype = {
       
 12842 		/**
       
 12843 		 * Move the selection cursor range to the specified node and offset.
       
 12844 		 * If there is no node specified it will move it to the first suitable location within the body.
       
 12845 		 *
       
 12846 		 * @method setCursorLocation
       
 12847 		 * @param {Node} node Optional node to put the cursor in.
       
 12848 		 * @param {Number} offset Optional offset from the start of the node to put the cursor at.
       
 12849 		 */
       
 12850 		setCursorLocation: function(node, offset) {
       
 12851 			var self = this, rng = self.dom.createRng();
       
 12852 
       
 12853 			if (!node) {
       
 12854 				self._moveEndPoint(rng, self.editor.getBody(), true);
       
 12855 				self.setRng(rng);
       
 12856 			} else {
       
 12857 				rng.setStart(node, offset);
       
 12858 				rng.setEnd(node, offset);
       
 12859 				self.setRng(rng);
       
 12860 				self.collapse(false);
       
 12861 			}
       
 12862 		},
       
 12863 
       
 12864 		/**
       
 12865 		 * Returns the selected contents using the DOM serializer passed in to this class.
       
 12866 		 *
       
 12867 		 * @method getContent
       
 12868 		 * @param {Object} s Optional settings class with for example output format text or html.
       
 12869 		 * @return {String} Selected contents in for example HTML format.
       
 12870 		 * @example
       
 12871 		 * // Alerts the currently selected contents
       
 12872 		 * alert(tinymce.activeEditor.selection.getContent());
       
 12873 		 *
       
 12874 		 * // Alerts the currently selected contents as plain text
       
 12875 		 * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
       
 12876 		 */
       
 12877 		getContent: function(args) {
       
 12878 			var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
       
 12879 			var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
       
 12880 
       
 12881 			args = args || {};
       
 12882 			whiteSpaceBefore = whiteSpaceAfter = '';
       
 12883 			args.get = true;
       
 12884 			args.format = args.format || 'html';
       
 12885 			args.selection = true;
       
 12886 			self.editor.fire('BeforeGetContent', args);
       
 12887 
       
 12888 			if (args.format == 'text') {
       
 12889 				return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
       
 12890 			}
       
 12891 
       
 12892 			if (rng.cloneContents) {
       
 12893 				fragment = rng.cloneContents();
       
 12894 
       
 12895 				if (fragment) {
       
 12896 					tmpElm.appendChild(fragment);
       
 12897 				}
       
 12898 			} else if (rng.item !== undefined || rng.htmlText !== undefined) {
       
 12899 				// IE will produce invalid markup if elements are present that
       
 12900 				// it doesn't understand like custom elements or HTML5 elements.
       
 12901 				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
       
 12902 				tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
       
 12903 				tmpElm.removeChild(tmpElm.firstChild);
       
 12904 			} else {
       
 12905 				tmpElm.innerHTML = rng.toString();
       
 12906 			}
       
 12907 
       
 12908 			// Keep whitespace before and after
       
 12909 			if (/^\s/.test(tmpElm.innerHTML)) {
       
 12910 				whiteSpaceBefore = ' ';
       
 12911 			}
       
 12912 
       
 12913 			if (/\s+$/.test(tmpElm.innerHTML)) {
       
 12914 				whiteSpaceAfter = ' ';
       
 12915 			}
       
 12916 
       
 12917 			args.getInner = true;
       
 12918 
       
 12919 			args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
       
 12920 			self.editor.fire('GetContent', args);
       
 12921 
       
 12922 			return args.content;
       
 12923 		},
       
 12924 
       
 12925 		/**
       
 12926 		 * Sets the current selection to the specified content. If any contents is selected it will be replaced
       
 12927 		 * with the contents passed in to this function. If there is no selection the contents will be inserted
       
 12928 		 * where the caret is placed in the editor/page.
       
 12929 		 *
       
 12930 		 * @method setContent
       
 12931 		 * @param {String} content HTML contents to set could also be other formats depending on settings.
       
 12932 		 * @param {Object} args Optional settings object with for example data format.
       
 12933 		 * @example
       
 12934 		 * // Inserts some HTML contents at the current selection
       
 12935 		 * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
       
 12936 		 */
       
 12937 		setContent: function(content, args) {
       
 12938 			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
       
 12939 
       
 12940 			args = args || {format: 'html'};
       
 12941 			args.set = true;
       
 12942 			args.selection = true;
       
 12943 			content = args.content = content;
       
 12944 
       
 12945 			// Dispatch before set content event
       
 12946 			if (!args.no_events) {
       
 12947 				self.editor.fire('BeforeSetContent', args);
       
 12948 			}
       
 12949 
       
 12950 			content = args.content;
       
 12951 
       
 12952 			if (rng.insertNode) {
       
 12953 				// Make caret marker since insertNode places the caret in the beginning of text after insert
       
 12954 				content += '<span id="__caret">_</span>';
       
 12955 
       
 12956 				// Delete and insert new node
       
 12957 				if (rng.startContainer == doc && rng.endContainer == doc) {
       
 12958 					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
       
 12959 					doc.body.innerHTML = content;
       
 12960 				} else {
       
 12961 					rng.deleteContents();
       
 12962 
       
 12963 					if (doc.body.childNodes.length === 0) {
       
 12964 						doc.body.innerHTML = content;
       
 12965 					} else {
       
 12966 						// createContextualFragment doesn't exists in IE 9 DOMRanges
       
 12967 						if (rng.createContextualFragment) {
       
 12968 							rng.insertNode(rng.createContextualFragment(content));
       
 12969 						} else {
       
 12970 							// Fake createContextualFragment call in IE 9
       
 12971 							frag = doc.createDocumentFragment();
       
 12972 							temp = doc.createElement('div');
       
 12973 
       
 12974 							frag.appendChild(temp);
       
 12975 							temp.outerHTML = content;
       
 12976 
       
 12977 							rng.insertNode(frag);
       
 12978 						}
       
 12979 					}
       
 12980 				}
       
 12981 
       
 12982 				// Move to caret marker
       
 12983 				caretNode = self.dom.get('__caret');
       
 12984 
       
 12985 				// Make sure we wrap it compleatly, Opera fails with a simple select call
       
 12986 				rng = doc.createRange();
       
 12987 				rng.setStartBefore(caretNode);
       
 12988 				rng.setEndBefore(caretNode);
       
 12989 				self.setRng(rng);
       
 12990 
       
 12991 				// Remove the caret position
       
 12992 				self.dom.remove('__caret');
       
 12993 
       
 12994 				try {
       
 12995 					self.setRng(rng);
       
 12996 				} catch (ex) {
       
 12997 					// Might fail on Opera for some odd reason
       
 12998 				}
       
 12999 			} else {
       
 13000 				if (rng.item) {
       
 13001 					// Delete content and get caret text selection
       
 13002 					doc.execCommand('Delete', false, null);
       
 13003 					rng = self.getRng();
       
 13004 				}
       
 13005 
       
 13006 				// Explorer removes spaces from the beginning of pasted contents
       
 13007 				if (/^\s+/.test(content)) {
       
 13008 					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
       
 13009 					self.dom.remove('__mce_tmp');
       
 13010 				} else {
       
 13011 					rng.pasteHTML(content);
       
 13012 				}
       
 13013 			}
       
 13014 
       
 13015 			// Dispatch set content event
       
 13016 			if (!args.no_events) {
       
 13017 				self.editor.fire('SetContent', args);
       
 13018 			}
       
 13019 		},
       
 13020 
       
 13021 		/**
       
 13022 		 * Returns the start element of a selection range. If the start is in a text
       
 13023 		 * node the parent element will be returned.
       
 13024 		 *
       
 13025 		 * @method getStart
       
 13026 		 * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
       
 13027 		 * @return {Element} Start element of selection range.
       
 13028 		 */
       
 13029 		getStart: function(real) {
       
 13030 			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
       
 13031 
       
 13032 			if (rng.duplicate || rng.item) {
       
 13033 				// Control selection, return first item
       
 13034 				if (rng.item) {
       
 13035 					return rng.item(0);
       
 13036 				}
       
 13037 
       
 13038 				// Get start element
       
 13039 				checkRng = rng.duplicate();
       
 13040 				checkRng.collapse(1);
       
 13041 				startElement = checkRng.parentElement();
       
 13042 				if (startElement.ownerDocument !== self.dom.doc) {
       
 13043 					startElement = self.dom.getRoot();
       
 13044 				}
       
 13045 
       
 13046 				// Check if range parent is inside the start element, then return the inner parent element
       
 13047 				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
       
 13048 				parentElement = node = rng.parentElement();
       
 13049 				while ((node = node.parentNode)) {
       
 13050 					if (node == startElement) {
       
 13051 						startElement = parentElement;
       
 13052 						break;
       
 13053 					}
       
 13054 				}
       
 13055 
       
 13056 				return startElement;
       
 13057 			} else {
       
 13058 				startElement = rng.startContainer;
       
 13059 
       
 13060 				if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
       
 13061 					if (!real || !rng.collapsed) {
       
 13062 						startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
       
 13063 					}
       
 13064 				}
       
 13065 
       
 13066 				if (startElement && startElement.nodeType == 3) {
       
 13067 					return startElement.parentNode;
       
 13068 				}
       
 13069 
       
 13070 				return startElement;
       
 13071 			}
       
 13072 		},
       
 13073 
       
 13074 		/**
       
 13075 		 * Returns the end element of a selection range. If the end is in a text
       
 13076 		 * node the parent element will be returned.
       
 13077 		 *
       
 13078 		 * @method getEnd
       
 13079 		 * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
       
 13080 		 * @return {Element} End element of selection range.
       
 13081 		 */
       
 13082 		getEnd: function(real) {
       
 13083 			var self = this, rng = self.getRng(), endElement, endOffset;
       
 13084 
       
 13085 			if (rng.duplicate || rng.item) {
       
 13086 				if (rng.item) {
       
 13087 					return rng.item(0);
       
 13088 				}
       
 13089 
       
 13090 				rng = rng.duplicate();
       
 13091 				rng.collapse(0);
       
 13092 				endElement = rng.parentElement();
       
 13093 				if (endElement.ownerDocument !== self.dom.doc) {
       
 13094 					endElement = self.dom.getRoot();
       
 13095 				}
       
 13096 
       
 13097 				if (endElement && endElement.nodeName == 'BODY') {
       
 13098 					return endElement.lastChild || endElement;
       
 13099 				}
       
 13100 
       
 13101 				return endElement;
       
 13102 			} else {
       
 13103 				endElement = rng.endContainer;
       
 13104 				endOffset = rng.endOffset;
       
 13105 
       
 13106 				if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
       
 13107 					if (!real || !rng.collapsed) {
       
 13108 						endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
       
 13109 					}
       
 13110 				}
       
 13111 
       
 13112 				if (endElement && endElement.nodeType == 3) {
       
 13113 					return endElement.parentNode;
       
 13114 				}
       
 13115 
       
 13116 				return endElement;
       
 13117 			}
       
 13118 		},
       
 13119 
       
 13120 		/**
       
 13121 		 * Returns a bookmark location for the current selection. This bookmark object
       
 13122 		 * can then be used to restore the selection after some content modification to the document.
       
 13123 		 *
       
 13124 		 * @method getBookmark
       
 13125 		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
       
 13126 		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
       
 13127 		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
       
 13128 		 * @example
       
 13129 		 * // Stores a bookmark of the current selection
       
 13130 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 13131 		 *
       
 13132 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 13133 		 *
       
 13134 		 * // Restore the selection bookmark
       
 13135 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 13136 		 */
       
 13137 		getBookmark: function(type, normalized) {
       
 13138 			return this.bookmarkManager.getBookmark(type, normalized);
       
 13139 		},
       
 13140 
       
 13141 		/**
       
 13142 		 * Restores the selection to the specified bookmark.
       
 13143 		 *
       
 13144 		 * @method moveToBookmark
       
 13145 		 * @param {Object} bookmark Bookmark to restore selection from.
       
 13146 		 * @return {Boolean} true/false if it was successful or not.
       
 13147 		 * @example
       
 13148 		 * // Stores a bookmark of the current selection
       
 13149 		 * var bm = tinymce.activeEditor.selection.getBookmark();
       
 13150 		 *
       
 13151 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
       
 13152 		 *
       
 13153 		 * // Restore the selection bookmark
       
 13154 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
       
 13155 		 */
       
 13156 		moveToBookmark: function(bookmark) {
       
 13157 			return this.bookmarkManager.moveToBookmark(bookmark);
       
 13158 		},
       
 13159 
       
 13160 		/**
       
 13161 		 * Selects the specified element. This will place the start and end of the selection range around the element.
       
 13162 		 *
       
 13163 		 * @method select
       
 13164 		 * @param {Element} node HMTL DOM element to select.
       
 13165 		 * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser.
       
 13166 		 * @return {Element} Selected element the same element as the one that got passed in.
       
 13167 		 * @example
       
 13168 		 * // Select the first paragraph in the active editor
       
 13169 		 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
       
 13170 		 */
       
 13171 		select: function(node, content) {
       
 13172 			var self = this, dom = self.dom, rng = dom.createRng(), idx;
       
 13173 
       
 13174 			// Clear stored range set by FocusManager
       
 13175 			self.lastFocusBookmark = null;
       
 13176 
       
 13177 			if (node) {
       
 13178 				if (!content && self.controlSelection.controlSelect(node)) {
       
 13179 					return;
       
 13180 				}
       
 13181 
       
 13182 				idx = dom.nodeIndex(node);
       
 13183 				rng.setStart(node.parentNode, idx);
       
 13184 				rng.setEnd(node.parentNode, idx + 1);
       
 13185 
       
 13186 				// Find first/last text node or BR element
       
 13187 				if (content) {
       
 13188 					self._moveEndPoint(rng, node, true);
       
 13189 					self._moveEndPoint(rng, node);
       
 13190 				}
       
 13191 
       
 13192 				self.setRng(rng);
       
 13193 			}
       
 13194 
       
 13195 			return node;
       
 13196 		},
       
 13197 
       
 13198 		/**
       
 13199 		 * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection.
       
 13200 		 *
       
 13201 		 * @method isCollapsed
       
 13202 		 * @return {Boolean} true/false state if the selection range is collapsed or not.
       
 13203 		 * Collapsed means if it's a caret or a larger selection.
       
 13204 		 */
       
 13205 		isCollapsed: function() {
       
 13206 			var self = this, rng = self.getRng(), sel = self.getSel();
       
 13207 
       
 13208 			if (!rng || rng.item) {
       
 13209 				return false;
       
 13210 			}
       
 13211 
       
 13212 			if (rng.compareEndPoints) {
       
 13213 				return rng.compareEndPoints('StartToEnd', rng) === 0;
       
 13214 			}
       
 13215 
       
 13216 			return !sel || rng.collapsed;
       
 13217 		},
       
 13218 
       
 13219 		/**
       
 13220 		 * Collapse the selection to start or end of range.
       
 13221 		 *
       
 13222 		 * @method collapse
       
 13223 		 * @param {Boolean} toStart Optional boolean state if to collapse to end or not. Defaults to start.
       
 13224 		 */
       
 13225 		collapse: function(toStart) {
       
 13226 			var self = this, rng = self.getRng(), node;
       
 13227 
       
 13228 			// Control range on IE
       
 13229 			if (rng.item) {
       
 13230 				node = rng.item(0);
       
 13231 				rng = self.win.document.body.createTextRange();
       
 13232 				rng.moveToElementText(node);
       
 13233 			}
       
 13234 
       
 13235 			rng.collapse(!!toStart);
       
 13236 			self.setRng(rng);
       
 13237 		},
       
 13238 
       
 13239 		/**
       
 13240 		 * Returns the browsers internal selection object.
       
 13241 		 *
       
 13242 		 * @method getSel
       
 13243 		 * @return {Selection} Internal browser selection object.
       
 13244 		 */
       
 13245 		getSel: function() {
       
 13246 			var win = this.win;
       
 13247 
       
 13248 			return win.getSelection ? win.getSelection() : win.document.selection;
       
 13249 		},
       
 13250 
       
 13251 		/**
       
 13252 		 * Returns the browsers internal range object.
       
 13253 		 *
       
 13254 		 * @method getRng
       
 13255 		 * @param {Boolean} w3c Forces a compatible W3C range on IE.
       
 13256 		 * @return {Range} Internal browser range object.
       
 13257 		 * @see http://www.quirksmode.org/dom/range_intro.html
       
 13258 		 * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/
       
 13259 		 */
       
 13260 		getRng: function(w3c) {
       
 13261 			var self = this, selection, rng, elm, doc = self.win.document, ieRng;
       
 13262 
       
 13263 			function tryCompareBoundaryPoints(how, sourceRange, destinationRange) {
       
 13264 				try {
       
 13265 					return sourceRange.compareBoundaryPoints(how, destinationRange);
       
 13266 				} catch (ex) {
       
 13267 					// Gecko throws wrong document exception if the range points
       
 13268 					// to nodes that where removed from the dom #6690
       
 13269 					// Browsers should mutate existing DOMRange instances so that they always point
       
 13270 					// to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink
       
 13271 					// For performance reasons just return -1
       
 13272 					return -1;
       
 13273 				}
       
 13274 			}
       
 13275 
       
 13276 			// Use last rng passed from FocusManager if it's available this enables
       
 13277 			// calls to editor.selection.getStart() to work when caret focus is lost on IE
       
 13278 			if (!w3c && self.lastFocusBookmark) {
       
 13279 				var bookmark = self.lastFocusBookmark;
       
 13280 
       
 13281 				// Convert bookmark to range IE 11 fix
       
 13282 				if (bookmark.startContainer) {
       
 13283 					rng = doc.createRange();
       
 13284 					rng.setStart(bookmark.startContainer, bookmark.startOffset);
       
 13285 					rng.setEnd(bookmark.endContainer, bookmark.endOffset);
       
 13286 				} else {
       
 13287 					rng = bookmark;
       
 13288 				}
       
 13289 
       
 13290 				return rng;
       
 13291 			}
       
 13292 
       
 13293 			// Found tridentSel object then we need to use that one
       
 13294 			if (w3c && self.tridentSel) {
       
 13295 				return self.tridentSel.getRangeAt(0);
       
 13296 			}
       
 13297 
       
 13298 			try {
       
 13299 				if ((selection = self.getSel())) {
       
 13300 					if (selection.rangeCount > 0) {
       
 13301 						rng = selection.getRangeAt(0);
       
 13302 					} else {
       
 13303 						rng = selection.createRange ? selection.createRange() : doc.createRange();
       
 13304 					}
       
 13305 				}
       
 13306 			} catch (ex) {
       
 13307 				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
       
 13308 			}
       
 13309 
       
 13310 			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
       
 13311 			// IE 11 doesn't support the selection object so we check for that as well
       
 13312 			if (isIE && rng && rng.setStart && doc.selection) {
       
 13313 				try {
       
 13314 					// IE will sometimes throw an exception here
       
 13315 					ieRng = doc.selection.createRange();
       
 13316 				} catch (ex) {
       
 13317 
       
 13318 				}
       
 13319 
       
 13320 				if (ieRng && ieRng.item) {
       
 13321 					elm = ieRng.item(0);
       
 13322 					rng = doc.createRange();
       
 13323 					rng.setStartBefore(elm);
       
 13324 					rng.setEndAfter(elm);
       
 13325 				}
       
 13326 			}
       
 13327 
       
 13328 			// No range found then create an empty one
       
 13329 			// This can occur when the editor is placed in a hidden container element on Gecko
       
 13330 			// Or on IE when there was an exception
       
 13331 			if (!rng) {
       
 13332 				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
       
 13333 			}
       
 13334 
       
 13335 			// If range is at start of document then move it to start of body
       
 13336 			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
       
 13337 				elm = self.dom.getRoot();
       
 13338 				rng.setStart(elm, 0);
       
 13339 				rng.setEnd(elm, 0);
       
 13340 			}
       
 13341 
       
 13342 			if (self.selectedRange && self.explicitRange) {
       
 13343 				if (tryCompareBoundaryPoints(rng.START_TO_START, rng, self.selectedRange) === 0 &&
       
 13344 					tryCompareBoundaryPoints(rng.END_TO_END, rng, self.selectedRange) === 0) {
       
 13345 					// Safari, Opera and Chrome only ever select text which causes the range to change.
       
 13346 					// This lets us use the originally set range if the selection hasn't been changed by the user.
       
 13347 					rng = self.explicitRange;
       
 13348 				} else {
       
 13349 					self.selectedRange = null;
       
 13350 					self.explicitRange = null;
       
 13351 				}
       
 13352 			}
       
 13353 
       
 13354 			return rng;
       
 13355 		},
       
 13356 
       
 13357 		/**
       
 13358 		 * Changes the selection to the specified DOM range.
       
 13359 		 *
       
 13360 		 * @method setRng
       
 13361 		 * @param {Range} rng Range to select.
       
 13362 		 */
       
 13363 		setRng: function(rng, forward) {
       
 13364 			var self = this, sel;
       
 13365 
       
 13366 			if (!rng) {
       
 13367 				return;
       
 13368 			}
       
 13369 
       
 13370 			// Is IE specific range
       
 13371 			if (rng.select) {
       
 13372 				try {
       
 13373 					rng.select();
       
 13374 				} catch (ex) {
       
 13375 					// Needed for some odd IE bug #1843306
       
 13376 				}
       
 13377 
       
 13378 				return;
       
 13379 			}
       
 13380 
       
 13381 			if (!self.tridentSel) {
       
 13382 				sel = self.getSel();
       
 13383 
       
 13384 				if (sel) {
       
 13385 					self.explicitRange = rng;
       
 13386 
       
 13387 					try {
       
 13388 						sel.removeAllRanges();
       
 13389 						sel.addRange(rng);
       
 13390 					} catch (ex) {
       
 13391 						// IE might throw errors here if the editor is within a hidden container and selection is changed
       
 13392 					}
       
 13393 
       
 13394 					// Forward is set to false and we have an extend function
       
 13395 					if (forward === false && sel.extend) {
       
 13396 						sel.collapse(rng.endContainer, rng.endOffset);
       
 13397 						sel.extend(rng.startContainer, rng.startOffset);
       
 13398 					}
       
 13399 
       
 13400 					// adding range isn't always successful so we need to check range count otherwise an exception can occur
       
 13401 					self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
       
 13402 				}
       
 13403 			} else {
       
 13404 				// Is W3C Range fake range on IE
       
 13405 				if (rng.cloneRange) {
       
 13406 					try {
       
 13407 						self.tridentSel.addRange(rng);
       
 13408 						return;
       
 13409 					} catch (ex) {
       
 13410 						//IE9 throws an error here if called before selection is placed in the editor
       
 13411 					}
       
 13412 				}
       
 13413 			}
       
 13414 		},
       
 13415 
       
 13416 		/**
       
 13417 		 * Sets the current selection to the specified DOM element.
       
 13418 		 *
       
 13419 		 * @method setNode
       
 13420 		 * @param {Element} elm Element to set as the contents of the selection.
       
 13421 		 * @return {Element} Returns the element that got passed in.
       
 13422 		 * @example
       
 13423 		 * // Inserts a DOM node at current selection/caret location
       
 13424 		 * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'}));
       
 13425 		 */
       
 13426 		setNode: function(elm) {
       
 13427 			var self = this;
       
 13428 
       
 13429 			self.setContent(self.dom.getOuterHTML(elm));
       
 13430 
       
 13431 			return elm;
       
 13432 		},
       
 13433 
       
 13434 		/**
       
 13435 		 * Returns the currently selected element or the common ancestor element for both start and end of the selection.
       
 13436 		 *
       
 13437 		 * @method getNode
       
 13438 		 * @return {Element} Currently selected element or common ancestor element.
       
 13439 		 * @example
       
 13440 		 * // Alerts the currently selected elements node name
       
 13441 		 * alert(tinymce.activeEditor.selection.getNode().nodeName);
       
 13442 		 */
       
 13443 		getNode: function() {
       
 13444 			var self = this, rng = self.getRng(), elm;
       
 13445 			var startContainer = rng.startContainer, endContainer = rng.endContainer;
       
 13446 			var startOffset = rng.startOffset, endOffset = rng.endOffset, root = self.dom.getRoot();
       
 13447 
       
 13448 			function skipEmptyTextNodes(node, forwards) {
       
 13449 				var orig = node;
       
 13450 
       
 13451 				while (node && node.nodeType === 3 && node.length === 0) {
       
 13452 					node = forwards ? node.nextSibling : node.previousSibling;
       
 13453 				}
       
 13454 
       
 13455 				return node || orig;
       
 13456 			}
       
 13457 
       
 13458 			// Range maybe lost after the editor is made visible again
       
 13459 			if (!rng) {
       
 13460 				return root;
       
 13461 			}
       
 13462 
       
 13463 			if (rng.setStart) {
       
 13464 				elm = rng.commonAncestorContainer;
       
 13465 
       
 13466 				// Handle selection a image or other control like element such as anchors
       
 13467 				if (!rng.collapsed) {
       
 13468 					if (startContainer == endContainer) {
       
 13469 						if (endOffset - startOffset < 2) {
       
 13470 							if (startContainer.hasChildNodes()) {
       
 13471 								elm = startContainer.childNodes[startOffset];
       
 13472 							}
       
 13473 						}
       
 13474 					}
       
 13475 
       
 13476 					// If the anchor node is a element instead of a text node then return this element
       
 13477 					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
       
 13478 					//	return sel.anchorNode.childNodes[sel.anchorOffset];
       
 13479 
       
 13480 					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
       
 13481 					// This happens when you double click an underlined word in FireFox.
       
 13482 					if (startContainer.nodeType === 3 && endContainer.nodeType === 3) {
       
 13483 						if (startContainer.length === startOffset) {
       
 13484 							startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
       
 13485 						} else {
       
 13486 							startContainer = startContainer.parentNode;
       
 13487 						}
       
 13488 
       
 13489 						if (endOffset === 0) {
       
 13490 							endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
       
 13491 						} else {
       
 13492 							endContainer = endContainer.parentNode;
       
 13493 						}
       
 13494 
       
 13495 						if (startContainer && startContainer === endContainer) {
       
 13496 							return startContainer;
       
 13497 						}
       
 13498 					}
       
 13499 				}
       
 13500 
       
 13501 				if (elm && elm.nodeType == 3) {
       
 13502 					return elm.parentNode;
       
 13503 				}
       
 13504 
       
 13505 				return elm;
       
 13506 			}
       
 13507 
       
 13508 			elm = rng.item ? rng.item(0) : rng.parentElement();
       
 13509 
       
 13510 			// IE 7 might return elements outside the iframe
       
 13511 			if (elm.ownerDocument !== self.win.document) {
       
 13512 				elm = root;
       
 13513 			}
       
 13514 
       
 13515 			return elm;
       
 13516 		},
       
 13517 
       
 13518 		getSelectedBlocks: function(startElm, endElm) {
       
 13519 			var self = this, dom = self.dom, node, root, selectedBlocks = [];
       
 13520 
       
 13521 			root = dom.getRoot();
       
 13522 			startElm = dom.getParent(startElm || self.getStart(), dom.isBlock);
       
 13523 			endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock);
       
 13524 
       
 13525 			if (startElm && startElm != root) {
       
 13526 				selectedBlocks.push(startElm);
       
 13527 			}
       
 13528 
       
 13529 			if (startElm && endElm && startElm != endElm) {
       
 13530 				node = startElm;
       
 13531 
       
 13532 				var walker = new TreeWalker(startElm, root);
       
 13533 				while ((node = walker.next()) && node != endElm) {
       
 13534 					if (dom.isBlock(node)) {
       
 13535 						selectedBlocks.push(node);
       
 13536 					}
       
 13537 				}
       
 13538 			}
       
 13539 
       
 13540 			if (endElm && startElm != endElm && endElm != root) {
       
 13541 				selectedBlocks.push(endElm);
       
 13542 			}
       
 13543 
       
 13544 			return selectedBlocks;
       
 13545 		},
       
 13546 
       
 13547 		isForward: function() {
       
 13548 			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
       
 13549 
       
 13550 			// No support for selection direction then always return true
       
 13551 			if (!sel || !sel.anchorNode || !sel.focusNode) {
       
 13552 				return true;
       
 13553 			}
       
 13554 
       
 13555 			anchorRange = dom.createRng();
       
 13556 			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
       
 13557 			anchorRange.collapse(true);
       
 13558 
       
 13559 			focusRange = dom.createRng();
       
 13560 			focusRange.setStart(sel.focusNode, sel.focusOffset);
       
 13561 			focusRange.collapse(true);
       
 13562 
       
 13563 			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
       
 13564 		},
       
 13565 
       
 13566 		normalize: function() {
       
 13567 			var self = this, rng = self.getRng();
       
 13568 
       
 13569 			if (Env.range && new RangeUtils(self.dom).normalize(rng)) {
       
 13570 				self.setRng(rng, self.isForward());
       
 13571 			}
       
 13572 
       
 13573 			return rng;
       
 13574 		},
       
 13575 
       
 13576 		/**
       
 13577 		 * Executes callback when the current selection starts/stops matching the specified selector. The current
       
 13578 		 * state will be passed to the callback as it's first argument.
       
 13579 		 *
       
 13580 		 * @method selectorChanged
       
 13581 		 * @param {String} selector CSS selector to check for.
       
 13582 		 * @param {function} callback Callback with state and args when the selector is matches or not.
       
 13583 		 */
       
 13584 		selectorChanged: function(selector, callback) {
       
 13585 			var self = this, currentSelectors;
       
 13586 
       
 13587 			if (!self.selectorChangedData) {
       
 13588 				self.selectorChangedData = {};
       
 13589 				currentSelectors = {};
       
 13590 
       
 13591 				self.editor.on('NodeChange', function(e) {
       
 13592 					var node = e.element, dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
       
 13593 
       
 13594 					// Check for new matching selectors
       
 13595 					each(self.selectorChangedData, function(callbacks, selector) {
       
 13596 						each(parents, function(node) {
       
 13597 							if (dom.is(node, selector)) {
       
 13598 								if (!currentSelectors[selector]) {
       
 13599 									// Execute callbacks
       
 13600 									each(callbacks, function(callback) {
       
 13601 										callback(true, {node: node, selector: selector, parents: parents});
       
 13602 									});
       
 13603 
       
 13604 									currentSelectors[selector] = callbacks;
       
 13605 								}
       
 13606 
       
 13607 								matchedSelectors[selector] = callbacks;
       
 13608 								return false;
       
 13609 							}
       
 13610 						});
       
 13611 					});
       
 13612 
       
 13613 					// Check if current selectors still match
       
 13614 					each(currentSelectors, function(callbacks, selector) {
       
 13615 						if (!matchedSelectors[selector]) {
       
 13616 							delete currentSelectors[selector];
       
 13617 
       
 13618 							each(callbacks, function(callback) {
       
 13619 								callback(false, {node: node, selector: selector, parents: parents});
       
 13620 							});
       
 13621 						}
       
 13622 					});
       
 13623 				});
       
 13624 			}
       
 13625 
       
 13626 			// Add selector listeners
       
 13627 			if (!self.selectorChangedData[selector]) {
       
 13628 				self.selectorChangedData[selector] = [];
       
 13629 			}
       
 13630 
       
 13631 			self.selectorChangedData[selector].push(callback);
       
 13632 
       
 13633 			return self;
       
 13634 		},
       
 13635 
       
 13636 		getScrollContainer: function() {
       
 13637 			var scrollContainer, node = this.dom.getRoot();
       
 13638 
       
 13639 			while (node && node.nodeName != 'BODY') {
       
 13640 				if (node.scrollHeight > node.clientHeight) {
       
 13641 					scrollContainer = node;
       
 13642 					break;
       
 13643 				}
       
 13644 
       
 13645 				node = node.parentNode;
       
 13646 			}
       
 13647 
       
 13648 			return scrollContainer;
       
 13649 		},
       
 13650 
       
 13651 		scrollIntoView: function(elm) {
       
 13652 			var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH;
       
 13653 
       
 13654 			function getPos(elm) {
       
 13655 				var x = 0, y = 0;
       
 13656 
       
 13657 				var offsetParent = elm;
       
 13658 				while (offsetParent && offsetParent.nodeType) {
       
 13659 					x += offsetParent.offsetLeft || 0;
       
 13660 					y += offsetParent.offsetTop || 0;
       
 13661 					offsetParent = offsetParent.offsetParent;
       
 13662 				}
       
 13663 
       
 13664 				return {x: x, y: y};
       
 13665 			}
       
 13666 
       
 13667 			if (root.nodeName != 'BODY') {
       
 13668 				var scrollContainer = self.getScrollContainer();
       
 13669 				if (scrollContainer) {
       
 13670 					y = getPos(elm).y - getPos(scrollContainer).y;
       
 13671 					viewPortH = scrollContainer.clientHeight;
       
 13672 					viewPortY = scrollContainer.scrollTop;
       
 13673 					if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
       
 13674 						scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
       
 13675 					}
       
 13676 
       
 13677 					return;
       
 13678 				}
       
 13679 			}
       
 13680 
       
 13681 			viewPort = dom.getViewPort(self.editor.getWin());
       
 13682 			y = dom.getPos(elm).y;
       
 13683 			viewPortY = viewPort.y;
       
 13684 			viewPortH = viewPort.h;
       
 13685 			if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
       
 13686 				self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
       
 13687 			}
       
 13688 		},
       
 13689 
       
 13690 		placeCaretAt: function(clientX, clientY) {
       
 13691 			var doc = this.editor.getDoc(), rng, point;
       
 13692 
       
 13693 			if (doc.caretPositionFromPoint) {
       
 13694 				point = doc.caretPositionFromPoint(clientX, clientY);
       
 13695 				rng = doc.createRange();
       
 13696 				rng.setStart(point.offsetNode, point.offset);
       
 13697 				rng.collapse(true);
       
 13698 			} else if (doc.caretRangeFromPoint) {
       
 13699 				rng = doc.caretRangeFromPoint(clientX, clientY);
       
 13700 			} else if (doc.body.createTextRange) {
       
 13701 				rng = doc.body.createTextRange();
       
 13702 
       
 13703 				try {
       
 13704 					rng.moveToPoint(clientX, clientY);
       
 13705 					rng.collapse(true);
       
 13706 				} catch (ex) {
       
 13707 					rng.collapse(clientY < doc.body.clientHeight);
       
 13708 				}
       
 13709 			}
       
 13710 
       
 13711 			this.setRng(rng);
       
 13712 		},
       
 13713 
       
 13714 		_moveEndPoint: function(rng, node, start) {
       
 13715 			var root = node, walker = new TreeWalker(node, root);
       
 13716 			var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
       
 13717 
       
 13718 			do {
       
 13719 				// Text node
       
 13720 				if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
       
 13721 					if (start) {
       
 13722 						rng.setStart(node, 0);
       
 13723 					} else {
       
 13724 						rng.setEnd(node, node.nodeValue.length);
       
 13725 					}
       
 13726 
       
 13727 					return;
       
 13728 				}
       
 13729 
       
 13730 				// BR/IMG/INPUT elements but not table cells
       
 13731 				if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) {
       
 13732 					if (start) {
       
 13733 						rng.setStartBefore(node);
       
 13734 					} else {
       
 13735 						if (node.nodeName == 'BR') {
       
 13736 							rng.setEndBefore(node);
       
 13737 						} else {
       
 13738 							rng.setEndAfter(node);
       
 13739 						}
       
 13740 					}
       
 13741 
       
 13742 					return;
       
 13743 				}
       
 13744 
       
 13745 				// Found empty text block old IE can place the selection inside those
       
 13746 				if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
       
 13747 					if (start) {
       
 13748 						rng.setStart(node, 0);
       
 13749 					} else {
       
 13750 						rng.setEnd(node, 0);
       
 13751 					}
       
 13752 
       
 13753 					return;
       
 13754 				}
       
 13755 			} while ((node = (start ? walker.next() : walker.prev())));
       
 13756 
       
 13757 			// Failed to find any text node or other suitable location then move to the root of body
       
 13758 			if (root.nodeName == 'BODY') {
       
 13759 				if (start) {
       
 13760 					rng.setStart(root, 0);
       
 13761 				} else {
       
 13762 					rng.setEnd(root, root.childNodes.length);
       
 13763 				}
       
 13764 			}
       
 13765 		},
       
 13766 
       
 13767 		destroy: function() {
       
 13768 			this.win = null;
       
 13769 			this.controlSelection.destroy();
       
 13770 		}
       
 13771 	};
       
 13772 
       
 13773 	return Selection;
       
 13774 });
       
 13775 
       
 13776 // Included from: js/tinymce/classes/dom/ElementUtils.js
       
 13777 
       
 13778 /**
       
 13779  * ElementUtils.js
       
 13780  *
       
 13781  * Copyright, Moxiecode Systems AB
       
 13782  * Released under LGPL License.
       
 13783  *
       
 13784  * License: http://www.tinymce.com/license
       
 13785  * Contributing: http://www.tinymce.com/contributing
       
 13786  */
       
 13787 
       
 13788 /**
       
 13789  * Utility class for various element specific functions.
       
 13790  *
       
 13791  * @private
       
 13792  */
       
 13793 define("tinymce/dom/ElementUtils", [
       
 13794 	"tinymce/dom/BookmarkManager",
       
 13795 	"tinymce/util/Tools"
       
 13796 ], function(BookmarkManager, Tools) {
       
 13797 	var each = Tools.each;
       
 13798 
       
 13799 	function ElementUtils(dom) {
       
 13800 		/**
       
 13801 		 * Compares two nodes and checks if it's attributes and styles matches.
       
 13802 		 * This doesn't compare classes as items since their order is significant.
       
 13803 		 *
       
 13804 		 * @method compare
       
 13805 		 * @param {Node} node1 First node to compare with.
       
 13806 		 * @param {Node} node2 Second node to compare with.
       
 13807 		 * @return {boolean} True/false if the nodes are the same or not.
       
 13808 		 */
       
 13809 		this.compare = function(node1, node2) {
       
 13810 			// Not the same name
       
 13811 			if (node1.nodeName != node2.nodeName) {
       
 13812 				return false;
       
 13813 			}
       
 13814 
       
 13815 			/**
       
 13816 			 * Returns all the nodes attributes excluding internal ones, styles and classes.
       
 13817 			 *
       
 13818 			 * @private
       
 13819 			 * @param {Node} node Node to get attributes from.
       
 13820 			 * @return {Object} Name/value object with attributes and attribute values.
       
 13821 			 */
       
 13822 			function getAttribs(node) {
       
 13823 				var attribs = {};
       
 13824 
       
 13825 				each(dom.getAttribs(node), function(attr) {
       
 13826 					var name = attr.nodeName.toLowerCase();
       
 13827 
       
 13828 					// Don't compare internal attributes or style
       
 13829 					if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
       
 13830 						attribs[name] = dom.getAttrib(node, name);
       
 13831 					}
       
 13832 				});
       
 13833 
       
 13834 				return attribs;
       
 13835 			}
       
 13836 
       
 13837 			/**
       
 13838 			 * Compares two objects checks if it's key + value exists in the other one.
       
 13839 			 *
       
 13840 			 * @private
       
 13841 			 * @param {Object} obj1 First object to compare.
       
 13842 			 * @param {Object} obj2 Second object to compare.
       
 13843 			 * @return {boolean} True/false if the objects matches or not.
       
 13844 			 */
       
 13845 			function compareObjects(obj1, obj2) {
       
 13846 				var value, name;
       
 13847 
       
 13848 				for (name in obj1) {
       
 13849 					// Obj1 has item obj2 doesn't have
       
 13850 					if (obj1.hasOwnProperty(name)) {
       
 13851 						value = obj2[name];
       
 13852 
       
 13853 						// Obj2 doesn't have obj1 item
       
 13854 						if (typeof value == "undefined") {
       
 13855 							return false;
       
 13856 						}
       
 13857 
       
 13858 						// Obj2 item has a different value
       
 13859 						if (obj1[name] != value) {
       
 13860 							return false;
       
 13861 						}
       
 13862 
       
 13863 						// Delete similar value
       
 13864 						delete obj2[name];
       
 13865 					}
       
 13866 				}
       
 13867 
       
 13868 				// Check if obj 2 has something obj 1 doesn't have
       
 13869 				for (name in obj2) {
       
 13870 					// Obj2 has item obj1 doesn't have
       
 13871 					if (obj2.hasOwnProperty(name)) {
       
 13872 						return false;
       
 13873 					}
       
 13874 				}
       
 13875 
       
 13876 				return true;
       
 13877 			}
       
 13878 
       
 13879 			// Attribs are not the same
       
 13880 			if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
       
 13881 				return false;
       
 13882 			}
       
 13883 
       
 13884 			// Styles are not the same
       
 13885 			if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
       
 13886 				return false;
       
 13887 			}
       
 13888 
       
 13889 			return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2);
       
 13890 		};
       
 13891 	}
       
 13892 
       
 13893 	return ElementUtils;
       
 13894 });
       
 13895 
       
 13896 // Included from: js/tinymce/classes/fmt/Preview.js
       
 13897 
       
 13898 /**
       
 13899  * Preview.js
       
 13900  *
       
 13901  * Copyright, Moxiecode Systems AB
       
 13902  * Released under LGPL License.
       
 13903  *
       
 13904  * License: http://www.tinymce.com/license
       
 13905  * Contributing: http://www.tinymce.com/contributing
       
 13906  */
       
 13907 
       
 13908 /**
       
 13909  * Internal class for generating previews styles for formats.
       
 13910  *
       
 13911  * Example:
       
 13912  *  Preview.getCssText(editor, 'bold');
       
 13913  *
       
 13914  * @class tinymce.fmt.Preview
       
 13915  * @private
       
 13916  */
       
 13917 define("tinymce/fmt/Preview", [
       
 13918 	"tinymce/util/Tools"
       
 13919 ], function(Tools) {
       
 13920 	var each = Tools.each;
       
 13921 
       
 13922 	function getCssText(editor, format) {
       
 13923 		var name, previewElm, dom = editor.dom;
       
 13924 		var previewCss = '', parentFontSize, previewStyles;
       
 13925 
       
 13926 		previewStyles = editor.settings.preview_styles;
       
 13927 
       
 13928 		// No preview forced
       
 13929 		if (previewStyles === false) {
       
 13930 			return '';
       
 13931 		}
       
 13932 
       
 13933 		// Default preview
       
 13934 		if (!previewStyles) {
       
 13935 			previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
       
 13936 				'text-transform color background-color border border-radius outline text-shadow';
       
 13937 		}
       
 13938 
       
 13939 		// Removes any variables since these can't be previewed
       
 13940 		function removeVars(val) {
       
 13941 			return val.replace(/%(\w+)/g, '');
       
 13942 		}
       
 13943 
       
 13944 		// Create block/inline element to use for preview
       
 13945 		if (typeof format == "string") {
       
 13946 			format = editor.formatter.get(format);
       
 13947 			if (!format) {
       
 13948 				return;
       
 13949 			}
       
 13950 
       
 13951 			format = format[0];
       
 13952 		}
       
 13953 
       
 13954 		name = format.block || format.inline || 'span';
       
 13955 		previewElm = dom.create(name);
       
 13956 
       
 13957 		// Add format styles to preview element
       
 13958 		each(format.styles, function(value, name) {
       
 13959 			value = removeVars(value);
       
 13960 
       
 13961 			if (value) {
       
 13962 				dom.setStyle(previewElm, name, value);
       
 13963 			}
       
 13964 		});
       
 13965 
       
 13966 		// Add attributes to preview element
       
 13967 		each(format.attributes, function(value, name) {
       
 13968 			value = removeVars(value);
       
 13969 
       
 13970 			if (value) {
       
 13971 				dom.setAttrib(previewElm, name, value);
       
 13972 			}
       
 13973 		});
       
 13974 
       
 13975 		// Add classes to preview element
       
 13976 		each(format.classes, function(value) {
       
 13977 			value = removeVars(value);
       
 13978 
       
 13979 			if (!dom.hasClass(previewElm, value)) {
       
 13980 				dom.addClass(previewElm, value);
       
 13981 			}
       
 13982 		});
       
 13983 
       
 13984 		editor.fire('PreviewFormats');
       
 13985 
       
 13986 		// Add the previewElm outside the visual area
       
 13987 		dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
       
 13988 		editor.getBody().appendChild(previewElm);
       
 13989 
       
 13990 		// Get parent container font size so we can compute px values out of em/% for older IE:s
       
 13991 		parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
       
 13992 		parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
       
 13993 
       
 13994 		each(previewStyles.split(' '), function(name) {
       
 13995 			var value = dom.getStyle(previewElm, name, true);
       
 13996 
       
 13997 			// If background is transparent then check if the body has a background color we can use
       
 13998 			if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
       
 13999 				value = dom.getStyle(editor.getBody(), name, true);
       
 14000 
       
 14001 				// Ignore white since it's the default color, not the nicest fix
       
 14002 				// TODO: Fix this by detecting runtime style
       
 14003 				if (dom.toHex(value).toLowerCase() == '#ffffff') {
       
 14004 					return;
       
 14005 				}
       
 14006 			}
       
 14007 
       
 14008 			if (name == 'color') {
       
 14009 				// Ignore black since it's the default color, not the nicest fix
       
 14010 				// TODO: Fix this by detecting runtime style
       
 14011 				if (dom.toHex(value).toLowerCase() == '#000000') {
       
 14012 					return;
       
 14013 				}
       
 14014 			}
       
 14015 
       
 14016 			// Old IE won't calculate the font size so we need to do that manually
       
 14017 			if (name == 'font-size') {
       
 14018 				if (/em|%$/.test(value)) {
       
 14019 					if (parentFontSize === 0) {
       
 14020 						return;
       
 14021 					}
       
 14022 
       
 14023 					// Convert font size from em/% to px
       
 14024 					value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
       
 14025 					value = (value * parentFontSize) + 'px';
       
 14026 				}
       
 14027 			}
       
 14028 
       
 14029 			if (name == "border" && value) {
       
 14030 				previewCss += 'padding:0 2px;';
       
 14031 			}
       
 14032 
       
 14033 			previewCss += name + ':' + value + ';';
       
 14034 		});
       
 14035 
       
 14036 		editor.fire('AfterPreviewFormats');
       
 14037 
       
 14038 		//previewCss += 'line-height:normal';
       
 14039 
       
 14040 		dom.remove(previewElm);
       
 14041 
       
 14042 		return previewCss;
       
 14043 	}
       
 14044 
       
 14045 	return {
       
 14046 		getCssText: getCssText
       
 14047 	};
       
 14048 });
       
 14049 
       
 14050 // Included from: js/tinymce/classes/Formatter.js
       
 14051 
       
 14052 /**
       
 14053  * Formatter.js
       
 14054  *
       
 14055  * Copyright, Moxiecode Systems AB
       
 14056  * Released under LGPL License.
       
 14057  *
       
 14058  * License: http://www.tinymce.com/license
       
 14059  * Contributing: http://www.tinymce.com/contributing
       
 14060  */
       
 14061 
       
 14062 /**
       
 14063  * Text formatter engine class. This class is used to apply formats like bold, italic, font size
       
 14064  * etc to the current selection or specific nodes. This engine was build to replace the browsers
       
 14065  * default formatting logic for execCommand due to it's inconsistent and buggy behavior.
       
 14066  *
       
 14067  * @class tinymce.Formatter
       
 14068  * @example
       
 14069  *  tinymce.activeEditor.formatter.register('mycustomformat', {
       
 14070  *    inline: 'span',
       
 14071  *    styles: {color: '#ff0000'}
       
 14072  *  });
       
 14073  *
       
 14074  *  tinymce.activeEditor.formatter.apply('mycustomformat');
       
 14075  */
       
 14076 define("tinymce/Formatter", [
       
 14077 	"tinymce/dom/TreeWalker",
       
 14078 	"tinymce/dom/RangeUtils",
       
 14079 	"tinymce/dom/BookmarkManager",
       
 14080 	"tinymce/dom/ElementUtils",
       
 14081 	"tinymce/util/Tools",
       
 14082 	"tinymce/fmt/Preview"
       
 14083 ], function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, Tools, Preview) {
       
 14084 	/**
       
 14085 	 * Constructs a new formatter instance.
       
 14086 	 *
       
 14087 	 * @constructor Formatter
       
 14088 	 * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to.
       
 14089 	 */
       
 14090 	return function(ed) {
       
 14091 		var formats = {},
       
 14092 			dom = ed.dom,
       
 14093 			selection = ed.selection,
       
 14094 			rangeUtils = new RangeUtils(dom),
       
 14095 			isValid = ed.schema.isValidChild,
       
 14096 			isBlock = dom.isBlock,
       
 14097 			forcedRootBlock = ed.settings.forced_root_block,
       
 14098 			nodeIndex = dom.nodeIndex,
       
 14099 			INVISIBLE_CHAR = '\uFEFF',
       
 14100 			MCE_ATTR_RE = /^(src|href|style)$/,
       
 14101 			FALSE = false,
       
 14102 			TRUE = true,
       
 14103 			formatChangeData,
       
 14104 			undef,
       
 14105 			getContentEditable = dom.getContentEditable,
       
 14106 			disableCaretContainer,
       
 14107 			markCaretContainersBogus,
       
 14108 			isBookmarkNode = BookmarkManager.isBookmarkNode;
       
 14109 
       
 14110 		var each = Tools.each,
       
 14111 			grep = Tools.grep,
       
 14112 			walk = Tools.walk,
       
 14113 			extend = Tools.extend;
       
 14114 
       
 14115 		function isTextBlock(name) {
       
 14116 			if (name.nodeType) {
       
 14117 				name = name.nodeName;
       
 14118 			}
       
 14119 
       
 14120 			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
       
 14121 		}
       
 14122 
       
 14123 		function isTableCell(node) {
       
 14124 			return /^(TH|TD)$/.test(node.nodeName);
       
 14125 		}
       
 14126 
       
 14127 		function getParents(node, selector) {
       
 14128 			return dom.getParents(node, selector, dom.getRoot());
       
 14129 		}
       
 14130 
       
 14131 		function isCaretNode(node) {
       
 14132 			return node.nodeType === 1 && node.id === '_mce_caret';
       
 14133 		}
       
 14134 
       
 14135 		function defaultFormats() {
       
 14136 			register({
       
 14137 				valigntop: [
       
 14138 					{selector: 'td,th', styles: {'verticalAlign': 'top'}}
       
 14139 				],
       
 14140 
       
 14141 				valignmiddle: [
       
 14142 					{selector: 'td,th', styles: {'verticalAlign': 'middle'}}
       
 14143 				],
       
 14144 
       
 14145 				valignbottom: [
       
 14146 					{selector: 'td,th', styles: {'verticalAlign': 'bottom'}}
       
 14147 				],
       
 14148 
       
 14149 				alignleft: [
       
 14150 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'left'}, defaultBlock: 'div'},
       
 14151 					{selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
       
 14152 				],
       
 14153 
       
 14154 				aligncenter: [
       
 14155 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'center'}, defaultBlock: 'div'},
       
 14156 					{selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
       
 14157 					{selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
       
 14158 				],
       
 14159 
       
 14160 				alignright: [
       
 14161 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'right'}, defaultBlock: 'div'},
       
 14162 					{selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
       
 14163 				],
       
 14164 
       
 14165 				alignjustify: [
       
 14166 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'justify'}, defaultBlock: 'div'}
       
 14167 				],
       
 14168 
       
 14169 				bold: [
       
 14170 					{inline: 'strong', remove: 'all'},
       
 14171 					{inline: 'span', styles: {fontWeight: 'bold'}},
       
 14172 					{inline: 'b', remove: 'all'}
       
 14173 				],
       
 14174 
       
 14175 				italic: [
       
 14176 					{inline: 'em', remove: 'all'},
       
 14177 					{inline: 'span', styles: {fontStyle: 'italic'}},
       
 14178 					{inline: 'i', remove: 'all'}
       
 14179 				],
       
 14180 
       
 14181 				underline: [
       
 14182 					{inline: 'span', styles: {textDecoration: 'underline'}, exact: true},
       
 14183 					{inline: 'u', remove: 'all'}
       
 14184 				],
       
 14185 
       
 14186 				strikethrough: [
       
 14187 					{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
       
 14188 					{inline: 'strike', remove: 'all'}
       
 14189 				],
       
 14190 
       
 14191 				forecolor: {inline: 'span', styles: {color: '%value'}, links: true, remove_similar: true},
       
 14192 				hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, links: true, remove_similar: true},
       
 14193 				fontname: {inline: 'span', styles: {fontFamily: '%value'}},
       
 14194 				fontsize: {inline: 'span', styles: {fontSize: '%value'}},
       
 14195 				fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
       
 14196 				blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
       
 14197 				subscript: {inline: 'sub'},
       
 14198 				superscript: {inline: 'sup'},
       
 14199 				code: {inline: 'code'},
       
 14200 
       
 14201 				link: {inline: 'a', selector: 'a', remove: 'all', split: true, deep: true,
       
 14202 					onmatch: function() {
       
 14203 						return true;
       
 14204 					},
       
 14205 
       
 14206 					onformat: function(elm, fmt, vars) {
       
 14207 						each(vars, function(value, key) {
       
 14208 							dom.setAttrib(elm, key, value);
       
 14209 						});
       
 14210 					}
       
 14211 				},
       
 14212 
       
 14213 				removeformat: [
       
 14214 					{
       
 14215 						selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins',
       
 14216 						remove: 'all',
       
 14217 						split: true,
       
 14218 						expand: false,
       
 14219 						block_expand: true,
       
 14220 						deep: true
       
 14221 					},
       
 14222 					{selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
       
 14223 					{selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
       
 14224 				]
       
 14225 			});
       
 14226 
       
 14227 			// Register default block formats
       
 14228 			each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) {
       
 14229 				register(name, {block: name, remove: 'all'});
       
 14230 			});
       
 14231 
       
 14232 			// Register user defined formats
       
 14233 			register(ed.settings.formats);
       
 14234 		}
       
 14235 
       
 14236 		function addKeyboardShortcuts() {
       
 14237 			// Add some inline shortcuts
       
 14238 			ed.addShortcut('meta+b', 'bold_desc', 'Bold');
       
 14239 			ed.addShortcut('meta+i', 'italic_desc', 'Italic');
       
 14240 			ed.addShortcut('meta+u', 'underline_desc', 'Underline');
       
 14241 
       
 14242 			// BlockFormat shortcuts keys
       
 14243 			for (var i = 1; i <= 6; i++) {
       
 14244 				ed.addShortcut('access+' + i, '', ['FormatBlock', false, 'h' + i]);
       
 14245 			}
       
 14246 
       
 14247 			ed.addShortcut('access+7', '', ['FormatBlock', false, 'p']);
       
 14248 			ed.addShortcut('access+8', '', ['FormatBlock', false, 'div']);
       
 14249 			ed.addShortcut('access+9', '', ['FormatBlock', false, 'address']);
       
 14250 		}
       
 14251 
       
 14252 		// Public functions
       
 14253 
       
 14254 		/**
       
 14255 		 * Returns the format by name or all formats if no name is specified.
       
 14256 		 *
       
 14257 		 * @method get
       
 14258 		 * @param {String} name Optional name to retrive by.
       
 14259 		 * @return {Array/Object} Array/Object with all registred formats or a specific format.
       
 14260 		 */
       
 14261 		function get(name) {
       
 14262 			return name ? formats[name] : formats;
       
 14263 		}
       
 14264 
       
 14265 		/**
       
 14266 		 * Registers a specific format by name.
       
 14267 		 *
       
 14268 		 * @method register
       
 14269 		 * @param {Object/String} name Name of the format for example "bold".
       
 14270 		 * @param {Object/Array} format Optional format object or array of format variants
       
 14271 		 * can only be omitted if the first arg is an object.
       
 14272 		 */
       
 14273 		function register(name, format) {
       
 14274 			if (name) {
       
 14275 				if (typeof name !== 'string') {
       
 14276 					each(name, function(format, name) {
       
 14277 						register(name, format);
       
 14278 					});
       
 14279 				} else {
       
 14280 					// Force format into array and add it to internal collection
       
 14281 					format = format.length ? format : [format];
       
 14282 
       
 14283 					each(format, function(format) {
       
 14284 						// Set deep to false by default on selector formats this to avoid removing
       
 14285 						// alignment on images inside paragraphs when alignment is changed on paragraphs
       
 14286 						if (format.deep === undef) {
       
 14287 							format.deep = !format.selector;
       
 14288 						}
       
 14289 
       
 14290 						// Default to true
       
 14291 						if (format.split === undef) {
       
 14292 							format.split = !format.selector || format.inline;
       
 14293 						}
       
 14294 
       
 14295 						// Default to true
       
 14296 						if (format.remove === undef && format.selector && !format.inline) {
       
 14297 							format.remove = 'none';
       
 14298 						}
       
 14299 
       
 14300 						// Mark format as a mixed format inline + block level
       
 14301 						if (format.selector && format.inline) {
       
 14302 							format.mixed = true;
       
 14303 							format.block_expand = true;
       
 14304 						}
       
 14305 
       
 14306 						// Split classes if needed
       
 14307 						if (typeof format.classes === 'string') {
       
 14308 							format.classes = format.classes.split(/\s+/);
       
 14309 						}
       
 14310 					});
       
 14311 
       
 14312 					formats[name] = format;
       
 14313 				}
       
 14314 			}
       
 14315 		}
       
 14316 
       
 14317 		/**
       
 14318 		 * Unregister a specific format by name.
       
 14319 		 *
       
 14320 		 * @method unregister
       
 14321 		 * @param {String} name Name of the format for example "bold".
       
 14322 		 */
       
 14323 		function unregister(name) {
       
 14324 			if (name && formats[name]) {
       
 14325 				delete formats[name];
       
 14326 			}
       
 14327 
       
 14328 			return formats;
       
 14329 		}
       
 14330 
       
 14331 		function getTextDecoration(node) {
       
 14332 			var decoration;
       
 14333 
       
 14334 			ed.dom.getParent(node, function(n) {
       
 14335 				decoration = ed.dom.getStyle(n, 'text-decoration');
       
 14336 				return decoration && decoration !== 'none';
       
 14337 			});
       
 14338 
       
 14339 			return decoration;
       
 14340 		}
       
 14341 
       
 14342 		function processUnderlineAndColor(node) {
       
 14343 			var textDecoration;
       
 14344 			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
       
 14345 				textDecoration = getTextDecoration(node.parentNode);
       
 14346 				if (ed.dom.getStyle(node, 'color') && textDecoration) {
       
 14347 					ed.dom.setStyle(node, 'text-decoration', textDecoration);
       
 14348 				} else if (ed.dom.getStyle(node, 'text-decoration') === textDecoration) {
       
 14349 					ed.dom.setStyle(node, 'text-decoration', null);
       
 14350 				}
       
 14351 			}
       
 14352 		}
       
 14353 
       
 14354 		/**
       
 14355 		 * Applies the specified format to the current selection or specified node.
       
 14356 		 *
       
 14357 		 * @method apply
       
 14358 		 * @param {String} name Name of format to apply.
       
 14359 		 * @param {Object} vars Optional list of variables to replace within format before applying it.
       
 14360 		 * @param {Node} node Optional node to apply the format to defaults to current selection.
       
 14361 		 */
       
 14362 		function apply(name, vars, node) {
       
 14363 			var formatList = get(name), format = formatList[0], bookmark, rng, isCollapsed = !node && selection.isCollapsed();
       
 14364 
       
 14365 			function setElementFormat(elm, fmt) {
       
 14366 				fmt = fmt || format;
       
 14367 
       
 14368 				if (elm) {
       
 14369 					if (fmt.onformat) {
       
 14370 						fmt.onformat(elm, fmt, vars, node);
       
 14371 					}
       
 14372 
       
 14373 					each(fmt.styles, function(value, name) {
       
 14374 						dom.setStyle(elm, name, replaceVars(value, vars));
       
 14375 					});
       
 14376 
       
 14377 					// Needed for the WebKit span spam bug
       
 14378 					// TODO: Remove this once WebKit/Blink fixes this
       
 14379 					if (fmt.styles) {
       
 14380 						var styleVal = dom.getAttrib(elm, 'style');
       
 14381 
       
 14382 						if (styleVal) {
       
 14383 							elm.setAttribute('data-mce-style', styleVal);
       
 14384 						}
       
 14385 					}
       
 14386 
       
 14387 					each(fmt.attributes, function(value, name) {
       
 14388 						dom.setAttrib(elm, name, replaceVars(value, vars));
       
 14389 					});
       
 14390 
       
 14391 					each(fmt.classes, function(value) {
       
 14392 						value = replaceVars(value, vars);
       
 14393 
       
 14394 						if (!dom.hasClass(elm, value)) {
       
 14395 							dom.addClass(elm, value);
       
 14396 						}
       
 14397 					});
       
 14398 				}
       
 14399 			}
       
 14400 
       
 14401 			function adjustSelectionToVisibleSelection() {
       
 14402 				function findSelectionEnd(start, end) {
       
 14403 					var walker = new TreeWalker(end);
       
 14404 					for (node = walker.current(); node; node = walker.prev()) {
       
 14405 						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
       
 14406 							return node;
       
 14407 						}
       
 14408 					}
       
 14409 				}
       
 14410 
       
 14411 				// Adjust selection so that a end container with a end offset of zero is not included in the selection
       
 14412 				// as this isn't visible to the user.
       
 14413 				var rng = ed.selection.getRng();
       
 14414 				var start = rng.startContainer;
       
 14415 				var end = rng.endContainer;
       
 14416 
       
 14417 				if (start != end && rng.endOffset === 0) {
       
 14418 					var newEnd = findSelectionEnd(start, end);
       
 14419 					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
       
 14420 
       
 14421 					rng.setEnd(newEnd, endOffset);
       
 14422 				}
       
 14423 
       
 14424 				return rng;
       
 14425 			}
       
 14426 
       
 14427 			function applyRngStyle(rng, bookmark, node_specific) {
       
 14428 				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
       
 14429 
       
 14430 				// Setup wrapper element
       
 14431 				wrapName = format.inline || format.block;
       
 14432 				wrapElm = dom.create(wrapName);
       
 14433 				setElementFormat(wrapElm);
       
 14434 
       
 14435 				rangeUtils.walk(rng, function(nodes) {
       
 14436 					var currentWrapElm;
       
 14437 
       
 14438 					/**
       
 14439 					 * Process a list of nodes wrap them.
       
 14440 					 */
       
 14441 					function process(node) {
       
 14442 						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
       
 14443 
       
 14444 						lastContentEditable = contentEditable;
       
 14445 						nodeName = node.nodeName.toLowerCase();
       
 14446 						parentName = node.parentNode.nodeName.toLowerCase();
       
 14447 
       
 14448 						// Node has a contentEditable value
       
 14449 						if (node.nodeType === 1 && getContentEditable(node)) {
       
 14450 							lastContentEditable = contentEditable;
       
 14451 							contentEditable = getContentEditable(node) === "true";
       
 14452 							hasContentEditableState = true; // We don't want to wrap the container only it's children
       
 14453 						}
       
 14454 
       
 14455 						// Stop wrapping on br elements
       
 14456 						if (isEq(nodeName, 'br')) {
       
 14457 							currentWrapElm = 0;
       
 14458 
       
 14459 							// Remove any br elements when we wrap things
       
 14460 							if (format.block) {
       
 14461 								dom.remove(node);
       
 14462 							}
       
 14463 
       
 14464 							return;
       
 14465 						}
       
 14466 
       
 14467 						// If node is wrapper type
       
 14468 						if (format.wrapper && matchNode(node, name, vars)) {
       
 14469 							currentWrapElm = 0;
       
 14470 							return;
       
 14471 						}
       
 14472 
       
 14473 						// Can we rename the block
       
 14474 						// TODO: Break this if up, too complex
       
 14475 						if (contentEditable && !hasContentEditableState && format.block &&
       
 14476 							!format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) {
       
 14477 							node = dom.rename(node, wrapName);
       
 14478 							setElementFormat(node);
       
 14479 							newWrappers.push(node);
       
 14480 							currentWrapElm = 0;
       
 14481 							return;
       
 14482 						}
       
 14483 
       
 14484 						// Handle selector patterns
       
 14485 						if (format.selector) {
       
 14486 							// Look for matching formats
       
 14487 							each(formatList, function(format) {
       
 14488 								// Check collapsed state if it exists
       
 14489 								if ('collapsed' in format && format.collapsed !== isCollapsed) {
       
 14490 									return;
       
 14491 								}
       
 14492 
       
 14493 								if (dom.is(node, format.selector) && !isCaretNode(node)) {
       
 14494 									setElementFormat(node, format);
       
 14495 									found = true;
       
 14496 								}
       
 14497 							});
       
 14498 
       
 14499 							// Continue processing if a selector match wasn't found and a inline element is defined
       
 14500 							if (!format.inline || found) {
       
 14501 								currentWrapElm = 0;
       
 14502 								return;
       
 14503 							}
       
 14504 						}
       
 14505 
       
 14506 						// Is it valid to wrap this item
       
 14507 						// TODO: Break this if up, too complex
       
 14508 						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
       
 14509 								!(!node_specific && node.nodeType === 3 &&
       
 14510 								node.nodeValue.length === 1 &&
       
 14511 								node.nodeValue.charCodeAt(0) === 65279) &&
       
 14512 								!isCaretNode(node) &&
       
 14513 								(!format.inline || !isBlock(node))) {
       
 14514 							// Start wrapping
       
 14515 							if (!currentWrapElm) {
       
 14516 								// Wrap the node
       
 14517 								currentWrapElm = dom.clone(wrapElm, FALSE);
       
 14518 								node.parentNode.insertBefore(currentWrapElm, node);
       
 14519 								newWrappers.push(currentWrapElm);
       
 14520 							}
       
 14521 
       
 14522 							currentWrapElm.appendChild(node);
       
 14523 						} else {
       
 14524 							// Start a new wrapper for possible children
       
 14525 							currentWrapElm = 0;
       
 14526 
       
 14527 							each(grep(node.childNodes), process);
       
 14528 
       
 14529 							if (hasContentEditableState) {
       
 14530 								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
       
 14531 							}
       
 14532 
       
 14533 							// End the last wrapper
       
 14534 							currentWrapElm = 0;
       
 14535 						}
       
 14536 					}
       
 14537 
       
 14538 					// Process siblings from range
       
 14539 					each(nodes, process);
       
 14540 				});
       
 14541 
       
 14542 				// Apply formats to links as well to get the color of the underline to change as well
       
 14543 				if (format.links === true) {
       
 14544 					each(newWrappers, function(node) {
       
 14545 						function process(node) {
       
 14546 							if (node.nodeName === 'A') {
       
 14547 								setElementFormat(node, format);
       
 14548 							}
       
 14549 
       
 14550 							each(grep(node.childNodes), process);
       
 14551 						}
       
 14552 
       
 14553 						process(node);
       
 14554 					});
       
 14555 				}
       
 14556 
       
 14557 				// Cleanup
       
 14558 				each(newWrappers, function(node) {
       
 14559 					var childCount;
       
 14560 
       
 14561 					function getChildCount(node) {
       
 14562 						var count = 0;
       
 14563 
       
 14564 						each(node.childNodes, function(node) {
       
 14565 							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) {
       
 14566 								count++;
       
 14567 							}
       
 14568 						});
       
 14569 
       
 14570 						return count;
       
 14571 					}
       
 14572 
       
 14573 					function mergeStyles(node) {
       
 14574 						var child, clone;
       
 14575 
       
 14576 						each(node.childNodes, function(node) {
       
 14577 							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
       
 14578 								child = node;
       
 14579 								return FALSE; // break loop
       
 14580 							}
       
 14581 						});
       
 14582 
       
 14583 						// If child was found and of the same type as the current node
       
 14584 						if (child && !isBookmarkNode(child) && matchName(child, format)) {
       
 14585 							clone = dom.clone(child, FALSE);
       
 14586 							setElementFormat(clone);
       
 14587 
       
 14588 							dom.replace(clone, node, TRUE);
       
 14589 							dom.remove(child, 1);
       
 14590 						}
       
 14591 
       
 14592 						return clone || node;
       
 14593 					}
       
 14594 
       
 14595 					childCount = getChildCount(node);
       
 14596 
       
 14597 					// Remove empty nodes but only if there is multiple wrappers and they are not block
       
 14598 					// elements so never remove single <h1></h1> since that would remove the
       
 14599 					// currrent empty block element where the caret is at
       
 14600 					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
       
 14601 						dom.remove(node, 1);
       
 14602 						return;
       
 14603 					}
       
 14604 
       
 14605 					if (format.inline || format.wrapper) {
       
 14606 						// Merges the current node with it's children of similar type to reduce the number of elements
       
 14607 						if (!format.exact && childCount === 1) {
       
 14608 							node = mergeStyles(node);
       
 14609 						}
       
 14610 
       
 14611 						// Remove/merge children
       
 14612 						each(formatList, function(format) {
       
 14613 							// Merge all children of similar type will move styles from child to parent
       
 14614 							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
       
 14615 							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
       
 14616 							each(dom.select(format.inline, node), function(child) {
       
 14617 								if (isBookmarkNode(child)) {
       
 14618 									return;
       
 14619 								}
       
 14620 
       
 14621 								removeFormat(format, vars, child, format.exact ? child : null);
       
 14622 							});
       
 14623 						});
       
 14624 
       
 14625 						// Remove child if direct parent is of same type
       
 14626 						if (matchNode(node.parentNode, name, vars)) {
       
 14627 							dom.remove(node, 1);
       
 14628 							node = 0;
       
 14629 							return TRUE;
       
 14630 						}
       
 14631 
       
 14632 						// Look for parent with similar style format
       
 14633 						if (format.merge_with_parents) {
       
 14634 							dom.getParent(node.parentNode, function(parent) {
       
 14635 								if (matchNode(parent, name, vars)) {
       
 14636 									dom.remove(node, 1);
       
 14637 									node = 0;
       
 14638 									return TRUE;
       
 14639 								}
       
 14640 							});
       
 14641 						}
       
 14642 
       
 14643 						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
       
 14644 						if (node && format.merge_siblings !== false) {
       
 14645 							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
       
 14646 							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
       
 14647 						}
       
 14648 					}
       
 14649 				});
       
 14650 			}
       
 14651 
       
 14652 			if (format) {
       
 14653 				if (node) {
       
 14654 					if (node.nodeType) {
       
 14655 						rng = dom.createRng();
       
 14656 						rng.setStartBefore(node);
       
 14657 						rng.setEndAfter(node);
       
 14658 						applyRngStyle(expandRng(rng, formatList), null, true);
       
 14659 					} else {
       
 14660 						applyRngStyle(node, null, true);
       
 14661 					}
       
 14662 				} else {
       
 14663 					if (!isCollapsed || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
       
 14664 						// Obtain selection node before selection is unselected by applyRngStyle()
       
 14665 						var curSelNode = ed.selection.getNode();
       
 14666 
       
 14667 						// If the formats have a default block and we can't find a parent block then
       
 14668 						// start wrapping it with a DIV this is for forced_root_blocks: false
       
 14669 						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
       
 14670 						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
       
 14671 							apply(formatList[0].defaultBlock);
       
 14672 						}
       
 14673 
       
 14674 						// Apply formatting to selection
       
 14675 						ed.selection.setRng(adjustSelectionToVisibleSelection());
       
 14676 						bookmark = selection.getBookmark();
       
 14677 						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
       
 14678 
       
 14679 						// Colored nodes should be underlined so that the color of the underline matches the text color.
       
 14680 						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
       
 14681 							walk(curSelNode, processUnderlineAndColor, 'childNodes');
       
 14682 							processUnderlineAndColor(curSelNode);
       
 14683 						}
       
 14684 
       
 14685 						selection.moveToBookmark(bookmark);
       
 14686 						moveStart(selection.getRng(TRUE));
       
 14687 						ed.nodeChanged();
       
 14688 					} else {
       
 14689 						performCaretAction('apply', name, vars);
       
 14690 					}
       
 14691 				}
       
 14692 			}
       
 14693 		}
       
 14694 
       
 14695 		/**
       
 14696 		 * Removes the specified format from the current selection or specified node.
       
 14697 		 *
       
 14698 		 * @method remove
       
 14699 		 * @param {String} name Name of format to remove.
       
 14700 		 * @param {Object} vars Optional list of variables to replace within format before removing it.
       
 14701 		 * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
       
 14702 		 */
       
 14703 		function remove(name, vars, node, similar) {
       
 14704 			var formatList = get(name), format = formatList[0], bookmark, rng, contentEditable = true;
       
 14705 
       
 14706 			// Merges the styles for each node
       
 14707 			function process(node) {
       
 14708 				var children, i, l, lastContentEditable, hasContentEditableState;
       
 14709 
       
 14710 				// Node has a contentEditable value
       
 14711 				if (node.nodeType === 1 && getContentEditable(node)) {
       
 14712 					lastContentEditable = contentEditable;
       
 14713 					contentEditable = getContentEditable(node) === "true";
       
 14714 					hasContentEditableState = true; // We don't want to wrap the container only it's children
       
 14715 				}
       
 14716 
       
 14717 				// Grab the children first since the nodelist might be changed
       
 14718 				children = grep(node.childNodes);
       
 14719 
       
 14720 				// Process current node
       
 14721 				if (contentEditable && !hasContentEditableState) {
       
 14722 					for (i = 0, l = formatList.length; i < l; i++) {
       
 14723 						if (removeFormat(formatList[i], vars, node, node)) {
       
 14724 							break;
       
 14725 						}
       
 14726 					}
       
 14727 				}
       
 14728 
       
 14729 				// Process the children
       
 14730 				if (format.deep) {
       
 14731 					if (children.length) {
       
 14732 						for (i = 0, l = children.length; i < l; i++) {
       
 14733 							process(children[i]);
       
 14734 						}
       
 14735 
       
 14736 						if (hasContentEditableState) {
       
 14737 							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
       
 14738 						}
       
 14739 					}
       
 14740 				}
       
 14741 			}
       
 14742 
       
 14743 			function findFormatRoot(container) {
       
 14744 				var formatRoot;
       
 14745 
       
 14746 				// Find format root
       
 14747 				each(getParents(container.parentNode).reverse(), function(parent) {
       
 14748 					var format;
       
 14749 
       
 14750 					// Find format root element
       
 14751 					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
       
 14752 						// Is the node matching the format we are looking for
       
 14753 						format = matchNode(parent, name, vars, similar);
       
 14754 						if (format && format.split !== false) {
       
 14755 							formatRoot = parent;
       
 14756 						}
       
 14757 					}
       
 14758 				});
       
 14759 
       
 14760 				return formatRoot;
       
 14761 			}
       
 14762 
       
 14763 			function wrapAndSplit(formatRoot, container, target, split) {
       
 14764 				var parent, clone, lastClone, firstClone, i, formatRootParent;
       
 14765 
       
 14766 				// Format root found then clone formats and split it
       
 14767 				if (formatRoot) {
       
 14768 					formatRootParent = formatRoot.parentNode;
       
 14769 
       
 14770 					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
       
 14771 						clone = dom.clone(parent, FALSE);
       
 14772 
       
 14773 						for (i = 0; i < formatList.length; i++) {
       
 14774 							if (removeFormat(formatList[i], vars, clone, clone)) {
       
 14775 								clone = 0;
       
 14776 								break;
       
 14777 							}
       
 14778 						}
       
 14779 
       
 14780 						// Build wrapper node
       
 14781 						if (clone) {
       
 14782 							if (lastClone) {
       
 14783 								clone.appendChild(lastClone);
       
 14784 							}
       
 14785 
       
 14786 							if (!firstClone) {
       
 14787 								firstClone = clone;
       
 14788 							}
       
 14789 
       
 14790 							lastClone = clone;
       
 14791 						}
       
 14792 					}
       
 14793 
       
 14794 					// Never split block elements if the format is mixed
       
 14795 					if (split && (!format.mixed || !isBlock(formatRoot))) {
       
 14796 						container = dom.split(formatRoot, container);
       
 14797 					}
       
 14798 
       
 14799 					// Wrap container in cloned formats
       
 14800 					if (lastClone) {
       
 14801 						target.parentNode.insertBefore(lastClone, target);
       
 14802 						firstClone.appendChild(target);
       
 14803 					}
       
 14804 				}
       
 14805 
       
 14806 				return container;
       
 14807 			}
       
 14808 
       
 14809 			function splitToFormatRoot(container) {
       
 14810 				return wrapAndSplit(findFormatRoot(container), container, container, true);
       
 14811 			}
       
 14812 
       
 14813 			function unwrap(start) {
       
 14814 				var node = dom.get(start ? '_start' : '_end'),
       
 14815 					out = node[start ? 'firstChild' : 'lastChild'];
       
 14816 
       
 14817 				// If the end is placed within the start the result will be removed
       
 14818 				// So this checks if the out node is a bookmark node if it is it
       
 14819 				// checks for another more suitable node
       
 14820 				if (isBookmarkNode(out)) {
       
 14821 					out = out[start ? 'firstChild' : 'lastChild'];
       
 14822 				}
       
 14823 
       
 14824 				// Since dom.remove removes empty text nodes then we need to try to find a better node
       
 14825 				if (out.nodeType == 3 && out.data.length === 0) {
       
 14826 					out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling;
       
 14827 				}
       
 14828 
       
 14829 				dom.remove(node, true);
       
 14830 
       
 14831 				return out;
       
 14832 			}
       
 14833 
       
 14834 			function removeRngStyle(rng) {
       
 14835 				var startContainer, endContainer;
       
 14836 				var commonAncestorContainer = rng.commonAncestorContainer;
       
 14837 
       
 14838 				rng = expandRng(rng, formatList, TRUE);
       
 14839 
       
 14840 				if (format.split) {
       
 14841 					startContainer = getContainer(rng, TRUE);
       
 14842 					endContainer = getContainer(rng);
       
 14843 
       
 14844 					if (startContainer != endContainer) {
       
 14845 						// WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN
       
 14846 						// so let's see if we can use the first child instead
       
 14847 						// This will happen if you triple click a table cell and use remove formatting
       
 14848 						if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
       
 14849 							if (startContainer.nodeName == "TR") {
       
 14850 								startContainer = startContainer.firstChild.firstChild || startContainer;
       
 14851 							} else {
       
 14852 								startContainer = startContainer.firstChild || startContainer;
       
 14853 							}
       
 14854 						}
       
 14855 
       
 14856 						// Try to adjust endContainer as well if cells on the same row were selected - bug #6410
       
 14857 						if (commonAncestorContainer &&
       
 14858 							/^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) &&
       
 14859 							isTableCell(endContainer) && endContainer.firstChild) {
       
 14860 							endContainer = endContainer.firstChild || endContainer;
       
 14861 						}
       
 14862 
       
 14863 						if (dom.isChildOf(startContainer, endContainer) && !isTableCell(startContainer) && !isTableCell(endContainer)) {
       
 14864 							startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
       
 14865 							splitToFormatRoot(startContainer);
       
 14866 							startContainer = unwrap(TRUE);
       
 14867 							return;
       
 14868 						} else {
       
 14869 							// Wrap start/end nodes in span element since these might be cloned/moved
       
 14870 							startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
       
 14871 							endContainer = wrap(endContainer, 'span', {id: '_end', 'data-mce-type': 'bookmark'});
       
 14872 
       
 14873 							// Split start/end
       
 14874 							splitToFormatRoot(startContainer);
       
 14875 							splitToFormatRoot(endContainer);
       
 14876 
       
 14877 							// Unwrap start/end to get real elements again
       
 14878 							startContainer = unwrap(TRUE);
       
 14879 							endContainer = unwrap();
       
 14880 						}
       
 14881 					} else {
       
 14882 						startContainer = endContainer = splitToFormatRoot(startContainer);
       
 14883 					}
       
 14884 
       
 14885 					// Update range positions since they might have changed after the split operations
       
 14886 					rng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer;
       
 14887 					rng.startOffset = nodeIndex(startContainer);
       
 14888 					rng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer;
       
 14889 					rng.endOffset = nodeIndex(endContainer) + 1;
       
 14890 				}
       
 14891 
       
 14892 				// Remove items between start/end
       
 14893 				rangeUtils.walk(rng, function(nodes) {
       
 14894 					each(nodes, function(node) {
       
 14895 						process(node);
       
 14896 
       
 14897 						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
       
 14898 						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' &&
       
 14899 							node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
       
 14900 							removeFormat({
       
 14901 								'deep': false,
       
 14902 								'exact': true,
       
 14903 								'inline': 'span',
       
 14904 								'styles': {
       
 14905 									'textDecoration': 'underline'
       
 14906 								}
       
 14907 							}, null, node);
       
 14908 						}
       
 14909 					});
       
 14910 				});
       
 14911 			}
       
 14912 
       
 14913 			// Handle node
       
 14914 			if (node) {
       
 14915 				if (node.nodeType) {
       
 14916 					rng = dom.createRng();
       
 14917 					rng.setStartBefore(node);
       
 14918 					rng.setEndAfter(node);
       
 14919 					removeRngStyle(rng);
       
 14920 				} else {
       
 14921 					removeRngStyle(node);
       
 14922 				}
       
 14923 
       
 14924 				return;
       
 14925 			}
       
 14926 
       
 14927 			if (!selection.isCollapsed() || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
       
 14928 				bookmark = selection.getBookmark();
       
 14929 				removeRngStyle(selection.getRng(TRUE));
       
 14930 				selection.moveToBookmark(bookmark);
       
 14931 
       
 14932 				// Check if start element still has formatting then we are at: "<b>text|</b>text"
       
 14933 				// and need to move the start into the next text node
       
 14934 				if (format.inline && match(name, vars, selection.getStart())) {
       
 14935 					moveStart(selection.getRng(true));
       
 14936 				}
       
 14937 
       
 14938 				ed.nodeChanged();
       
 14939 			} else {
       
 14940 				performCaretAction('remove', name, vars, similar);
       
 14941 			}
       
 14942 		}
       
 14943 
       
 14944 		/**
       
 14945 		 * Toggles the specified format on/off.
       
 14946 		 *
       
 14947 		 * @method toggle
       
 14948 		 * @param {String} name Name of format to apply/remove.
       
 14949 		 * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
       
 14950 		 * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
       
 14951 		 */
       
 14952 		function toggle(name, vars, node) {
       
 14953 			var fmt = get(name);
       
 14954 
       
 14955 			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
       
 14956 				remove(name, vars, node);
       
 14957 			} else {
       
 14958 				apply(name, vars, node);
       
 14959 			}
       
 14960 		}
       
 14961 
       
 14962 		/**
       
 14963 		 * Return true/false if the specified node has the specified format.
       
 14964 		 *
       
 14965 		 * @method matchNode
       
 14966 		 * @param {Node} node Node to check the format on.
       
 14967 		 * @param {String} name Format name to check.
       
 14968 		 * @param {Object} vars Optional list of variables to replace before checking it.
       
 14969 		 * @param {Boolean} similar Match format that has similar properties.
       
 14970 		 * @return {Object} Returns the format object it matches or undefined if it doesn't match.
       
 14971 		 */
       
 14972 		function matchNode(node, name, vars, similar) {
       
 14973 			var formatList = get(name), format, i, classes;
       
 14974 
       
 14975 			function matchItems(node, format, item_name) {
       
 14976 				var key, value, items = format[item_name], i;
       
 14977 
       
 14978 				// Custom match
       
 14979 				if (format.onmatch) {
       
 14980 					return format.onmatch(node, format, item_name);
       
 14981 				}
       
 14982 
       
 14983 				// Check all items
       
 14984 				if (items) {
       
 14985 					// Non indexed object
       
 14986 					if (items.length === undef) {
       
 14987 						for (key in items) {
       
 14988 							if (items.hasOwnProperty(key)) {
       
 14989 								if (item_name === 'attributes') {
       
 14990 									value = dom.getAttrib(node, key);
       
 14991 								} else {
       
 14992 									value = getStyle(node, key);
       
 14993 								}
       
 14994 
       
 14995 								if (similar && !value && !format.exact) {
       
 14996 									return;
       
 14997 								}
       
 14998 
       
 14999 								if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) {
       
 15000 									return;
       
 15001 								}
       
 15002 							}
       
 15003 						}
       
 15004 					} else {
       
 15005 						// Only one match needed for indexed arrays
       
 15006 						for (i = 0; i < items.length; i++) {
       
 15007 							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) {
       
 15008 								return format;
       
 15009 							}
       
 15010 						}
       
 15011 					}
       
 15012 				}
       
 15013 
       
 15014 				return format;
       
 15015 			}
       
 15016 
       
 15017 			if (formatList && node) {
       
 15018 				// Check each format in list
       
 15019 				for (i = 0; i < formatList.length; i++) {
       
 15020 					format = formatList[i];
       
 15021 
       
 15022 					// Name name, attributes, styles and classes
       
 15023 					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
       
 15024 						// Match classes
       
 15025 						if ((classes = format.classes)) {
       
 15026 							for (i = 0; i < classes.length; i++) {
       
 15027 								if (!dom.hasClass(node, classes[i])) {
       
 15028 									return;
       
 15029 								}
       
 15030 							}
       
 15031 						}
       
 15032 
       
 15033 						return format;
       
 15034 					}
       
 15035 				}
       
 15036 			}
       
 15037 		}
       
 15038 
       
 15039 		/**
       
 15040 		 * Matches the current selection or specified node against the specified format name.
       
 15041 		 *
       
 15042 		 * @method match
       
 15043 		 * @param {String} name Name of format to match.
       
 15044 		 * @param {Object} vars Optional list of variables to replace before checking it.
       
 15045 		 * @param {Node} node Optional node to check.
       
 15046 		 * @return {boolean} true/false if the specified selection/node matches the format.
       
 15047 		 */
       
 15048 		function match(name, vars, node) {
       
 15049 			var startNode;
       
 15050 
       
 15051 			function matchParents(node) {
       
 15052 				var root = dom.getRoot();
       
 15053 
       
 15054 				if (node === root) {
       
 15055 					return false;
       
 15056 				}
       
 15057 
       
 15058 				// Find first node with similar format settings
       
 15059 				node = dom.getParent(node, function(node) {
       
 15060 					return node.parentNode === root || !!matchNode(node, name, vars, true);
       
 15061 				});
       
 15062 
       
 15063 				// Do an exact check on the similar format element
       
 15064 				return matchNode(node, name, vars);
       
 15065 			}
       
 15066 
       
 15067 			// Check specified node
       
 15068 			if (node) {
       
 15069 				return matchParents(node);
       
 15070 			}
       
 15071 
       
 15072 			// Check selected node
       
 15073 			node = selection.getNode();
       
 15074 			if (matchParents(node)) {
       
 15075 				return TRUE;
       
 15076 			}
       
 15077 
       
 15078 			// Check start node if it's different
       
 15079 			startNode = selection.getStart();
       
 15080 			if (startNode != node) {
       
 15081 				if (matchParents(startNode)) {
       
 15082 					return TRUE;
       
 15083 				}
       
 15084 			}
       
 15085 
       
 15086 			return FALSE;
       
 15087 		}
       
 15088 
       
 15089 		/**
       
 15090 		 * Matches the current selection against the array of formats and returns a new array with matching formats.
       
 15091 		 *
       
 15092 		 * @method matchAll
       
 15093 		 * @param {Array} names Name of format to match.
       
 15094 		 * @param {Object} vars Optional list of variables to replace before checking it.
       
 15095 		 * @return {Array} Array with matched formats.
       
 15096 		 */
       
 15097 		function matchAll(names, vars) {
       
 15098 			var startElement, matchedFormatNames = [], checkedMap = {};
       
 15099 
       
 15100 			// Check start of selection for formats
       
 15101 			startElement = selection.getStart();
       
 15102 			dom.getParent(startElement, function(node) {
       
 15103 				var i, name;
       
 15104 
       
 15105 				for (i = 0; i < names.length; i++) {
       
 15106 					name = names[i];
       
 15107 
       
 15108 					if (!checkedMap[name] && matchNode(node, name, vars)) {
       
 15109 						checkedMap[name] = true;
       
 15110 						matchedFormatNames.push(name);
       
 15111 					}
       
 15112 				}
       
 15113 			}, dom.getRoot());
       
 15114 
       
 15115 			return matchedFormatNames;
       
 15116 		}
       
 15117 
       
 15118 		/**
       
 15119 		 * Returns true/false if the specified format can be applied to the current selection or not. It
       
 15120 		 * will currently only check the state for selector formats, it returns true on all other format types.
       
 15121 		 *
       
 15122 		 * @method canApply
       
 15123 		 * @param {String} name Name of format to check.
       
 15124 		 * @return {boolean} true/false if the specified format can be applied to the current selection/node.
       
 15125 		 */
       
 15126 		function canApply(name) {
       
 15127 			var formatList = get(name), startNode, parents, i, x, selector;
       
 15128 
       
 15129 			if (formatList) {
       
 15130 				startNode = selection.getStart();
       
 15131 				parents = getParents(startNode);
       
 15132 
       
 15133 				for (x = formatList.length - 1; x >= 0; x--) {
       
 15134 					selector = formatList[x].selector;
       
 15135 
       
 15136 					// Format is not selector based then always return TRUE
       
 15137 					// Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
       
 15138 					if (!selector || formatList[x].defaultBlock) {
       
 15139 						return TRUE;
       
 15140 					}
       
 15141 
       
 15142 					for (i = parents.length - 1; i >= 0; i--) {
       
 15143 						if (dom.is(parents[i], selector)) {
       
 15144 							return TRUE;
       
 15145 						}
       
 15146 					}
       
 15147 				}
       
 15148 			}
       
 15149 
       
 15150 			return FALSE;
       
 15151 		}
       
 15152 
       
 15153 		/**
       
 15154 		 * Executes the specified callback when the current selection matches the formats or not.
       
 15155 		 *
       
 15156 		 * @method formatChanged
       
 15157 		 * @param {String} formats Comma separated list of formats to check for.
       
 15158 		 * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
       
 15159 		 * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
       
 15160 		 */
       
 15161 		function formatChanged(formats, callback, similar) {
       
 15162 			var currentFormats;
       
 15163 
       
 15164 			// Setup format node change logic
       
 15165 			if (!formatChangeData) {
       
 15166 				formatChangeData = {};
       
 15167 				currentFormats = {};
       
 15168 
       
 15169 				ed.on('NodeChange', function(e) {
       
 15170 					var parents = getParents(e.element), matchedFormats = {};
       
 15171 
       
 15172 					// Ignore bogus nodes like the <a> tag created by moveStart()
       
 15173 					parents = Tools.grep(parents, function(node) {
       
 15174 						return node.nodeType == 1 && !node.getAttribute('data-mce-bogus');
       
 15175 					});
       
 15176 
       
 15177 					// Check for new formats
       
 15178 					each(formatChangeData, function(callbacks, format) {
       
 15179 						each(parents, function(node) {
       
 15180 							if (matchNode(node, format, {}, callbacks.similar)) {
       
 15181 								if (!currentFormats[format]) {
       
 15182 									// Execute callbacks
       
 15183 									each(callbacks, function(callback) {
       
 15184 										callback(true, {node: node, format: format, parents: parents});
       
 15185 									});
       
 15186 
       
 15187 									currentFormats[format] = callbacks;
       
 15188 								}
       
 15189 
       
 15190 								matchedFormats[format] = callbacks;
       
 15191 								return false;
       
 15192 							}
       
 15193 						});
       
 15194 					});
       
 15195 
       
 15196 					// Check if current formats still match
       
 15197 					each(currentFormats, function(callbacks, format) {
       
 15198 						if (!matchedFormats[format]) {
       
 15199 							delete currentFormats[format];
       
 15200 
       
 15201 							each(callbacks, function(callback) {
       
 15202 								callback(false, {node: e.element, format: format, parents: parents});
       
 15203 							});
       
 15204 						}
       
 15205 					});
       
 15206 				});
       
 15207 			}
       
 15208 
       
 15209 			// Add format listeners
       
 15210 			each(formats.split(','), function(format) {
       
 15211 				if (!formatChangeData[format]) {
       
 15212 					formatChangeData[format] = [];
       
 15213 					formatChangeData[format].similar = similar;
       
 15214 				}
       
 15215 
       
 15216 				formatChangeData[format].push(callback);
       
 15217 			});
       
 15218 
       
 15219 			return this;
       
 15220 		}
       
 15221 
       
 15222 		/**
       
 15223 		 * Returns a preview css text for the specified format.
       
 15224 		 *
       
 15225 		 * @method getCssText
       
 15226 		 * @param {String/Object} format Format to generate preview css text for.
       
 15227 		 * @return {String} Css text for the specified format.
       
 15228 		 * @example
       
 15229 		 * var cssText1 = editor.formatter.getCssText('bold');
       
 15230 		 * var cssText2 = editor.formatter.getCssText({inline: 'b'});
       
 15231 		 */
       
 15232 		function getCssText(format) {
       
 15233 			return Preview.getCssText(ed, format);
       
 15234 		}
       
 15235 
       
 15236 		// Expose to public
       
 15237 		extend(this, {
       
 15238 			get: get,
       
 15239 			register: register,
       
 15240 			unregister: unregister,
       
 15241 			apply: apply,
       
 15242 			remove: remove,
       
 15243 			toggle: toggle,
       
 15244 			match: match,
       
 15245 			matchAll: matchAll,
       
 15246 			matchNode: matchNode,
       
 15247 			canApply: canApply,
       
 15248 			formatChanged: formatChanged,
       
 15249 			getCssText: getCssText
       
 15250 		});
       
 15251 
       
 15252 		// Initialize
       
 15253 		defaultFormats();
       
 15254 		addKeyboardShortcuts();
       
 15255 		ed.on('BeforeGetContent', function(e) {
       
 15256 			if (markCaretContainersBogus && e.format != 'raw') {
       
 15257 				markCaretContainersBogus();
       
 15258 			}
       
 15259 		});
       
 15260 		ed.on('mouseup keydown', function(e) {
       
 15261 			if (disableCaretContainer) {
       
 15262 				disableCaretContainer(e);
       
 15263 			}
       
 15264 		});
       
 15265 
       
 15266 		// Private functions
       
 15267 
       
 15268 		/**
       
 15269 		 * Checks if the specified nodes name matches the format inline/block or selector.
       
 15270 		 *
       
 15271 		 * @private
       
 15272 		 * @param {Node} node Node to match against the specified format.
       
 15273 		 * @param {Object} format Format object o match with.
       
 15274 		 * @return {boolean} true/false if the format matches.
       
 15275 		 */
       
 15276 		function matchName(node, format) {
       
 15277 			// Check for inline match
       
 15278 			if (isEq(node, format.inline)) {
       
 15279 				return TRUE;
       
 15280 			}
       
 15281 
       
 15282 			// Check for block match
       
 15283 			if (isEq(node, format.block)) {
       
 15284 				return TRUE;
       
 15285 			}
       
 15286 
       
 15287 			// Check for selector match
       
 15288 			if (format.selector) {
       
 15289 				return node.nodeType == 1 && dom.is(node, format.selector);
       
 15290 			}
       
 15291 		}
       
 15292 
       
 15293 		/**
       
 15294 		 * Compares two string/nodes regardless of their case.
       
 15295 		 *
       
 15296 		 * @private
       
 15297 		 * @param {String/Node} Node or string to compare.
       
 15298 		 * @param {String/Node} Node or string to compare.
       
 15299 		 * @return {boolean} True/false if they match.
       
 15300 		 */
       
 15301 		function isEq(str1, str2) {
       
 15302 			str1 = str1 || '';
       
 15303 			str2 = str2 || '';
       
 15304 
       
 15305 			str1 = '' + (str1.nodeName || str1);
       
 15306 			str2 = '' + (str2.nodeName || str2);
       
 15307 
       
 15308 			return str1.toLowerCase() == str2.toLowerCase();
       
 15309 		}
       
 15310 
       
 15311 		/**
       
 15312 		 * Returns the style by name on the specified node. This method modifies the style
       
 15313 		 * contents to make it more easy to match. This will resolve a few browser issues.
       
 15314 		 *
       
 15315 		 * @private
       
 15316 		 * @param {Node} node to get style from.
       
 15317 		 * @param {String} name Style name to get.
       
 15318 		 * @return {String} Style item value.
       
 15319 		 */
       
 15320 		function getStyle(node, name) {
       
 15321 			return normalizeStyleValue(dom.getStyle(node, name), name);
       
 15322 		}
       
 15323 
       
 15324 		/**
       
 15325 		 * Normalize style value by name. This method modifies the style contents
       
 15326 		 * to make it more easy to match. This will resolve a few browser issues.
       
 15327 		 *
       
 15328 		 * @private
       
 15329 		 * @param {Node} node to get style from.
       
 15330 		 * @param {String} name Style name to get.
       
 15331 		 * @return {String} Style item value.
       
 15332 		 */
       
 15333 		function normalizeStyleValue(value, name) {
       
 15334 			// Force the format to hex
       
 15335 			if (name == 'color' || name == 'backgroundColor') {
       
 15336 				value = dom.toHex(value);
       
 15337 			}
       
 15338 
       
 15339 			// Opera will return bold as 700
       
 15340 			if (name == 'fontWeight' && value == 700) {
       
 15341 				value = 'bold';
       
 15342 			}
       
 15343 
       
 15344 			// Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font"
       
 15345 			if (name == 'fontFamily') {
       
 15346 				value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
       
 15347 			}
       
 15348 
       
 15349 			return '' + value;
       
 15350 		}
       
 15351 
       
 15352 		/**
       
 15353 		 * Replaces variables in the value. The variable format is %var.
       
 15354 		 *
       
 15355 		 * @private
       
 15356 		 * @param {String} value Value to replace variables in.
       
 15357 		 * @param {Object} vars Name/value array with variables to replace.
       
 15358 		 * @return {String} New value with replaced variables.
       
 15359 		 */
       
 15360 		function replaceVars(value, vars) {
       
 15361 			if (typeof value != "string") {
       
 15362 				value = value(vars);
       
 15363 			} else if (vars) {
       
 15364 				value = value.replace(/%(\w+)/g, function(str, name) {
       
 15365 					return vars[name] || str;
       
 15366 				});
       
 15367 			}
       
 15368 
       
 15369 			return value;
       
 15370 		}
       
 15371 
       
 15372 		function isWhiteSpaceNode(node) {
       
 15373 			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
       
 15374 		}
       
 15375 
       
 15376 		function wrap(node, name, attrs) {
       
 15377 			var wrapper = dom.create(name, attrs);
       
 15378 
       
 15379 			node.parentNode.insertBefore(wrapper, node);
       
 15380 			wrapper.appendChild(node);
       
 15381 
       
 15382 			return wrapper;
       
 15383 		}
       
 15384 
       
 15385 		/**
       
 15386 		 * Expands the specified range like object to depending on format.
       
 15387 		 *
       
 15388 		 * For example on block formats it will move the start/end position
       
 15389 		 * to the beginning of the current block.
       
 15390 		 *
       
 15391 		 * @private
       
 15392 		 * @param {Object} rng Range like object.
       
 15393 		 * @param {Array} formats Array with formats to expand by.
       
 15394 		 * @return {Object} Expanded range like object.
       
 15395 		 */
       
 15396 		function expandRng(rng, format, remove) {
       
 15397 			var lastIdx, leaf, endPoint,
       
 15398 				startContainer = rng.startContainer,
       
 15399 				startOffset = rng.startOffset,
       
 15400 				endContainer = rng.endContainer,
       
 15401 				endOffset = rng.endOffset;
       
 15402 
       
 15403 			// This function walks up the tree if there is no siblings before/after the node
       
 15404 			function findParentContainer(start) {
       
 15405 				var container, parent, sibling, siblingName, root;
       
 15406 
       
 15407 				container = parent = start ? startContainer : endContainer;
       
 15408 				siblingName = start ? 'previousSibling' : 'nextSibling';
       
 15409 				root = dom.getRoot();
       
 15410 
       
 15411 				function isBogusBr(node) {
       
 15412 					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
       
 15413 				}
       
 15414 
       
 15415 				// If it's a text node and the offset is inside the text
       
 15416 				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
       
 15417 					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
       
 15418 						return container;
       
 15419 					}
       
 15420 				}
       
 15421 
       
 15422 				/*eslint no-constant-condition:0 */
       
 15423 				while (true) {
       
 15424 					// Stop expanding on block elements
       
 15425 					if (!format[0].block_expand && isBlock(parent)) {
       
 15426 						return parent;
       
 15427 					}
       
 15428 
       
 15429 					// Walk left/right
       
 15430 					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
       
 15431 						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
       
 15432 							return parent;
       
 15433 						}
       
 15434 					}
       
 15435 
       
 15436 					// Check if we can move up are we at root level or body level
       
 15437 					if (parent.parentNode == root) {
       
 15438 						container = parent;
       
 15439 						break;
       
 15440 					}
       
 15441 
       
 15442 					parent = parent.parentNode;
       
 15443 				}
       
 15444 
       
 15445 				return container;
       
 15446 			}
       
 15447 
       
 15448 			// This function walks down the tree to find the leaf at the selection.
       
 15449 			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
       
 15450 			function findLeaf(node, offset) {
       
 15451 				if (offset === undef) {
       
 15452 					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
       
 15453 				}
       
 15454 
       
 15455 				while (node && node.hasChildNodes()) {
       
 15456 					node = node.childNodes[offset];
       
 15457 					if (node) {
       
 15458 						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
       
 15459 					}
       
 15460 				}
       
 15461 				return {node: node, offset: offset};
       
 15462 			}
       
 15463 
       
 15464 			// If index based start position then resolve it
       
 15465 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
       
 15466 				lastIdx = startContainer.childNodes.length - 1;
       
 15467 				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
       
 15468 
       
 15469 				if (startContainer.nodeType == 3) {
       
 15470 					startOffset = 0;
       
 15471 				}
       
 15472 			}
       
 15473 
       
 15474 			// If index based end position then resolve it
       
 15475 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
       
 15476 				lastIdx = endContainer.childNodes.length - 1;
       
 15477 				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
       
 15478 
       
 15479 				if (endContainer.nodeType == 3) {
       
 15480 					endOffset = endContainer.nodeValue.length;
       
 15481 				}
       
 15482 			}
       
 15483 
       
 15484 			// Expands the node to the closes contentEditable false element if it exists
       
 15485 			function findParentContentEditable(node) {
       
 15486 				var parent = node;
       
 15487 
       
 15488 				while (parent) {
       
 15489 					if (parent.nodeType === 1 && getContentEditable(parent)) {
       
 15490 						return getContentEditable(parent) === "false" ? parent : node;
       
 15491 					}
       
 15492 
       
 15493 					parent = parent.parentNode;
       
 15494 				}
       
 15495 
       
 15496 				return node;
       
 15497 			}
       
 15498 
       
 15499 			function findWordEndPoint(container, offset, start) {
       
 15500 				var walker, node, pos, lastTextNode;
       
 15501 
       
 15502 				function findSpace(node, offset) {
       
 15503 					var pos, pos2, str = node.nodeValue;
       
 15504 
       
 15505 					if (typeof offset == "undefined") {
       
 15506 						offset = start ? str.length : 0;
       
 15507 					}
       
 15508 
       
 15509 					if (start) {
       
 15510 						pos = str.lastIndexOf(' ', offset);
       
 15511 						pos2 = str.lastIndexOf('\u00a0', offset);
       
 15512 						pos = pos > pos2 ? pos : pos2;
       
 15513 
       
 15514 						// Include the space on remove to avoid tag soup
       
 15515 						if (pos !== -1 && !remove) {
       
 15516 							pos++;
       
 15517 						}
       
 15518 					} else {
       
 15519 						pos = str.indexOf(' ', offset);
       
 15520 						pos2 = str.indexOf('\u00a0', offset);
       
 15521 						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
       
 15522 					}
       
 15523 
       
 15524 					return pos;
       
 15525 				}
       
 15526 
       
 15527 				if (container.nodeType === 3) {
       
 15528 					pos = findSpace(container, offset);
       
 15529 
       
 15530 					if (pos !== -1) {
       
 15531 						return {container: container, offset: pos};
       
 15532 					}
       
 15533 
       
 15534 					lastTextNode = container;
       
 15535 				}
       
 15536 
       
 15537 				// Walk the nodes inside the block
       
 15538 				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
       
 15539 				while ((node = walker[start ? 'prev' : 'next']())) {
       
 15540 					if (node.nodeType === 3) {
       
 15541 						lastTextNode = node;
       
 15542 						pos = findSpace(node);
       
 15543 
       
 15544 						if (pos !== -1) {
       
 15545 							return {container: node, offset: pos};
       
 15546 						}
       
 15547 					} else if (isBlock(node)) {
       
 15548 						break;
       
 15549 					}
       
 15550 				}
       
 15551 
       
 15552 				if (lastTextNode) {
       
 15553 					if (start) {
       
 15554 						offset = 0;
       
 15555 					} else {
       
 15556 						offset = lastTextNode.length;
       
 15557 					}
       
 15558 
       
 15559 					return {container: lastTextNode, offset: offset};
       
 15560 				}
       
 15561 			}
       
 15562 
       
 15563 			function findSelectorEndPoint(container, sibling_name) {
       
 15564 				var parents, i, y, curFormat;
       
 15565 
       
 15566 				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name]) {
       
 15567 					container = container[sibling_name];
       
 15568 				}
       
 15569 
       
 15570 				parents = getParents(container);
       
 15571 				for (i = 0; i < parents.length; i++) {
       
 15572 					for (y = 0; y < format.length; y++) {
       
 15573 						curFormat = format[y];
       
 15574 
       
 15575 						// If collapsed state is set then skip formats that doesn't match that
       
 15576 						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) {
       
 15577 							continue;
       
 15578 						}
       
 15579 
       
 15580 						if (dom.is(parents[i], curFormat.selector)) {
       
 15581 							return parents[i];
       
 15582 						}
       
 15583 					}
       
 15584 				}
       
 15585 
       
 15586 				return container;
       
 15587 			}
       
 15588 
       
 15589 			function findBlockEndPoint(container, sibling_name) {
       
 15590 				var node, root = dom.getRoot();
       
 15591 
       
 15592 				// Expand to block of similar type
       
 15593 				if (!format[0].wrapper) {
       
 15594 					node = dom.getParent(container, format[0].block, root);
       
 15595 				}
       
 15596 
       
 15597 				// Expand to first wrappable block element or any block element
       
 15598 				if (!node) {
       
 15599 					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
       
 15600 						// Fixes #6183 where it would expand to editable parent element in inline mode
       
 15601 						return node != root && isTextBlock(node);
       
 15602 					});
       
 15603 				}
       
 15604 
       
 15605 				// Exclude inner lists from wrapping
       
 15606 				if (node && format[0].wrapper) {
       
 15607 					node = getParents(node, 'ul,ol').reverse()[0] || node;
       
 15608 				}
       
 15609 
       
 15610 				// Didn't find a block element look for first/last wrappable element
       
 15611 				if (!node) {
       
 15612 					node = container;
       
 15613 
       
 15614 					while (node[sibling_name] && !isBlock(node[sibling_name])) {
       
 15615 						node = node[sibling_name];
       
 15616 
       
 15617 						// Break on BR but include it will be removed later on
       
 15618 						// we can't remove it now since we need to check if it can be wrapped
       
 15619 						if (isEq(node, 'br')) {
       
 15620 							break;
       
 15621 						}
       
 15622 					}
       
 15623 				}
       
 15624 
       
 15625 				return node || container;
       
 15626 			}
       
 15627 
       
 15628 			// Expand to closest contentEditable element
       
 15629 			startContainer = findParentContentEditable(startContainer);
       
 15630 			endContainer = findParentContentEditable(endContainer);
       
 15631 
       
 15632 			// Exclude bookmark nodes if possible
       
 15633 			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
       
 15634 				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
       
 15635 				startContainer = startContainer.nextSibling || startContainer;
       
 15636 
       
 15637 				if (startContainer.nodeType == 3) {
       
 15638 					startOffset = 0;
       
 15639 				}
       
 15640 			}
       
 15641 
       
 15642 			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
       
 15643 				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
       
 15644 				endContainer = endContainer.previousSibling || endContainer;
       
 15645 
       
 15646 				if (endContainer.nodeType == 3) {
       
 15647 					endOffset = endContainer.length;
       
 15648 				}
       
 15649 			}
       
 15650 
       
 15651 			if (format[0].inline) {
       
 15652 				if (rng.collapsed) {
       
 15653 					// Expand left to closest word boundary
       
 15654 					endPoint = findWordEndPoint(startContainer, startOffset, true);
       
 15655 					if (endPoint) {
       
 15656 						startContainer = endPoint.container;
       
 15657 						startOffset = endPoint.offset;
       
 15658 					}
       
 15659 
       
 15660 					// Expand right to closest word boundary
       
 15661 					endPoint = findWordEndPoint(endContainer, endOffset);
       
 15662 					if (endPoint) {
       
 15663 						endContainer = endPoint.container;
       
 15664 						endOffset = endPoint.offset;
       
 15665 					}
       
 15666 				}
       
 15667 
       
 15668 				// Avoid applying formatting to a trailing space.
       
 15669 				leaf = findLeaf(endContainer, endOffset);
       
 15670 				if (leaf.node) {
       
 15671 					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) {
       
 15672 						leaf = findLeaf(leaf.node.previousSibling);
       
 15673 					}
       
 15674 
       
 15675 					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
       
 15676 							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
       
 15677 
       
 15678 						if (leaf.offset > 1) {
       
 15679 							endContainer = leaf.node;
       
 15680 							endContainer.splitText(leaf.offset - 1);
       
 15681 						}
       
 15682 					}
       
 15683 				}
       
 15684 			}
       
 15685 
       
 15686 			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
       
 15687 			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
       
 15688 			// This will reduce the number of wrapper elements that needs to be created
       
 15689 			// Move start point up the tree
       
 15690 			if (format[0].inline || format[0].block_expand) {
       
 15691 				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
       
 15692 					startContainer = findParentContainer(true);
       
 15693 				}
       
 15694 
       
 15695 				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
       
 15696 					endContainer = findParentContainer();
       
 15697 				}
       
 15698 			}
       
 15699 
       
 15700 			// Expand start/end container to matching selector
       
 15701 			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
       
 15702 				// Find new startContainer/endContainer if there is better one
       
 15703 				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
       
 15704 				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
       
 15705 			}
       
 15706 
       
 15707 			// Expand start/end container to matching block element or text node
       
 15708 			if (format[0].block || format[0].selector) {
       
 15709 				// Find new startContainer/endContainer if there is better one
       
 15710 				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
       
 15711 				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
       
 15712 
       
 15713 				// Non block element then try to expand up the leaf
       
 15714 				if (format[0].block) {
       
 15715 					if (!isBlock(startContainer)) {
       
 15716 						startContainer = findParentContainer(true);
       
 15717 					}
       
 15718 
       
 15719 					if (!isBlock(endContainer)) {
       
 15720 						endContainer = findParentContainer();
       
 15721 					}
       
 15722 				}
       
 15723 			}
       
 15724 
       
 15725 			// Setup index for startContainer
       
 15726 			if (startContainer.nodeType == 1) {
       
 15727 				startOffset = nodeIndex(startContainer);
       
 15728 				startContainer = startContainer.parentNode;
       
 15729 			}
       
 15730 
       
 15731 			// Setup index for endContainer
       
 15732 			if (endContainer.nodeType == 1) {
       
 15733 				endOffset = nodeIndex(endContainer) + 1;
       
 15734 				endContainer = endContainer.parentNode;
       
 15735 			}
       
 15736 
       
 15737 			// Return new range like object
       
 15738 			return {
       
 15739 				startContainer: startContainer,
       
 15740 				startOffset: startOffset,
       
 15741 				endContainer: endContainer,
       
 15742 				endOffset: endOffset
       
 15743 			};
       
 15744 		}
       
 15745 
       
 15746 		function isColorFormatAndAnchor(node, format) {
       
 15747 			return format.links && node.tagName == 'A';
       
 15748 		}
       
 15749 
       
 15750 		/**
       
 15751 		 * Removes the specified format for the specified node. It will also remove the node if it doesn't have
       
 15752 		 * any attributes if the format specifies it to do so.
       
 15753 		 *
       
 15754 		 * @private
       
 15755 		 * @param {Object} format Format object with items to remove from node.
       
 15756 		 * @param {Object} vars Name/value object with variables to apply to format.
       
 15757 		 * @param {Node} node Node to remove the format styles on.
       
 15758 		 * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
       
 15759 		 * @return {Boolean} True/false if the node was removed or not.
       
 15760 		 */
       
 15761 		function removeFormat(format, vars, node, compare_node) {
       
 15762 			var i, attrs, stylesModified;
       
 15763 
       
 15764 			// Check if node matches format
       
 15765 			if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) {
       
 15766 				return FALSE;
       
 15767 			}
       
 15768 
       
 15769 			// Should we compare with format attribs and styles
       
 15770 			if (format.remove != 'all') {
       
 15771 				// Remove styles
       
 15772 				each(format.styles, function(value, name) {
       
 15773 					value = normalizeStyleValue(replaceVars(value, vars), name);
       
 15774 
       
 15775 					// Indexed array
       
 15776 					if (typeof name === 'number') {
       
 15777 						name = value;
       
 15778 						compare_node = 0;
       
 15779 					}
       
 15780 
       
 15781 					if (format.remove_similar || (!compare_node || isEq(getStyle(compare_node, name), value))) {
       
 15782 						dom.setStyle(node, name, '');
       
 15783 					}
       
 15784 
       
 15785 					stylesModified = 1;
       
 15786 				});
       
 15787 
       
 15788 				// Remove style attribute if it's empty
       
 15789 				if (stylesModified && dom.getAttrib(node, 'style') === '') {
       
 15790 					node.removeAttribute('style');
       
 15791 					node.removeAttribute('data-mce-style');
       
 15792 				}
       
 15793 
       
 15794 				// Remove attributes
       
 15795 				each(format.attributes, function(value, name) {
       
 15796 					var valueOut;
       
 15797 
       
 15798 					value = replaceVars(value, vars);
       
 15799 
       
 15800 					// Indexed array
       
 15801 					if (typeof name === 'number') {
       
 15802 						name = value;
       
 15803 						compare_node = 0;
       
 15804 					}
       
 15805 
       
 15806 					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
       
 15807 						// Keep internal classes
       
 15808 						if (name == 'class') {
       
 15809 							value = dom.getAttrib(node, name);
       
 15810 							if (value) {
       
 15811 								// Build new class value where everything is removed except the internal prefixed classes
       
 15812 								valueOut = '';
       
 15813 								each(value.split(/\s+/), function(cls) {
       
 15814 									if (/mce\-\w+/.test(cls)) {
       
 15815 										valueOut += (valueOut ? ' ' : '') + cls;
       
 15816 									}
       
 15817 								});
       
 15818 
       
 15819 								// We got some internal classes left
       
 15820 								if (valueOut) {
       
 15821 									dom.setAttrib(node, name, valueOut);
       
 15822 									return;
       
 15823 								}
       
 15824 							}
       
 15825 						}
       
 15826 
       
 15827 						// IE6 has a bug where the attribute doesn't get removed correctly
       
 15828 						if (name == "class") {
       
 15829 							node.removeAttribute('className');
       
 15830 						}
       
 15831 
       
 15832 						// Remove mce prefixed attributes
       
 15833 						if (MCE_ATTR_RE.test(name)) {
       
 15834 							node.removeAttribute('data-mce-' + name);
       
 15835 						}
       
 15836 
       
 15837 						node.removeAttribute(name);
       
 15838 					}
       
 15839 				});
       
 15840 
       
 15841 				// Remove classes
       
 15842 				each(format.classes, function(value) {
       
 15843 					value = replaceVars(value, vars);
       
 15844 
       
 15845 					if (!compare_node || dom.hasClass(compare_node, value)) {
       
 15846 						dom.removeClass(node, value);
       
 15847 					}
       
 15848 				});
       
 15849 
       
 15850 				// Check for non internal attributes
       
 15851 				attrs = dom.getAttribs(node);
       
 15852 				for (i = 0; i < attrs.length; i++) {
       
 15853 					if (attrs[i].nodeName.indexOf('_') !== 0) {
       
 15854 						return FALSE;
       
 15855 					}
       
 15856 				}
       
 15857 			}
       
 15858 
       
 15859 			// Remove the inline child if it's empty for example <b> or <span>
       
 15860 			if (format.remove != 'none') {
       
 15861 				removeNode(node, format);
       
 15862 				return TRUE;
       
 15863 			}
       
 15864 		}
       
 15865 
       
 15866 		/**
       
 15867 		 * Removes the node and wrap it's children in paragraphs before doing so or
       
 15868 		 * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled.
       
 15869 		 *
       
 15870 		 * If the div in the node below gets removed:
       
 15871 		 *  text<div>text</div>text
       
 15872 		 *
       
 15873 		 * Output becomes:
       
 15874 		 *  text<div><br />text<br /></div>text
       
 15875 		 *
       
 15876 		 * So when the div is removed the result is:
       
 15877 		 *  text<br />text<br />text
       
 15878 		 *
       
 15879 		 * @private
       
 15880 		 * @param {Node} node Node to remove + apply BR/P elements to.
       
 15881 		 * @param {Object} format Format rule.
       
 15882 		 * @return {Node} Input node.
       
 15883 		 */
       
 15884 		function removeNode(node, format) {
       
 15885 			var parentNode = node.parentNode, rootBlockElm;
       
 15886 
       
 15887 			function find(node, next, inc) {
       
 15888 				node = getNonWhiteSpaceSibling(node, next, inc);
       
 15889 
       
 15890 				return !node || (node.nodeName == 'BR' || isBlock(node));
       
 15891 			}
       
 15892 
       
 15893 			if (format.block) {
       
 15894 				if (!forcedRootBlock) {
       
 15895 					// Append BR elements if needed before we remove the block
       
 15896 					if (isBlock(node) && !isBlock(parentNode)) {
       
 15897 						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) {
       
 15898 							node.insertBefore(dom.create('br'), node.firstChild);
       
 15899 						}
       
 15900 
       
 15901 						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) {
       
 15902 							node.appendChild(dom.create('br'));
       
 15903 						}
       
 15904 					}
       
 15905 				} else {
       
 15906 					// Wrap the block in a forcedRootBlock if we are at the root of document
       
 15907 					if (parentNode == dom.getRoot()) {
       
 15908 						if (!format.list_block || !isEq(node, format.list_block)) {
       
 15909 							each(grep(node.childNodes), function(node) {
       
 15910 								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
       
 15911 									if (!rootBlockElm) {
       
 15912 										rootBlockElm = wrap(node, forcedRootBlock);
       
 15913 										dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs);
       
 15914 									} else {
       
 15915 										rootBlockElm.appendChild(node);
       
 15916 									}
       
 15917 								} else {
       
 15918 									rootBlockElm = 0;
       
 15919 								}
       
 15920 							});
       
 15921 						}
       
 15922 					}
       
 15923 				}
       
 15924 			}
       
 15925 
       
 15926 			// Never remove nodes that isn't the specified inline element if a selector is specified too
       
 15927 			if (format.selector && format.inline && !isEq(format.inline, node)) {
       
 15928 				return;
       
 15929 			}
       
 15930 
       
 15931 			dom.remove(node, 1);
       
 15932 		}
       
 15933 
       
 15934 		/**
       
 15935 		 * Returns the next/previous non whitespace node.
       
 15936 		 *
       
 15937 		 * @private
       
 15938 		 * @param {Node} node Node to start at.
       
 15939 		 * @param {boolean} next (Optional) Include next or previous node defaults to previous.
       
 15940 		 * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false.
       
 15941 		 * @return {Node} Next or previous node or undefined if it wasn't found.
       
 15942 		 */
       
 15943 		function getNonWhiteSpaceSibling(node, next, inc) {
       
 15944 			if (node) {
       
 15945 				next = next ? 'nextSibling' : 'previousSibling';
       
 15946 
       
 15947 				for (node = inc ? node : node[next]; node; node = node[next]) {
       
 15948 					if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
       
 15949 						return node;
       
 15950 					}
       
 15951 				}
       
 15952 			}
       
 15953 		}
       
 15954 
       
 15955 		/**
       
 15956 		 * Merges the next/previous sibling element if they match.
       
 15957 		 *
       
 15958 		 * @private
       
 15959 		 * @param {Node} prev Previous node to compare/merge.
       
 15960 		 * @param {Node} next Next node to compare/merge.
       
 15961 		 * @return {Node} Next node if we didn't merge and prev node if we did.
       
 15962 		 */
       
 15963 		function mergeSiblings(prev, next) {
       
 15964 			var sibling, tmpSibling, elementUtils = new ElementUtils(dom);
       
 15965 
       
 15966 			function findElementSibling(node, sibling_name) {
       
 15967 				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
       
 15968 					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) {
       
 15969 						return node;
       
 15970 					}
       
 15971 
       
 15972 					if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) {
       
 15973 						return sibling;
       
 15974 					}
       
 15975 				}
       
 15976 
       
 15977 				return node;
       
 15978 			}
       
 15979 
       
 15980 			// Check if next/prev exists and that they are elements
       
 15981 			if (prev && next) {
       
 15982 				// If previous sibling is empty then jump over it
       
 15983 				prev = findElementSibling(prev, 'previousSibling');
       
 15984 				next = findElementSibling(next, 'nextSibling');
       
 15985 
       
 15986 				// Compare next and previous nodes
       
 15987 				if (elementUtils.compare(prev, next)) {
       
 15988 					// Append nodes between
       
 15989 					for (sibling = prev.nextSibling; sibling && sibling != next;) {
       
 15990 						tmpSibling = sibling;
       
 15991 						sibling = sibling.nextSibling;
       
 15992 						prev.appendChild(tmpSibling);
       
 15993 					}
       
 15994 
       
 15995 					// Remove next node
       
 15996 					dom.remove(next);
       
 15997 
       
 15998 					// Move children into prev node
       
 15999 					each(grep(next.childNodes), function(node) {
       
 16000 						prev.appendChild(node);
       
 16001 					});
       
 16002 
       
 16003 					return prev;
       
 16004 				}
       
 16005 			}
       
 16006 
       
 16007 			return next;
       
 16008 		}
       
 16009 
       
 16010 		function getContainer(rng, start) {
       
 16011 			var container, offset, lastIdx;
       
 16012 
       
 16013 			container = rng[start ? 'startContainer' : 'endContainer'];
       
 16014 			offset = rng[start ? 'startOffset' : 'endOffset'];
       
 16015 
       
 16016 			if (container.nodeType == 1) {
       
 16017 				lastIdx = container.childNodes.length - 1;
       
 16018 
       
 16019 				if (!start && offset) {
       
 16020 					offset--;
       
 16021 				}
       
 16022 
       
 16023 				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
       
 16024 			}
       
 16025 
       
 16026 			// If start text node is excluded then walk to the next node
       
 16027 			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
       
 16028 				container = new TreeWalker(container, ed.getBody()).next() || container;
       
 16029 			}
       
 16030 
       
 16031 			// If end text node is excluded then walk to the previous node
       
 16032 			if (container.nodeType === 3 && !start && offset === 0) {
       
 16033 				container = new TreeWalker(container, ed.getBody()).prev() || container;
       
 16034 			}
       
 16035 
       
 16036 			return container;
       
 16037 		}
       
 16038 
       
 16039 		function performCaretAction(type, name, vars, similar) {
       
 16040 			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
       
 16041 
       
 16042 			// Creates a caret container bogus element
       
 16043 			function createCaretContainer(fill) {
       
 16044 				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
       
 16045 
       
 16046 				if (fill) {
       
 16047 					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
       
 16048 				}
       
 16049 
       
 16050 				return caretContainer;
       
 16051 			}
       
 16052 
       
 16053 			function isCaretContainerEmpty(node, nodes) {
       
 16054 				while (node) {
       
 16055 					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
       
 16056 						return false;
       
 16057 					}
       
 16058 
       
 16059 					// Collect nodes
       
 16060 					if (nodes && node.nodeType === 1) {
       
 16061 						nodes.push(node);
       
 16062 					}
       
 16063 
       
 16064 					node = node.firstChild;
       
 16065 				}
       
 16066 
       
 16067 				return true;
       
 16068 			}
       
 16069 
       
 16070 			// Returns any parent caret container element
       
 16071 			function getParentCaretContainer(node) {
       
 16072 				while (node) {
       
 16073 					if (node.id === caretContainerId) {
       
 16074 						return node;
       
 16075 					}
       
 16076 
       
 16077 					node = node.parentNode;
       
 16078 				}
       
 16079 			}
       
 16080 
       
 16081 			// Finds the first text node in the specified node
       
 16082 			function findFirstTextNode(node) {
       
 16083 				var walker;
       
 16084 
       
 16085 				if (node) {
       
 16086 					walker = new TreeWalker(node, node);
       
 16087 
       
 16088 					for (node = walker.current(); node; node = walker.next()) {
       
 16089 						if (node.nodeType === 3) {
       
 16090 							return node;
       
 16091 						}
       
 16092 					}
       
 16093 				}
       
 16094 			}
       
 16095 
       
 16096 			// Removes the caret container for the specified node or all on the current document
       
 16097 			function removeCaretContainer(node, move_caret) {
       
 16098 				var child, rng;
       
 16099 
       
 16100 				if (!node) {
       
 16101 					node = getParentCaretContainer(selection.getStart());
       
 16102 
       
 16103 					if (!node) {
       
 16104 						while ((node = dom.get(caretContainerId))) {
       
 16105 							removeCaretContainer(node, false);
       
 16106 						}
       
 16107 					}
       
 16108 				} else {
       
 16109 					rng = selection.getRng(true);
       
 16110 
       
 16111 					if (isCaretContainerEmpty(node)) {
       
 16112 						if (move_caret !== false) {
       
 16113 							rng.setStartBefore(node);
       
 16114 							rng.setEndBefore(node);
       
 16115 						}
       
 16116 
       
 16117 						dom.remove(node);
       
 16118 					} else {
       
 16119 						child = findFirstTextNode(node);
       
 16120 
       
 16121 						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
       
 16122 							child.deleteData(0, 1);
       
 16123 
       
 16124 							// Fix for bug #6976
       
 16125 							if (rng.startContainer == child && rng.startOffset > 0) {
       
 16126 								rng.setStart(child, rng.startOffset - 1);
       
 16127 							}
       
 16128 
       
 16129 							if (rng.endContainer == child && rng.endOffset > 0) {
       
 16130 								rng.setEnd(child, rng.endOffset - 1);
       
 16131 							}
       
 16132 						}
       
 16133 
       
 16134 						dom.remove(node, 1);
       
 16135 					}
       
 16136 
       
 16137 					selection.setRng(rng);
       
 16138 				}
       
 16139 			}
       
 16140 
       
 16141 			// Applies formatting to the caret postion
       
 16142 			function applyCaretFormat() {
       
 16143 				var rng, caretContainer, textNode, offset, bookmark, container, text;
       
 16144 
       
 16145 				rng = selection.getRng(true);
       
 16146 				offset = rng.startOffset;
       
 16147 				container = rng.startContainer;
       
 16148 				text = container.nodeValue;
       
 16149 
       
 16150 				caretContainer = getParentCaretContainer(selection.getStart());
       
 16151 				if (caretContainer) {
       
 16152 					textNode = findFirstTextNode(caretContainer);
       
 16153 				}
       
 16154 
       
 16155 				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
       
 16156 				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
       
 16157 					// Get bookmark of caret position
       
 16158 					bookmark = selection.getBookmark();
       
 16159 
       
 16160 					// Collapse bookmark range (WebKit)
       
 16161 					rng.collapse(true);
       
 16162 
       
 16163 					// Expand the range to the closest word and split it at those points
       
 16164 					rng = expandRng(rng, get(name));
       
 16165 					rng = rangeUtils.split(rng);
       
 16166 
       
 16167 					// Apply the format to the range
       
 16168 					apply(name, vars, rng);
       
 16169 
       
 16170 					// Move selection back to caret position
       
 16171 					selection.moveToBookmark(bookmark);
       
 16172 				} else {
       
 16173 					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
       
 16174 						caretContainer = createCaretContainer(true);
       
 16175 						textNode = caretContainer.firstChild;
       
 16176 
       
 16177 						rng.insertNode(caretContainer);
       
 16178 						offset = 1;
       
 16179 
       
 16180 						apply(name, vars, caretContainer);
       
 16181 					} else {
       
 16182 						apply(name, vars, caretContainer);
       
 16183 					}
       
 16184 
       
 16185 					// Move selection to text node
       
 16186 					selection.setCursorLocation(textNode, offset);
       
 16187 				}
       
 16188 			}
       
 16189 
       
 16190 			function removeCaretFormat() {
       
 16191 				var rng = selection.getRng(true), container, offset, bookmark,
       
 16192 					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
       
 16193 
       
 16194 				container = rng.startContainer;
       
 16195 				offset = rng.startOffset;
       
 16196 				node = container;
       
 16197 
       
 16198 				if (container.nodeType == 3) {
       
 16199 					if (offset != container.nodeValue.length) {
       
 16200 						hasContentAfter = true;
       
 16201 					}
       
 16202 
       
 16203 					node = node.parentNode;
       
 16204 				}
       
 16205 
       
 16206 				while (node) {
       
 16207 					if (matchNode(node, name, vars, similar)) {
       
 16208 						formatNode = node;
       
 16209 						break;
       
 16210 					}
       
 16211 
       
 16212 					if (node.nextSibling) {
       
 16213 						hasContentAfter = true;
       
 16214 					}
       
 16215 
       
 16216 					parents.push(node);
       
 16217 					node = node.parentNode;
       
 16218 				}
       
 16219 
       
 16220 				// Node doesn't have the specified format
       
 16221 				if (!formatNode) {
       
 16222 					return;
       
 16223 				}
       
 16224 
       
 16225 				// Is there contents after the caret then remove the format on the element
       
 16226 				if (hasContentAfter) {
       
 16227 					// Get bookmark of caret position
       
 16228 					bookmark = selection.getBookmark();
       
 16229 
       
 16230 					// Collapse bookmark range (WebKit)
       
 16231 					rng.collapse(true);
       
 16232 
       
 16233 					// Expand the range to the closest word and split it at those points
       
 16234 					rng = expandRng(rng, get(name), true);
       
 16235 					rng = rangeUtils.split(rng);
       
 16236 
       
 16237 					// Remove the format from the range
       
 16238 					remove(name, vars, rng);
       
 16239 
       
 16240 					// Move selection back to caret position
       
 16241 					selection.moveToBookmark(bookmark);
       
 16242 				} else {
       
 16243 					caretContainer = createCaretContainer();
       
 16244 
       
 16245 					node = caretContainer;
       
 16246 					for (i = parents.length - 1; i >= 0; i--) {
       
 16247 						node.appendChild(dom.clone(parents[i], false));
       
 16248 						node = node.firstChild;
       
 16249 					}
       
 16250 
       
 16251 					// Insert invisible character into inner most format element
       
 16252 					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
       
 16253 					node = node.firstChild;
       
 16254 
       
 16255 					var block = dom.getParent(formatNode, isTextBlock);
       
 16256 
       
 16257 					if (block && dom.isEmpty(block)) {
       
 16258 						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
       
 16259 						formatNode.parentNode.replaceChild(caretContainer, formatNode);
       
 16260 					} else {
       
 16261 						// Insert caret container after the formated node
       
 16262 						dom.insertAfter(caretContainer, formatNode);
       
 16263 					}
       
 16264 
       
 16265 					// Move selection to text node
       
 16266 					selection.setCursorLocation(node, 1);
       
 16267 
       
 16268 					// If the formatNode is empty, we can remove it safely.
       
 16269 					if (dom.isEmpty(formatNode)) {
       
 16270 						dom.remove(formatNode);
       
 16271 					}
       
 16272 				}
       
 16273 			}
       
 16274 
       
 16275 			// Checks if the parent caret container node isn't empty if that is the case it
       
 16276 			// will remove the bogus state on all children that isn't empty
       
 16277 			function unmarkBogusCaretParents() {
       
 16278 				var caretContainer;
       
 16279 
       
 16280 				caretContainer = getParentCaretContainer(selection.getStart());
       
 16281 				if (caretContainer && !dom.isEmpty(caretContainer)) {
       
 16282 					walk(caretContainer, function(node) {
       
 16283 						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
       
 16284 							dom.setAttrib(node, 'data-mce-bogus', null);
       
 16285 						}
       
 16286 					}, 'childNodes');
       
 16287 				}
       
 16288 			}
       
 16289 
       
 16290 			// Only bind the caret events once
       
 16291 			if (!ed._hasCaretEvents) {
       
 16292 				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
       
 16293 				markCaretContainersBogus = function() {
       
 16294 					var nodes = [], i;
       
 16295 
       
 16296 					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
       
 16297 						// Mark children
       
 16298 						i = nodes.length;
       
 16299 						while (i--) {
       
 16300 							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
       
 16301 						}
       
 16302 					}
       
 16303 				};
       
 16304 
       
 16305 				disableCaretContainer = function(e) {
       
 16306 					var keyCode = e.keyCode;
       
 16307 
       
 16308 					removeCaretContainer();
       
 16309 
       
 16310 					// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
       
 16311 					// Backspace key needs to check if the range is collapsed due to bug #6780
       
 16312 					if ((keyCode == 8 && selection.isCollapsed()) || keyCode == 37 || keyCode == 39) {
       
 16313 						removeCaretContainer(getParentCaretContainer(selection.getStart()));
       
 16314 					}
       
 16315 
       
 16316 					unmarkBogusCaretParents();
       
 16317 				};
       
 16318 
       
 16319 				// Remove bogus state if they got filled by contents using editor.selection.setContent
       
 16320 				ed.on('SetContent', function(e) {
       
 16321 					if (e.selection) {
       
 16322 						unmarkBogusCaretParents();
       
 16323 					}
       
 16324 				});
       
 16325 				ed._hasCaretEvents = true;
       
 16326 			}
       
 16327 
       
 16328 			// Do apply or remove caret format
       
 16329 			if (type == "apply") {
       
 16330 				applyCaretFormat();
       
 16331 			} else {
       
 16332 				removeCaretFormat();
       
 16333 			}
       
 16334 		}
       
 16335 
       
 16336 		/**
       
 16337 		 * Moves the start to the first suitable text node.
       
 16338 		 */
       
 16339 		function moveStart(rng) {
       
 16340 			var container = rng.startContainer,
       
 16341 					offset = rng.startOffset, isAtEndOfText,
       
 16342 					walker, node, nodes, tmpNode;
       
 16343 
       
 16344 			// Convert text node into index if possible
       
 16345 			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
       
 16346 				// Get the parent container location and walk from there
       
 16347 				offset = nodeIndex(container);
       
 16348 				container = container.parentNode;
       
 16349 				isAtEndOfText = true;
       
 16350 			}
       
 16351 
       
 16352 			// Move startContainer/startOffset in to a suitable node
       
 16353 			if (container.nodeType == 1) {
       
 16354 				nodes = container.childNodes;
       
 16355 				container = nodes[Math.min(offset, nodes.length - 1)];
       
 16356 				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
       
 16357 
       
 16358 				// If offset is at end of the parent node walk to the next one
       
 16359 				if (offset > nodes.length - 1 || isAtEndOfText) {
       
 16360 					walker.next();
       
 16361 				}
       
 16362 
       
 16363 				for (node = walker.current(); node; node = walker.next()) {
       
 16364 					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
       
 16365 						// IE has a "neat" feature where it moves the start node into the closest element
       
 16366 						// we can avoid this by inserting an element before it and then remove it after we set the selection
       
 16367 						tmpNode = dom.create('a', {'data-mce-bogus': 'all'}, INVISIBLE_CHAR);
       
 16368 						node.parentNode.insertBefore(tmpNode, node);
       
 16369 
       
 16370 						// Set selection and remove tmpNode
       
 16371 						rng.setStart(node, 0);
       
 16372 						selection.setRng(rng);
       
 16373 						dom.remove(tmpNode);
       
 16374 
       
 16375 						return;
       
 16376 					}
       
 16377 				}
       
 16378 			}
       
 16379 		}
       
 16380 	};
       
 16381 });
       
 16382 
       
 16383 // Included from: js/tinymce/classes/UndoManager.js
       
 16384 
       
 16385 /**
       
 16386  * UndoManager.js
       
 16387  *
       
 16388  * Copyright, Moxiecode Systems AB
       
 16389  * Released under LGPL License.
       
 16390  *
       
 16391  * License: http://www.tinymce.com/license
       
 16392  * Contributing: http://www.tinymce.com/contributing
       
 16393  */
       
 16394 
       
 16395 /**
       
 16396  * 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.
       
 16397  *
       
 16398  * @class tinymce.UndoManager
       
 16399  */
       
 16400 define("tinymce/UndoManager", [
       
 16401 	"tinymce/util/VK",
       
 16402 	"tinymce/Env",
       
 16403 	"tinymce/util/Tools",
       
 16404 	"tinymce/html/SaxParser"
       
 16405 ], function(VK, Env, Tools, SaxParser) {
       
 16406 	var trim = Tools.trim, trimContentRegExp;
       
 16407 
       
 16408 	trimContentRegExp = new RegExp([
       
 16409 		'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
       
 16410 		'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
       
 16411 	].join('|'), 'gi');
       
 16412 
       
 16413 	return function(editor) {
       
 16414 		var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
       
 16415 
       
 16416 		/**
       
 16417 		 * Returns a trimmed version of the editor contents to be used for the undo level. This
       
 16418 		 * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also
       
 16419 		 * remove the data-mce-selected attributes used for selection of objects and caret containers.
       
 16420 		 * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will
       
 16421 		 * be removed by the serialization logic when you save.
       
 16422 		 *
       
 16423 		 * @private
       
 16424 		 * @return {String} HTML contents of the editor excluding some internal bogus elements.
       
 16425 		 */
       
 16426 		function getContent() {
       
 16427 			var content = editor.getContent({format: 'raw', no_events: 1});
       
 16428 			var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g;
       
 16429 			var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema;
       
 16430 
       
 16431 			content = content.replace(trimContentRegExp, '');
       
 16432 			shortEndedElements = schema.getShortEndedElements();
       
 16433 
       
 16434 			// Remove all bogus elements marked with "all"
       
 16435 			while ((matches = bogusAllRegExp.exec(content))) {
       
 16436 				index = bogusAllRegExp.lastIndex;
       
 16437 				matchLength = matches[0].length;
       
 16438 
       
 16439 				if (shortEndedElements[matches[1]]) {
       
 16440 					endTagIndex = index;
       
 16441 				} else {
       
 16442 					endTagIndex = SaxParser.findEndTag(schema, content, index);
       
 16443 				}
       
 16444 
       
 16445 				content = content.substring(0, index - matchLength) + content.substring(endTagIndex);
       
 16446 				bogusAllRegExp.lastIndex = index - matchLength;
       
 16447 			}
       
 16448 
       
 16449 			return trim(content);
       
 16450 		}
       
 16451 
       
 16452 		function setDirty(state) {
       
 16453 			editor.isNotDirty = !state;
       
 16454 		}
       
 16455 
       
 16456 		function addNonTypingUndoLevel(e) {
       
 16457 			self.typing = false;
       
 16458 			self.add({}, e);
       
 16459 		}
       
 16460 
       
 16461 		// Add initial undo level when the editor is initialized
       
 16462 		editor.on('init', function() {
       
 16463 			self.add();
       
 16464 		});
       
 16465 
       
 16466 		// Get position before an execCommand is processed
       
 16467 		editor.on('BeforeExecCommand', function(e) {
       
 16468 			var cmd = e.command;
       
 16469 
       
 16470 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
       
 16471 				self.beforeChange();
       
 16472 			}
       
 16473 		});
       
 16474 
       
 16475 		// Add undo level after an execCommand call was made
       
 16476 		editor.on('ExecCommand', function(e) {
       
 16477 			var cmd = e.command;
       
 16478 
       
 16479 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
       
 16480 				addNonTypingUndoLevel(e);
       
 16481 			}
       
 16482 		});
       
 16483 
       
 16484 		editor.on('ObjectResizeStart', function() {
       
 16485 			self.beforeChange();
       
 16486 		});
       
 16487 
       
 16488 		editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel);
       
 16489 		editor.on('DragEnd', addNonTypingUndoLevel);
       
 16490 
       
 16491 		editor.on('KeyUp', function(e) {
       
 16492 			var keyCode = e.keyCode;
       
 16493 
       
 16494 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
       
 16495 				addNonTypingUndoLevel();
       
 16496 				editor.nodeChanged();
       
 16497 			}
       
 16498 
       
 16499 			if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
       
 16500 				editor.nodeChanged();
       
 16501 			}
       
 16502 
       
 16503 			// Fire a TypingUndo event on the first character entered
       
 16504 			if (isFirstTypedCharacter && self.typing) {
       
 16505 				// Make it dirty if the content was changed after typing the first character
       
 16506 				if (!editor.isDirty()) {
       
 16507 					setDirty(data[0] && getContent() != data[0].content);
       
 16508 
       
 16509 					// Fire initial change event
       
 16510 					if (!editor.isNotDirty) {
       
 16511 						editor.fire('change', {level: data[0], lastLevel: null});
       
 16512 					}
       
 16513 				}
       
 16514 
       
 16515 				editor.fire('TypingUndo');
       
 16516 				isFirstTypedCharacter = false;
       
 16517 				editor.nodeChanged();
       
 16518 			}
       
 16519 		});
       
 16520 
       
 16521 		editor.on('KeyDown', function(e) {
       
 16522 			var keyCode = e.keyCode;
       
 16523 
       
 16524 			// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
       
 16525 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
       
 16526 				if (self.typing) {
       
 16527 					addNonTypingUndoLevel(e);
       
 16528 				}
       
 16529 
       
 16530 				return;
       
 16531 			}
       
 16532 
       
 16533 			// If key isn't Ctrl+Alt/AltGr
       
 16534 			var modKey = (e.ctrlKey && !e.altKey) || e.metaKey;
       
 16535 			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing && !modKey) {
       
 16536 				self.beforeChange();
       
 16537 				self.typing = true;
       
 16538 				self.add({}, e);
       
 16539 				isFirstTypedCharacter = true;
       
 16540 			}
       
 16541 		});
       
 16542 
       
 16543 		editor.on('MouseDown', function(e) {
       
 16544 			if (self.typing) {
       
 16545 				addNonTypingUndoLevel(e);
       
 16546 			}
       
 16547 		});
       
 16548 
       
 16549 		// Add keyboard shortcuts for undo/redo keys
       
 16550 		editor.addShortcut('meta+z', '', 'Undo');
       
 16551 		editor.addShortcut('meta+y,meta+shift+z', '', 'Redo');
       
 16552 
       
 16553 		editor.on('AddUndo Undo Redo ClearUndos', function(e) {
       
 16554 			if (!e.isDefaultPrevented()) {
       
 16555 				editor.nodeChanged();
       
 16556 			}
       
 16557 		});
       
 16558 
       
 16559 		/*eslint consistent-this:0 */
       
 16560 		self = {
       
 16561 			// Explose for debugging reasons
       
 16562 			data: data,
       
 16563 
       
 16564 			/**
       
 16565 			 * State if the user is currently typing or not. This will add a typing operation into one undo
       
 16566 			 * level instead of one new level for each keystroke.
       
 16567 			 *
       
 16568 			 * @field {Boolean} typing
       
 16569 			 */
       
 16570 			typing: false,
       
 16571 
       
 16572 			/**
       
 16573 			 * Stores away a bookmark to be used when performing an undo action so that the selection is before
       
 16574 			 * the change has been made.
       
 16575 			 *
       
 16576 			 * @method beforeChange
       
 16577 			 */
       
 16578 			beforeChange: function() {
       
 16579 				if (!locks) {
       
 16580 					beforeBookmark = editor.selection.getBookmark(2, true);
       
 16581 				}
       
 16582 			},
       
 16583 
       
 16584 			/**
       
 16585 			 * Adds a new undo level/snapshot to the undo list.
       
 16586 			 *
       
 16587 			 * @method add
       
 16588 			 * @param {Object} level Optional undo level object to add.
       
 16589 			 * @param {DOMEvent} Event Optional event responsible for the creation of the undo level.
       
 16590 			 * @return {Object} Undo level that got added or null it a level wasn't needed.
       
 16591 			 */
       
 16592 			add: function(level, event) {
       
 16593 				var i, settings = editor.settings, lastLevel;
       
 16594 
       
 16595 				level = level || {};
       
 16596 				level.content = getContent();
       
 16597 
       
 16598 				if (locks || editor.removed) {
       
 16599 					return null;
       
 16600 				}
       
 16601 
       
 16602 				lastLevel = data[index];
       
 16603 				if (editor.fire('BeforeAddUndo', {level: level, lastLevel: lastLevel, originalEvent: event}).isDefaultPrevented()) {
       
 16604 					return null;
       
 16605 				}
       
 16606 
       
 16607 				// Add undo level if needed
       
 16608 				if (lastLevel && lastLevel.content == level.content) {
       
 16609 					return null;
       
 16610 				}
       
 16611 
       
 16612 				// Set before bookmark on previous level
       
 16613 				if (data[index]) {
       
 16614 					data[index].beforeBookmark = beforeBookmark;
       
 16615 				}
       
 16616 
       
 16617 				// Time to compress
       
 16618 				if (settings.custom_undo_redo_levels) {
       
 16619 					if (data.length > settings.custom_undo_redo_levels) {
       
 16620 						for (i = 0; i < data.length - 1; i++) {
       
 16621 							data[i] = data[i + 1];
       
 16622 						}
       
 16623 
       
 16624 						data.length--;
       
 16625 						index = data.length;
       
 16626 					}
       
 16627 				}
       
 16628 
       
 16629 				// Get a non intrusive normalized bookmark
       
 16630 				level.bookmark = editor.selection.getBookmark(2, true);
       
 16631 
       
 16632 				// Crop array if needed
       
 16633 				if (index < data.length - 1) {
       
 16634 					data.length = index + 1;
       
 16635 				}
       
 16636 
       
 16637 				data.push(level);
       
 16638 				index = data.length - 1;
       
 16639 
       
 16640 				var args = {level: level, lastLevel: lastLevel, originalEvent: event};
       
 16641 
       
 16642 				editor.fire('AddUndo', args);
       
 16643 
       
 16644 				if (index > 0) {
       
 16645 					setDirty(true);
       
 16646 					editor.fire('change', args);
       
 16647 				}
       
 16648 
       
 16649 				return level;
       
 16650 			},
       
 16651 
       
 16652 			/**
       
 16653 			 * Undoes the last action.
       
 16654 			 *
       
 16655 			 * @method undo
       
 16656 			 * @return {Object} Undo level or null if no undo was performed.
       
 16657 			 */
       
 16658 			undo: function() {
       
 16659 				var level;
       
 16660 
       
 16661 				if (self.typing) {
       
 16662 					self.add();
       
 16663 					self.typing = false;
       
 16664 				}
       
 16665 
       
 16666 				if (index > 0) {
       
 16667 					level = data[--index];
       
 16668 
       
 16669 					// Undo to first index then set dirty state to false
       
 16670 					if (index === 0) {
       
 16671 						setDirty(false);
       
 16672 					}
       
 16673 
       
 16674 					editor.setContent(level.content, {format: 'raw'});
       
 16675 					editor.selection.moveToBookmark(level.beforeBookmark);
       
 16676 
       
 16677 					editor.fire('undo', {level: level});
       
 16678 				}
       
 16679 
       
 16680 				return level;
       
 16681 			},
       
 16682 
       
 16683 			/**
       
 16684 			 * Redoes the last action.
       
 16685 			 *
       
 16686 			 * @method redo
       
 16687 			 * @return {Object} Redo level or null if no redo was performed.
       
 16688 			 */
       
 16689 			redo: function() {
       
 16690 				var level;
       
 16691 
       
 16692 				if (index < data.length - 1) {
       
 16693 					level = data[++index];
       
 16694 
       
 16695 					editor.setContent(level.content, {format: 'raw'});
       
 16696 					editor.selection.moveToBookmark(level.bookmark);
       
 16697 					setDirty(true);
       
 16698 
       
 16699 					editor.fire('redo', {level: level});
       
 16700 				}
       
 16701 
       
 16702 				return level;
       
 16703 			},
       
 16704 
       
 16705 			/**
       
 16706 			 * Removes all undo levels.
       
 16707 			 *
       
 16708 			 * @method clear
       
 16709 			 */
       
 16710 			clear: function() {
       
 16711 				data = [];
       
 16712 				index = 0;
       
 16713 				self.typing = false;
       
 16714 				editor.fire('ClearUndos');
       
 16715 			},
       
 16716 
       
 16717 			/**
       
 16718 			 * Returns true/false if the undo manager has any undo levels.
       
 16719 			 *
       
 16720 			 * @method hasUndo
       
 16721 			 * @return {Boolean} true/false if the undo manager has any undo levels.
       
 16722 			 */
       
 16723 			hasUndo: function() {
       
 16724 				// Has undo levels or typing and content isn't the same as the initial level
       
 16725 				return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
       
 16726 			},
       
 16727 
       
 16728 			/**
       
 16729 			 * Returns true/false if the undo manager has any redo levels.
       
 16730 			 *
       
 16731 			 * @method hasRedo
       
 16732 			 * @return {Boolean} true/false if the undo manager has any redo levels.
       
 16733 			 */
       
 16734 			hasRedo: function() {
       
 16735 				return index < data.length - 1 && !this.typing;
       
 16736 			},
       
 16737 
       
 16738 			/**
       
 16739 			 * Executes the specified function in an undo transation. The selection
       
 16740 			 * before the modification will be stored to the undo stack and if the DOM changes
       
 16741 			 * it will add a new undo level. Any methods within the transation that adds undo levels will
       
 16742 			 * be ignored. So a transation can include calls to execCommand or editor.insertContent.
       
 16743 			 *
       
 16744 			 * @method transact
       
 16745 			 * @param {function} callback Function to execute dom manipulation logic in.
       
 16746 			 */
       
 16747 			transact: function(callback) {
       
 16748 				self.beforeChange();
       
 16749 
       
 16750 				try {
       
 16751 					locks++;
       
 16752 					callback();
       
 16753 				} finally {
       
 16754 					locks--;
       
 16755 				}
       
 16756 
       
 16757 				self.add();
       
 16758 			}
       
 16759 		};
       
 16760 
       
 16761 		return self;
       
 16762 	};
       
 16763 });
       
 16764 
       
 16765 // Included from: js/tinymce/classes/EnterKey.js
       
 16766 
       
 16767 /**
       
 16768  * EnterKey.js
       
 16769  *
       
 16770  * Copyright, Moxiecode Systems AB
       
 16771  * Released under LGPL License.
       
 16772  *
       
 16773  * License: http://www.tinymce.com/license
       
 16774  * Contributing: http://www.tinymce.com/contributing
       
 16775  */
       
 16776 
       
 16777 /**
       
 16778  * Contains logic for handling the enter key to split/generate block elements.
       
 16779  */
       
 16780 define("tinymce/EnterKey", [
       
 16781 	"tinymce/dom/TreeWalker",
       
 16782 	"tinymce/dom/RangeUtils",
       
 16783 	"tinymce/Env"
       
 16784 ], function(TreeWalker, RangeUtils, Env) {
       
 16785 	var isIE = Env.ie && Env.ie < 11;
       
 16786 
       
 16787 	return function(editor) {
       
 16788 		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
       
 16789 		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements(),
       
 16790 			moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements();
       
 16791 
       
 16792 		function handleEnterKey(evt) {
       
 16793 			var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
       
 16794 				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
       
 16795 
       
 16796 			// Returns true if the block can be split into two blocks or not
       
 16797 			function canSplitBlock(node) {
       
 16798 				return node &&
       
 16799 					dom.isBlock(node) &&
       
 16800 					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
       
 16801 					!/^(fixed|absolute)/i.test(node.style.position) &&
       
 16802 					dom.getContentEditable(node) !== "true";
       
 16803 			}
       
 16804 
       
 16805 			// Renders empty block on IE
       
 16806 			function renderBlockOnIE(block) {
       
 16807 				var oldRng;
       
 16808 
       
 16809 				if (dom.isBlock(block)) {
       
 16810 					oldRng = selection.getRng();
       
 16811 					block.appendChild(dom.create('span', null, '\u00a0'));
       
 16812 					selection.select(block);
       
 16813 					block.lastChild.outerHTML = '';
       
 16814 					selection.setRng(oldRng);
       
 16815 				}
       
 16816 			}
       
 16817 
       
 16818 			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
       
 16819 			function trimInlineElementsOnLeftSideOfBlock(block) {
       
 16820 				var node = block, firstChilds = [], i;
       
 16821 
       
 16822 				if (!node) {
       
 16823 					return;
       
 16824 				}
       
 16825 
       
 16826 				// Find inner most first child ex: <p><i><b>*</b></i></p>
       
 16827 				while ((node = node.firstChild)) {
       
 16828 					if (dom.isBlock(node)) {
       
 16829 						return;
       
 16830 					}
       
 16831 
       
 16832 					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
 16833 						firstChilds.push(node);
       
 16834 					}
       
 16835 				}
       
 16836 
       
 16837 				i = firstChilds.length;
       
 16838 				while (i--) {
       
 16839 					node = firstChilds[i];
       
 16840 					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
       
 16841 						dom.remove(node);
       
 16842 					} else {
       
 16843 						// Remove <a> </a> see #5381
       
 16844 						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
       
 16845 							dom.remove(node);
       
 16846 						}
       
 16847 					}
       
 16848 				}
       
 16849 			}
       
 16850 
       
 16851 			// Moves the caret to a suitable position within the root for example in the first non
       
 16852 			// pure whitespace text node or before an image
       
 16853 			function moveToCaretPosition(root) {
       
 16854 				var walker, node, rng, lastNode = root, tempElm;
       
 16855 				function firstNonWhiteSpaceNodeSibling(node) {
       
 16856 					while (node) {
       
 16857 						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
       
 16858 							return node;
       
 16859 						}
       
 16860 
       
 16861 						node = node.nextSibling;
       
 16862 					}
       
 16863 				}
       
 16864 
       
 16865 				if (!root) {
       
 16866 					return;
       
 16867 				}
       
 16868 
       
 16869 				// Old IE versions doesn't properly render blocks with br elements in them
       
 16870 				// For example <p><br></p> wont be rendered correctly in a contentEditable area
       
 16871 				// until you remove the br producing <p></p>
       
 16872 				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
       
 16873 					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
       
 16874 						dom.remove(parentBlock.firstChild);
       
 16875 					}
       
 16876 				}
       
 16877 
       
 16878 				if (/^(LI|DT|DD)$/.test(root.nodeName)) {
       
 16879 					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
       
 16880 
       
 16881 					if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
       
 16882 						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
       
 16883 					}
       
 16884 				}
       
 16885 
       
 16886 				rng = dom.createRng();
       
 16887 
       
 16888 				// Normalize whitespace to remove empty text nodes. Fix for: #6904
       
 16889 				// Gecko will be able to place the caret in empty text nodes but it won't render propery
       
 16890 				// Older IE versions will sometimes crash so for now ignore all IE versions
       
 16891 				if (!Env.ie) {
       
 16892 					root.normalize();
       
 16893 				}
       
 16894 
       
 16895 				if (root.hasChildNodes()) {
       
 16896 					walker = new TreeWalker(root, root);
       
 16897 
       
 16898 					while ((node = walker.current())) {
       
 16899 						if (node.nodeType == 3) {
       
 16900 							rng.setStart(node, 0);
       
 16901 							rng.setEnd(node, 0);
       
 16902 							break;
       
 16903 						}
       
 16904 
       
 16905 						if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) {
       
 16906 							rng.setStartBefore(node);
       
 16907 							rng.setEndBefore(node);
       
 16908 							break;
       
 16909 						}
       
 16910 
       
 16911 						lastNode = node;
       
 16912 						node = walker.next();
       
 16913 					}
       
 16914 
       
 16915 					if (!node) {
       
 16916 						rng.setStart(lastNode, 0);
       
 16917 						rng.setEnd(lastNode, 0);
       
 16918 					}
       
 16919 				} else {
       
 16920 					if (root.nodeName == 'BR') {
       
 16921 						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
       
 16922 							// Trick on older IE versions to render the caret before the BR between two lists
       
 16923 							if (!documentMode || documentMode < 9) {
       
 16924 								tempElm = dom.create('br');
       
 16925 								root.parentNode.insertBefore(tempElm, root);
       
 16926 							}
       
 16927 
       
 16928 							rng.setStartBefore(root);
       
 16929 							rng.setEndBefore(root);
       
 16930 						} else {
       
 16931 							rng.setStartAfter(root);
       
 16932 							rng.setEndAfter(root);
       
 16933 						}
       
 16934 					} else {
       
 16935 						rng.setStart(root, 0);
       
 16936 						rng.setEnd(root, 0);
       
 16937 					}
       
 16938 				}
       
 16939 
       
 16940 				selection.setRng(rng);
       
 16941 
       
 16942 				// Remove tempElm created for old IE:s
       
 16943 				dom.remove(tempElm);
       
 16944 				selection.scrollIntoView(root);
       
 16945 			}
       
 16946 
       
 16947 			function setForcedBlockAttrs(node) {
       
 16948 				var forcedRootBlockName = settings.forced_root_block;
       
 16949 
       
 16950 				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
       
 16951 					dom.setAttribs(node, settings.forced_root_block_attrs);
       
 16952 				}
       
 16953 			}
       
 16954 
       
 16955 			// Creates a new block element by cloning the current one or creating a new one if the name is specified
       
 16956 			// This function will also copy any text formatting from the parent block and add it to the new one
       
 16957 			function createNewBlock(name) {
       
 16958 				var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements();
       
 16959 
       
 16960 				if (name || parentBlockName == "TABLE") {
       
 16961 					block = dom.create(name || newBlockName);
       
 16962 					setForcedBlockAttrs(block);
       
 16963 				} else {
       
 16964 					block = parentBlock.cloneNode(false);
       
 16965 				}
       
 16966 
       
 16967 				caretNode = block;
       
 16968 
       
 16969 				// Clone any parent styles
       
 16970 				if (settings.keep_styles !== false) {
       
 16971 					do {
       
 16972 						if (textInlineElements[node.nodeName]) {
       
 16973 							// Never clone a caret containers
       
 16974 							if (node.id == '_mce_caret') {
       
 16975 								continue;
       
 16976 							}
       
 16977 
       
 16978 							clonedNode = node.cloneNode(false);
       
 16979 							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
       
 16980 
       
 16981 							if (block.hasChildNodes()) {
       
 16982 								clonedNode.appendChild(block.firstChild);
       
 16983 								block.appendChild(clonedNode);
       
 16984 							} else {
       
 16985 								caretNode = clonedNode;
       
 16986 								block.appendChild(clonedNode);
       
 16987 							}
       
 16988 						}
       
 16989 					} while ((node = node.parentNode));
       
 16990 				}
       
 16991 
       
 16992 				// BR is needed in empty blocks on non IE browsers
       
 16993 				if (!isIE) {
       
 16994 					caretNode.innerHTML = '<br data-mce-bogus="1">';
       
 16995 				}
       
 16996 
       
 16997 				return block;
       
 16998 			}
       
 16999 
       
 17000 			// Returns true/false if the caret is at the start/end of the parent block element
       
 17001 			function isCaretAtStartOrEndOfBlock(start) {
       
 17002 				var walker, node, name;
       
 17003 
       
 17004 				// Caret is in the middle of a text node like "a|b"
       
 17005 				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
       
 17006 					return false;
       
 17007 				}
       
 17008 
       
 17009 				// If after the last element in block node edge case for #5091
       
 17010 				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
       
 17011 					return true;
       
 17012 				}
       
 17013 
       
 17014 				// If the caret if before the first element in parentBlock
       
 17015 				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
       
 17016 					return true;
       
 17017 				}
       
 17018 
       
 17019 				// Caret can be before/after a table
       
 17020 				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
       
 17021 					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
       
 17022 				}
       
 17023 
       
 17024 				// Walk the DOM and look for text nodes or non empty elements
       
 17025 				walker = new TreeWalker(container, parentBlock);
       
 17026 
       
 17027 				// If caret is in beginning or end of a text block then jump to the next/previous node
       
 17028 				if (container.nodeType == 3) {
       
 17029 					if (start && offset === 0) {
       
 17030 						walker.prev();
       
 17031 					} else if (!start && offset == container.nodeValue.length) {
       
 17032 						walker.next();
       
 17033 					}
       
 17034 				}
       
 17035 
       
 17036 				while ((node = walker.current())) {
       
 17037 					if (node.nodeType === 1) {
       
 17038 						// Ignore bogus elements
       
 17039 						if (!node.getAttribute('data-mce-bogus')) {
       
 17040 							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
       
 17041 							name = node.nodeName.toLowerCase();
       
 17042 							if (nonEmptyElementsMap[name] && name !== 'br') {
       
 17043 								return false;
       
 17044 							}
       
 17045 						}
       
 17046 					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
       
 17047 						return false;
       
 17048 					}
       
 17049 
       
 17050 					if (start) {
       
 17051 						walker.prev();
       
 17052 					} else {
       
 17053 						walker.next();
       
 17054 					}
       
 17055 				}
       
 17056 
       
 17057 				return true;
       
 17058 			}
       
 17059 
       
 17060 			// Wraps any text nodes or inline elements in the specified forced root block name
       
 17061 			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
       
 17062 				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
       
 17063 
       
 17064 				// Not in a block element or in a table cell or caption
       
 17065 				parentBlock = dom.getParent(container, dom.isBlock);
       
 17066 				rootBlockName = editor.getBody().nodeName.toLowerCase();
       
 17067 				if (!parentBlock || !canSplitBlock(parentBlock)) {
       
 17068 					parentBlock = parentBlock || editableRoot;
       
 17069 
       
 17070 					if (!parentBlock.hasChildNodes()) {
       
 17071 						newBlock = dom.create(blockName);
       
 17072 						setForcedBlockAttrs(newBlock);
       
 17073 						parentBlock.appendChild(newBlock);
       
 17074 						rng.setStart(newBlock, 0);
       
 17075 						rng.setEnd(newBlock, 0);
       
 17076 						return newBlock;
       
 17077 					}
       
 17078 
       
 17079 					// Find parent that is the first child of parentBlock
       
 17080 					node = container;
       
 17081 					while (node.parentNode != parentBlock) {
       
 17082 						node = node.parentNode;
       
 17083 					}
       
 17084 
       
 17085 					// Loop left to find start node start wrapping at
       
 17086 					while (node && !dom.isBlock(node)) {
       
 17087 						startNode = node;
       
 17088 						node = node.previousSibling;
       
 17089 					}
       
 17090 
       
 17091 					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
       
 17092 						newBlock = dom.create(blockName);
       
 17093 						setForcedBlockAttrs(newBlock);
       
 17094 						startNode.parentNode.insertBefore(newBlock, startNode);
       
 17095 
       
 17096 						// Start wrapping until we hit a block
       
 17097 						node = startNode;
       
 17098 						while (node && !dom.isBlock(node)) {
       
 17099 							next = node.nextSibling;
       
 17100 							newBlock.appendChild(node);
       
 17101 							node = next;
       
 17102 						}
       
 17103 
       
 17104 						// Restore range to it's past location
       
 17105 						rng.setStart(container, offset);
       
 17106 						rng.setEnd(container, offset);
       
 17107 					}
       
 17108 				}
       
 17109 
       
 17110 				return container;
       
 17111 			}
       
 17112 
       
 17113 			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
       
 17114 			function handleEmptyListItem() {
       
 17115 				function isFirstOrLastLi(first) {
       
 17116 					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
       
 17117 
       
 17118 					// Find first/last element since there might be whitespace there
       
 17119 					while (node) {
       
 17120 						if (node.nodeType == 1) {
       
 17121 							break;
       
 17122 						}
       
 17123 
       
 17124 						node = node[first ? 'nextSibling' : 'previousSibling'];
       
 17125 					}
       
 17126 
       
 17127 					return node === parentBlock;
       
 17128 				}
       
 17129 
       
 17130 				function getContainerBlock() {
       
 17131 					var containerBlockParent = containerBlock.parentNode;
       
 17132 
       
 17133 					if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) {
       
 17134 						return containerBlockParent;
       
 17135 					}
       
 17136 
       
 17137 					return containerBlock;
       
 17138 				}
       
 17139 
       
 17140 				// Check if we are in an nested list
       
 17141 				var containerBlockParentName = containerBlock.parentNode.nodeName;
       
 17142 				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
       
 17143 					newBlockName = 'LI';
       
 17144 				}
       
 17145 
       
 17146 				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
       
 17147 
       
 17148 				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
       
 17149 					if (containerBlockParentName == 'LI') {
       
 17150 						// Nested list is inside a LI
       
 17151 						dom.insertAfter(newBlock, getContainerBlock());
       
 17152 					} else {
       
 17153 						// Is first and last list item then replace the OL/UL with a text block
       
 17154 						dom.replace(newBlock, containerBlock);
       
 17155 					}
       
 17156 				} else if (isFirstOrLastLi(true)) {
       
 17157 					if (containerBlockParentName == 'LI') {
       
 17158 						// List nested in an LI then move the list to a new sibling LI
       
 17159 						dom.insertAfter(newBlock, getContainerBlock());
       
 17160 						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
       
 17161 						newBlock.appendChild(containerBlock);
       
 17162 					} else {
       
 17163 						// First LI in list then remove LI and add text block before list
       
 17164 						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
       
 17165 					}
       
 17166 				} else if (isFirstOrLastLi()) {
       
 17167 					// Last LI in list then remove LI and add text block after list
       
 17168 					dom.insertAfter(newBlock, getContainerBlock());
       
 17169 					renderBlockOnIE(newBlock);
       
 17170 				} else {
       
 17171 					// Middle LI in list the split the list and insert a text block in the middle
       
 17172 					// Extract after fragment and insert it after the current block
       
 17173 					containerBlock = getContainerBlock();
       
 17174 					tmpRng = rng.cloneRange();
       
 17175 					tmpRng.setStartAfter(parentBlock);
       
 17176 					tmpRng.setEndAfter(containerBlock);
       
 17177 					fragment = tmpRng.extractContents();
       
 17178 
       
 17179 					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
       
 17180 						newBlock = fragment.firstChild;
       
 17181 						dom.insertAfter(fragment, containerBlock);
       
 17182 					} else {
       
 17183 						dom.insertAfter(fragment, containerBlock);
       
 17184 						dom.insertAfter(newBlock, containerBlock);
       
 17185 					}
       
 17186 				}
       
 17187 
       
 17188 				dom.remove(parentBlock);
       
 17189 				moveToCaretPosition(newBlock);
       
 17190 				undoManager.add();
       
 17191 			}
       
 17192 
       
 17193 			// Inserts a BR element if the forced_root_block option is set to false or empty string
       
 17194 			function insertBr() {
       
 17195 				editor.execCommand("InsertLineBreak", false, evt);
       
 17196 			}
       
 17197 
       
 17198 			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
       
 17199 			function trimLeadingLineBreaks(node) {
       
 17200 				do {
       
 17201 					if (node.nodeType === 3) {
       
 17202 						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
       
 17203 					}
       
 17204 
       
 17205 					node = node.firstChild;
       
 17206 				} while (node);
       
 17207 			}
       
 17208 
       
 17209 			function getEditableRoot(node) {
       
 17210 				var root = dom.getRoot(), parent, editableRoot;
       
 17211 
       
 17212 				// Get all parents until we hit a non editable parent or the root
       
 17213 				parent = node;
       
 17214 				while (parent !== root && dom.getContentEditable(parent) !== "false") {
       
 17215 					if (dom.getContentEditable(parent) === "true") {
       
 17216 						editableRoot = parent;
       
 17217 					}
       
 17218 
       
 17219 					parent = parent.parentNode;
       
 17220 				}
       
 17221 
       
 17222 				return parent !== root ? editableRoot : root;
       
 17223 			}
       
 17224 
       
 17225 			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
       
 17226 			// these might be floated and then they won't expand the block
       
 17227 			function addBrToBlockIfNeeded(block) {
       
 17228 				var lastChild;
       
 17229 
       
 17230 				// IE will render the blocks correctly other browsers needs a BR
       
 17231 				if (!isIE) {
       
 17232 					block.normalize(); // Remove empty text nodes that got left behind by the extract
       
 17233 
       
 17234 					// Check if the block is empty or contains a floated last child
       
 17235 					lastChild = block.lastChild;
       
 17236 					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
       
 17237 						dom.add(block, 'br');
       
 17238 					}
       
 17239 				}
       
 17240 			}
       
 17241 
       
 17242 			rng = selection.getRng(true);
       
 17243 
       
 17244 			// Event is blocked by some other handler for example the lists plugin
       
 17245 			if (evt.isDefaultPrevented()) {
       
 17246 				return;
       
 17247 			}
       
 17248 
       
 17249 			// Delete any selected contents
       
 17250 			if (!rng.collapsed) {
       
 17251 				editor.execCommand('Delete');
       
 17252 				return;
       
 17253 			}
       
 17254 
       
 17255 			// Setup range items and newBlockName
       
 17256 			new RangeUtils(dom).normalize(rng);
       
 17257 			container = rng.startContainer;
       
 17258 			offset = rng.startOffset;
       
 17259 			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
       
 17260 			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
       
 17261 			documentMode = dom.doc.documentMode;
       
 17262 			shiftKey = evt.shiftKey;
       
 17263 
       
 17264 			// Resolve node index
       
 17265 			if (container.nodeType == 1 && container.hasChildNodes()) {
       
 17266 				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
       
 17267 
       
 17268 				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
       
 17269 				if (isAfterLastNodeInContainer && container.nodeType == 3) {
       
 17270 					offset = container.nodeValue.length;
       
 17271 				} else {
       
 17272 					offset = 0;
       
 17273 				}
       
 17274 			}
       
 17275 
       
 17276 			// Get editable root node normaly the body element but sometimes a div or span
       
 17277 			editableRoot = getEditableRoot(container);
       
 17278 
       
 17279 			// If there is no editable root then enter is done inside a contentEditable false element
       
 17280 			if (!editableRoot) {
       
 17281 				return;
       
 17282 			}
       
 17283 
       
 17284 			undoManager.beforeChange();
       
 17285 
       
 17286 			// If editable root isn't block nor the root of the editor
       
 17287 			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
       
 17288 				if (!newBlockName || shiftKey) {
       
 17289 					insertBr();
       
 17290 				}
       
 17291 
       
 17292 				return;
       
 17293 			}
       
 17294 
       
 17295 			// Wrap the current node and it's sibling in a default block if it's needed.
       
 17296 			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
       
 17297 			// This won't happen if root blocks are disabled or the shiftKey is pressed
       
 17298 			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
       
 17299 				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
       
 17300 			}
       
 17301 
       
 17302 			// Find parent block and setup empty block paddings
       
 17303 			parentBlock = dom.getParent(container, dom.isBlock);
       
 17304 			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
       
 17305 
       
 17306 			// Setup block names
       
 17307 			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 17308 			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 17309 
       
 17310 			// Enter inside block contained within a LI then split or insert before/after LI
       
 17311 			if (containerBlockName == 'LI' && !evt.ctrlKey) {
       
 17312 				parentBlock = containerBlock;
       
 17313 				parentBlockName = containerBlockName;
       
 17314 			}
       
 17315 
       
 17316 			// Handle enter in list item
       
 17317 			if (/^(LI|DT|DD)$/.test(parentBlockName)) {
       
 17318 				if (!newBlockName && shiftKey) {
       
 17319 					insertBr();
       
 17320 					return;
       
 17321 				}
       
 17322 
       
 17323 				// Handle enter inside an empty list item
       
 17324 				if (dom.isEmpty(parentBlock)) {
       
 17325 					handleEmptyListItem();
       
 17326 					return;
       
 17327 				}
       
 17328 			}
       
 17329 
       
 17330 			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
       
 17331 			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
       
 17332 				if (!shiftKey) {
       
 17333 					insertBr();
       
 17334 					return;
       
 17335 				}
       
 17336 			} else {
       
 17337 				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
       
 17338 				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
       
 17339 					insertBr();
       
 17340 					return;
       
 17341 				}
       
 17342 			}
       
 17343 
       
 17344 			// If parent block is root then never insert new blocks
       
 17345 			if (newBlockName && parentBlock === editor.getBody()) {
       
 17346 				return;
       
 17347 			}
       
 17348 
       
 17349 			// Default block name if it's not configured
       
 17350 			newBlockName = newBlockName || 'P';
       
 17351 
       
 17352 			// Insert new block before/after the parent block depending on caret location
       
 17353 			if (isCaretAtStartOrEndOfBlock()) {
       
 17354 				// 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
       
 17355 				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
       
 17356 					newBlock = createNewBlock(newBlockName);
       
 17357 				} else {
       
 17358 					newBlock = createNewBlock();
       
 17359 				}
       
 17360 
       
 17361 				// Split the current container block element if enter is pressed inside an empty inner block element
       
 17362 				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
       
 17363 					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
       
 17364 					newBlock = dom.split(containerBlock, parentBlock);
       
 17365 				} else {
       
 17366 					dom.insertAfter(newBlock, parentBlock);
       
 17367 				}
       
 17368 
       
 17369 				moveToCaretPosition(newBlock);
       
 17370 			} else if (isCaretAtStartOrEndOfBlock(true)) {
       
 17371 				// Insert new block before
       
 17372 				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
       
 17373 				renderBlockOnIE(newBlock);
       
 17374 				moveToCaretPosition(parentBlock);
       
 17375 			} else {
       
 17376 				// Extract after fragment and insert it after the current block
       
 17377 				tmpRng = rng.cloneRange();
       
 17378 				tmpRng.setEndAfter(parentBlock);
       
 17379 				fragment = tmpRng.extractContents();
       
 17380 				trimLeadingLineBreaks(fragment);
       
 17381 				newBlock = fragment.firstChild;
       
 17382 				dom.insertAfter(fragment, parentBlock);
       
 17383 				trimInlineElementsOnLeftSideOfBlock(newBlock);
       
 17384 				addBrToBlockIfNeeded(parentBlock);
       
 17385 				moveToCaretPosition(newBlock);
       
 17386 			}
       
 17387 
       
 17388 			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
       
 17389 
       
 17390 			// Allow custom handling of new blocks
       
 17391 			editor.fire('NewBlock', {newBlock: newBlock});
       
 17392 
       
 17393 			undoManager.add();
       
 17394 		}
       
 17395 
       
 17396 		editor.on('keydown', function(evt) {
       
 17397 			if (evt.keyCode == 13) {
       
 17398 				if (handleEnterKey(evt) !== false) {
       
 17399 					evt.preventDefault();
       
 17400 				}
       
 17401 			}
       
 17402 		});
       
 17403 	};
       
 17404 });
       
 17405 
       
 17406 // Included from: js/tinymce/classes/ForceBlocks.js
       
 17407 
       
 17408 /**
       
 17409  * ForceBlocks.js
       
 17410  *
       
 17411  * Copyright, Moxiecode Systems AB
       
 17412  * Released under LGPL License.
       
 17413  *
       
 17414  * License: http://www.tinymce.com/license
       
 17415  * Contributing: http://www.tinymce.com/contributing
       
 17416  */
       
 17417 
       
 17418 define("tinymce/ForceBlocks", [], function() {
       
 17419 	return function(editor) {
       
 17420 		var settings = editor.settings, dom = editor.dom, selection = editor.selection;
       
 17421 		var schema = editor.schema, blockElements = schema.getBlockElements();
       
 17422 
       
 17423 		function addRootBlocks() {
       
 17424 			var node = selection.getStart(), rootNode = editor.getBody(), rng;
       
 17425 			var startContainer, startOffset, endContainer, endOffset, rootBlockNode;
       
 17426 			var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection;
       
 17427 			var tmpRng, rootNodeName, forcedRootBlock;
       
 17428 
       
 17429 			forcedRootBlock = settings.forced_root_block;
       
 17430 
       
 17431 			if (!node || node.nodeType !== 1 || !forcedRootBlock) {
       
 17432 				return;
       
 17433 			}
       
 17434 
       
 17435 			// Check if node is wrapped in block
       
 17436 			while (node && node != rootNode) {
       
 17437 				if (blockElements[node.nodeName]) {
       
 17438 					return;
       
 17439 				}
       
 17440 
       
 17441 				node = node.parentNode;
       
 17442 			}
       
 17443 
       
 17444 			// Get current selection
       
 17445 			rng = selection.getRng();
       
 17446 			if (rng.setStart) {
       
 17447 				startContainer = rng.startContainer;
       
 17448 				startOffset = rng.startOffset;
       
 17449 				endContainer = rng.endContainer;
       
 17450 				endOffset = rng.endOffset;
       
 17451 
       
 17452 				try {
       
 17453 					restoreSelection = editor.getDoc().activeElement === rootNode;
       
 17454 				} catch (ex) {
       
 17455 					// IE throws unspecified error here sometimes
       
 17456 				}
       
 17457 			} else {
       
 17458 				// Force control range into text range
       
 17459 				if (rng.item) {
       
 17460 					node = rng.item(0);
       
 17461 					rng = editor.getDoc().body.createTextRange();
       
 17462 					rng.moveToElementText(node);
       
 17463 				}
       
 17464 
       
 17465 				restoreSelection = rng.parentElement().ownerDocument === editor.getDoc();
       
 17466 				tmpRng = rng.duplicate();
       
 17467 				tmpRng.collapse(true);
       
 17468 				startOffset = tmpRng.move('character', offset) * -1;
       
 17469 
       
 17470 				if (!tmpRng.collapsed) {
       
 17471 					tmpRng = rng.duplicate();
       
 17472 					tmpRng.collapse(false);
       
 17473 					endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
       
 17474 				}
       
 17475 			}
       
 17476 
       
 17477 			// Wrap non block elements and text nodes
       
 17478 			node = rootNode.firstChild;
       
 17479 			rootNodeName = rootNode.nodeName.toLowerCase();
       
 17480 			while (node) {
       
 17481 				// TODO: Break this up, too complex
       
 17482 				if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) &&
       
 17483 					schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) {
       
 17484 					// Remove empty text nodes
       
 17485 					if (node.nodeType === 3 && node.nodeValue.length === 0) {
       
 17486 						tempNode = node;
       
 17487 						node = node.nextSibling;
       
 17488 						dom.remove(tempNode);
       
 17489 						continue;
       
 17490 					}
       
 17491 
       
 17492 					if (!rootBlockNode) {
       
 17493 						rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs);
       
 17494 						node.parentNode.insertBefore(rootBlockNode, node);
       
 17495 						wrapped = true;
       
 17496 					}
       
 17497 
       
 17498 					tempNode = node;
       
 17499 					node = node.nextSibling;
       
 17500 					rootBlockNode.appendChild(tempNode);
       
 17501 				} else {
       
 17502 					rootBlockNode = null;
       
 17503 					node = node.nextSibling;
       
 17504 				}
       
 17505 			}
       
 17506 
       
 17507 			if (wrapped && restoreSelection) {
       
 17508 				if (rng.setStart) {
       
 17509 					rng.setStart(startContainer, startOffset);
       
 17510 					rng.setEnd(endContainer, endOffset);
       
 17511 					selection.setRng(rng);
       
 17512 				} else {
       
 17513 					// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
       
 17514 					try {
       
 17515 						rng = editor.getDoc().body.createTextRange();
       
 17516 						rng.moveToElementText(rootNode);
       
 17517 						rng.collapse(true);
       
 17518 						rng.moveStart('character', startOffset);
       
 17519 
       
 17520 						if (endOffset > 0) {
       
 17521 							rng.moveEnd('character', endOffset);
       
 17522 						}
       
 17523 
       
 17524 						rng.select();
       
 17525 					} catch (ex) {
       
 17526 						// Ignore
       
 17527 					}
       
 17528 				}
       
 17529 
       
 17530 				editor.nodeChanged();
       
 17531 			}
       
 17532 		}
       
 17533 
       
 17534 		// Force root blocks
       
 17535 		if (settings.forced_root_block) {
       
 17536 			editor.on('NodeChange', addRootBlocks);
       
 17537 		}
       
 17538 	};
       
 17539 });
       
 17540 
       
 17541 // Included from: js/tinymce/classes/EditorCommands.js
       
 17542 
       
 17543 /**
       
 17544  * EditorCommands.js
       
 17545  *
       
 17546  * Copyright, Moxiecode Systems AB
       
 17547  * Released under LGPL License.
       
 17548  *
       
 17549  * License: http://www.tinymce.com/license
       
 17550  * Contributing: http://www.tinymce.com/contributing
       
 17551  */
       
 17552 
       
 17553 /**
       
 17554  * This class enables you to add custom editor commands and it contains
       
 17555  * overrides for native browser commands to address various bugs and issues.
       
 17556  *
       
 17557  * @class tinymce.EditorCommands
       
 17558  */
       
 17559 define("tinymce/EditorCommands", [
       
 17560 	"tinymce/html/Serializer",
       
 17561 	"tinymce/Env",
       
 17562 	"tinymce/util/Tools",
       
 17563 	"tinymce/dom/ElementUtils",
       
 17564 	"tinymce/dom/RangeUtils",
       
 17565 	"tinymce/dom/TreeWalker"
       
 17566 ], function(Serializer, Env, Tools, ElementUtils, RangeUtils, TreeWalker) {
       
 17567 	// Added for compression purposes
       
 17568 	var each = Tools.each, extend = Tools.extend;
       
 17569 	var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
       
 17570 	var isGecko = Env.gecko, isIE = Env.ie, isOldIE = Env.ie && Env.ie < 11;
       
 17571 	var TRUE = true, FALSE = false;
       
 17572 
       
 17573 	return function(editor) {
       
 17574 		var dom, selection, formatter,
       
 17575 			commands = {state: {}, exec: {}, value: {}},
       
 17576 			settings = editor.settings,
       
 17577 			bookmark;
       
 17578 
       
 17579 		editor.on('PreInit', function() {
       
 17580 			dom = editor.dom;
       
 17581 			selection = editor.selection;
       
 17582 			settings = editor.settings;
       
 17583 			formatter = editor.formatter;
       
 17584 		});
       
 17585 
       
 17586 		/**
       
 17587 		 * Executes the specified command.
       
 17588 		 *
       
 17589 		 * @method execCommand
       
 17590 		 * @param {String} command Command to execute.
       
 17591 		 * @param {Boolean} ui Optional user interface state.
       
 17592 		 * @param {Object} value Optional value for command.
       
 17593 		 * @return {Boolean} true/false if the command was found or not.
       
 17594 		 */
       
 17595 		function execCommand(command, ui, value, args) {
       
 17596 			var func, customCommand, state = 0;
       
 17597 
       
 17598 			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(command) && (!args || !args.skip_focus)) {
       
 17599 				editor.focus();
       
 17600 			}
       
 17601 
       
 17602 			args = extend({}, args);
       
 17603 			args = editor.fire('BeforeExecCommand', {command: command, ui: ui, value: value});
       
 17604 			if (args.isDefaultPrevented()) {
       
 17605 				return false;
       
 17606 			}
       
 17607 
       
 17608 			customCommand = command.toLowerCase();
       
 17609 			if ((func = commands.exec[customCommand])) {
       
 17610 				func(customCommand, ui, value);
       
 17611 				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 17612 				return true;
       
 17613 			}
       
 17614 
       
 17615 			// Plugin commands
       
 17616 			each(editor.plugins, function(p) {
       
 17617 				if (p.execCommand && p.execCommand(command, ui, value)) {
       
 17618 					editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 17619 					state = true;
       
 17620 					return false;
       
 17621 				}
       
 17622 			});
       
 17623 
       
 17624 			if (state) {
       
 17625 				return state;
       
 17626 			}
       
 17627 
       
 17628 			// Theme commands
       
 17629 			if (editor.theme && editor.theme.execCommand && editor.theme.execCommand(command, ui, value)) {
       
 17630 				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 17631 				return true;
       
 17632 			}
       
 17633 
       
 17634 			// Browser commands
       
 17635 			try {
       
 17636 				state = editor.getDoc().execCommand(command, ui, value);
       
 17637 			} catch (ex) {
       
 17638 				// Ignore old IE errors
       
 17639 			}
       
 17640 
       
 17641 			if (state) {
       
 17642 				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
       
 17643 				return true;
       
 17644 			}
       
 17645 
       
 17646 			return false;
       
 17647 		}
       
 17648 
       
 17649 		/**
       
 17650 		 * Queries the current state for a command for example if the current selection is "bold".
       
 17651 		 *
       
 17652 		 * @method queryCommandState
       
 17653 		 * @param {String} command Command to check the state of.
       
 17654 		 * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
       
 17655 		 */
       
 17656 		function queryCommandState(command) {
       
 17657 			var func;
       
 17658 
       
 17659 			// Is hidden then return undefined
       
 17660 			if (editor._isHidden()) {
       
 17661 				return;
       
 17662 			}
       
 17663 
       
 17664 			command = command.toLowerCase();
       
 17665 			if ((func = commands.state[command])) {
       
 17666 				return func(command);
       
 17667 			}
       
 17668 
       
 17669 			// Browser commands
       
 17670 			try {
       
 17671 				return editor.getDoc().queryCommandState(command);
       
 17672 			} catch (ex) {
       
 17673 				// Fails sometimes see bug: 1896577
       
 17674 			}
       
 17675 
       
 17676 			return false;
       
 17677 		}
       
 17678 
       
 17679 		/**
       
 17680 		 * Queries the command value for example the current fontsize.
       
 17681 		 *
       
 17682 		 * @method queryCommandValue
       
 17683 		 * @param {String} command Command to check the value of.
       
 17684 		 * @return {Object} Command value of false if it's not found.
       
 17685 		 */
       
 17686 		function queryCommandValue(command) {
       
 17687 			var func;
       
 17688 
       
 17689 			// Is hidden then return undefined
       
 17690 			if (editor._isHidden()) {
       
 17691 				return;
       
 17692 			}
       
 17693 
       
 17694 			command = command.toLowerCase();
       
 17695 			if ((func = commands.value[command])) {
       
 17696 				return func(command);
       
 17697 			}
       
 17698 
       
 17699 			// Browser commands
       
 17700 			try {
       
 17701 				return editor.getDoc().queryCommandValue(command);
       
 17702 			} catch (ex) {
       
 17703 				// Fails sometimes see bug: 1896577
       
 17704 			}
       
 17705 		}
       
 17706 
       
 17707 		/**
       
 17708 		 * Adds commands to the command collection.
       
 17709 		 *
       
 17710 		 * @method addCommands
       
 17711 		 * @param {Object} command_list Name/value collection with commands to add, the names can also be comma separated.
       
 17712 		 * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
       
 17713 		 */
       
 17714 		function addCommands(command_list, type) {
       
 17715 			type = type || 'exec';
       
 17716 
       
 17717 			each(command_list, function(callback, command) {
       
 17718 				each(command.toLowerCase().split(','), function(command) {
       
 17719 					commands[type][command] = callback;
       
 17720 				});
       
 17721 			});
       
 17722 		}
       
 17723 
       
 17724 		function addCommand(command, callback, scope) {
       
 17725 			command = command.toLowerCase();
       
 17726 			commands.exec[command] = function(command, ui, value, args) {
       
 17727 				return callback.call(scope || editor, ui, value, args);
       
 17728 			};
       
 17729 		}
       
 17730 
       
 17731 		/**
       
 17732 		 * Returns true/false if the command is supported or not.
       
 17733 		 *
       
 17734 		 * @method queryCommandSupported
       
 17735 		 * @param {String} cmd Command that we check support for.
       
 17736 		 * @return {Boolean} true/false if the command is supported or not.
       
 17737 		 */
       
 17738 		function queryCommandSupported(command) {
       
 17739 			command = command.toLowerCase();
       
 17740 
       
 17741 			if (commands.exec[command]) {
       
 17742 				return true;
       
 17743 			}
       
 17744 
       
 17745 			// Browser commands
       
 17746 			try {
       
 17747 				return editor.getDoc().queryCommandSupported(command);
       
 17748 			} catch (ex) {
       
 17749 				// Fails sometimes see bug: 1896577
       
 17750 			}
       
 17751 
       
 17752 			return false;
       
 17753 		}
       
 17754 
       
 17755 		function addQueryStateHandler(command, callback, scope) {
       
 17756 			command = command.toLowerCase();
       
 17757 			commands.state[command] = function() {
       
 17758 				return callback.call(scope || editor);
       
 17759 			};
       
 17760 		}
       
 17761 
       
 17762 		function addQueryValueHandler(command, callback, scope) {
       
 17763 			command = command.toLowerCase();
       
 17764 			commands.value[command] = function() {
       
 17765 				return callback.call(scope || editor);
       
 17766 			};
       
 17767 		}
       
 17768 
       
 17769 		function hasCustomCommand(command) {
       
 17770 			command = command.toLowerCase();
       
 17771 			return !!commands.exec[command];
       
 17772 		}
       
 17773 
       
 17774 		// Expose public methods
       
 17775 		extend(this, {
       
 17776 			execCommand: execCommand,
       
 17777 			queryCommandState: queryCommandState,
       
 17778 			queryCommandValue: queryCommandValue,
       
 17779 			queryCommandSupported: queryCommandSupported,
       
 17780 			addCommands: addCommands,
       
 17781 			addCommand: addCommand,
       
 17782 			addQueryStateHandler: addQueryStateHandler,
       
 17783 			addQueryValueHandler: addQueryValueHandler,
       
 17784 			hasCustomCommand: hasCustomCommand
       
 17785 		});
       
 17786 
       
 17787 		// Private methods
       
 17788 
       
 17789 		function execNativeCommand(command, ui, value) {
       
 17790 			if (ui === undefined) {
       
 17791 				ui = FALSE;
       
 17792 			}
       
 17793 
       
 17794 			if (value === undefined) {
       
 17795 				value = null;
       
 17796 			}
       
 17797 
       
 17798 			return editor.getDoc().execCommand(command, ui, value);
       
 17799 		}
       
 17800 
       
 17801 		function isFormatMatch(name) {
       
 17802 			return formatter.match(name);
       
 17803 		}
       
 17804 
       
 17805 		function toggleFormat(name, value) {
       
 17806 			formatter.toggle(name, value ? {value: value} : undefined);
       
 17807 			editor.nodeChanged();
       
 17808 		}
       
 17809 
       
 17810 		function storeSelection(type) {
       
 17811 			bookmark = selection.getBookmark(type);
       
 17812 		}
       
 17813 
       
 17814 		function restoreSelection() {
       
 17815 			selection.moveToBookmark(bookmark);
       
 17816 		}
       
 17817 
       
 17818 		// Add execCommand overrides
       
 17819 		addCommands({
       
 17820 			// Ignore these, added for compatibility
       
 17821 			'mceResetDesignMode,mceBeginUndoLevel': function() {},
       
 17822 
       
 17823 			// Add undo manager logic
       
 17824 			'mceEndUndoLevel,mceAddUndoLevel': function() {
       
 17825 				editor.undoManager.add();
       
 17826 			},
       
 17827 
       
 17828 			'Cut,Copy,Paste': function(command) {
       
 17829 				var doc = editor.getDoc(), failed;
       
 17830 
       
 17831 				// Try executing the native command
       
 17832 				try {
       
 17833 					execNativeCommand(command);
       
 17834 				} catch (ex) {
       
 17835 					// Command failed
       
 17836 					failed = TRUE;
       
 17837 				}
       
 17838 
       
 17839 				// Present alert message about clipboard access not being available
       
 17840 				if (failed || !doc.queryCommandSupported(command)) {
       
 17841 					var msg = editor.translate(
       
 17842 						"Your browser doesn't support direct access to the clipboard. " +
       
 17843 						"Please use the Ctrl+X/C/V keyboard shortcuts instead."
       
 17844 					);
       
 17845 
       
 17846 					if (Env.mac) {
       
 17847 						msg = msg.replace(/Ctrl\+/g, '\u2318+');
       
 17848 					}
       
 17849 
       
 17850 					editor.windowManager.alert(msg);
       
 17851 				}
       
 17852 			},
       
 17853 
       
 17854 			// Override unlink command
       
 17855 			unlink: function() {
       
 17856 				if (selection.isCollapsed()) {
       
 17857 					var elm = selection.getNode();
       
 17858 					if (elm.tagName == 'A') {
       
 17859 						editor.dom.remove(elm, true);
       
 17860 					}
       
 17861 
       
 17862 					return;
       
 17863 				}
       
 17864 
       
 17865 				formatter.remove("link");
       
 17866 			},
       
 17867 
       
 17868 			// Override justify commands to use the text formatter engine
       
 17869 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
       
 17870 				var align = command.substring(7);
       
 17871 
       
 17872 				if (align == 'full') {
       
 17873 					align = 'justify';
       
 17874 				}
       
 17875 
       
 17876 				// Remove all other alignments first
       
 17877 				each('left,center,right,justify'.split(','), function(name) {
       
 17878 					if (align != name) {
       
 17879 						formatter.remove('align' + name);
       
 17880 					}
       
 17881 				});
       
 17882 
       
 17883 				toggleFormat('align' + align);
       
 17884 				execCommand('mceRepaint');
       
 17885 			},
       
 17886 
       
 17887 			// Override list commands to fix WebKit bug
       
 17888 			'InsertUnorderedList,InsertOrderedList': function(command) {
       
 17889 				var listElm, listParent;
       
 17890 
       
 17891 				execNativeCommand(command);
       
 17892 
       
 17893 				// WebKit produces lists within block elements so we need to split them
       
 17894 				// we will replace the native list creation logic to custom logic later on
       
 17895 				// TODO: Remove this when the list creation logic is removed
       
 17896 				listElm = dom.getParent(selection.getNode(), 'ol,ul');
       
 17897 				if (listElm) {
       
 17898 					listParent = listElm.parentNode;
       
 17899 
       
 17900 					// If list is within a text block then split that block
       
 17901 					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
       
 17902 						storeSelection();
       
 17903 						dom.split(listParent, listElm);
       
 17904 						restoreSelection();
       
 17905 					}
       
 17906 				}
       
 17907 			},
       
 17908 
       
 17909 			// Override commands to use the text formatter engine
       
 17910 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
       
 17911 				toggleFormat(command);
       
 17912 			},
       
 17913 
       
 17914 			// Override commands to use the text formatter engine
       
 17915 			'ForeColor,HiliteColor,FontName': function(command, ui, value) {
       
 17916 				toggleFormat(command, value);
       
 17917 			},
       
 17918 
       
 17919 			FontSize: function(command, ui, value) {
       
 17920 				var fontClasses, fontSizes;
       
 17921 
       
 17922 				// Convert font size 1-7 to styles
       
 17923 				if (value >= 1 && value <= 7) {
       
 17924 					fontSizes = explode(settings.font_size_style_values);
       
 17925 					fontClasses = explode(settings.font_size_classes);
       
 17926 
       
 17927 					if (fontClasses) {
       
 17928 						value = fontClasses[value - 1] || value;
       
 17929 					} else {
       
 17930 						value = fontSizes[value - 1] || value;
       
 17931 					}
       
 17932 				}
       
 17933 
       
 17934 				toggleFormat(command, value);
       
 17935 			},
       
 17936 
       
 17937 			RemoveFormat: function(command) {
       
 17938 				formatter.remove(command);
       
 17939 			},
       
 17940 
       
 17941 			mceBlockQuote: function() {
       
 17942 				toggleFormat('blockquote');
       
 17943 			},
       
 17944 
       
 17945 			FormatBlock: function(command, ui, value) {
       
 17946 				return toggleFormat(value || 'p');
       
 17947 			},
       
 17948 
       
 17949 			mceCleanup: function() {
       
 17950 				var bookmark = selection.getBookmark();
       
 17951 
       
 17952 				editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
       
 17953 
       
 17954 				selection.moveToBookmark(bookmark);
       
 17955 			},
       
 17956 
       
 17957 			mceRemoveNode: function(command, ui, value) {
       
 17958 				var node = value || selection.getNode();
       
 17959 
       
 17960 				// Make sure that the body node isn't removed
       
 17961 				if (node != editor.getBody()) {
       
 17962 					storeSelection();
       
 17963 					editor.dom.remove(node, TRUE);
       
 17964 					restoreSelection();
       
 17965 				}
       
 17966 			},
       
 17967 
       
 17968 			mceSelectNodeDepth: function(command, ui, value) {
       
 17969 				var counter = 0;
       
 17970 
       
 17971 				dom.getParent(selection.getNode(), function(node) {
       
 17972 					if (node.nodeType == 1 && counter++ == value) {
       
 17973 						selection.select(node);
       
 17974 						return FALSE;
       
 17975 					}
       
 17976 				}, editor.getBody());
       
 17977 			},
       
 17978 
       
 17979 			mceSelectNode: function(command, ui, value) {
       
 17980 				selection.select(value);
       
 17981 			},
       
 17982 
       
 17983 			mceInsertContent: function(command, ui, value) {
       
 17984 				var parser, serializer, parentNode, rootNode, fragment, args;
       
 17985 				var marker, rng, node, node2, bookmarkHtml, merge;
       
 17986 				var textInlineElements = editor.schema.getTextInlineElements();
       
 17987 
       
 17988 				function trimOrPaddLeftRight(html) {
       
 17989 					var rng, container, offset;
       
 17990 
       
 17991 					rng = selection.getRng(true);
       
 17992 					container = rng.startContainer;
       
 17993 					offset = rng.startOffset;
       
 17994 
       
 17995 					function hasSiblingText(siblingName) {
       
 17996 						return container[siblingName] && container[siblingName].nodeType == 3;
       
 17997 					}
       
 17998 
       
 17999 					if (container.nodeType == 3) {
       
 18000 						if (offset > 0) {
       
 18001 							html = html.replace(/^&nbsp;/, ' ');
       
 18002 						} else if (!hasSiblingText('previousSibling')) {
       
 18003 							html = html.replace(/^ /, '&nbsp;');
       
 18004 						}
       
 18005 
       
 18006 						if (offset < container.length) {
       
 18007 							html = html.replace(/&nbsp;(<br>|)$/, ' ');
       
 18008 						} else if (!hasSiblingText('nextSibling')) {
       
 18009 							html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
       
 18010 						}
       
 18011 					}
       
 18012 
       
 18013 					return html;
       
 18014 				}
       
 18015 
       
 18016 				// Removes &nbsp; from a [b] c -> a &nbsp;c -> a c
       
 18017 				function trimNbspAfterDeleteAndPaddValue() {
       
 18018 					var rng, container, offset;
       
 18019 
       
 18020 					rng = selection.getRng(true);
       
 18021 					container = rng.startContainer;
       
 18022 					offset = rng.startOffset;
       
 18023 
       
 18024 					if (container.nodeType == 3 && rng.collapsed) {
       
 18025 						if (container.data[offset] === '\u00a0') {
       
 18026 							container.deleteData(offset, 1);
       
 18027 
       
 18028 							if (!/[\u00a0| ]$/.test(value)) {
       
 18029 								value += ' ';
       
 18030 							}
       
 18031 						} else if (container.data[offset - 1] === '\u00a0') {
       
 18032 							container.deleteData(offset - 1, 1);
       
 18033 
       
 18034 							if (!/[\u00a0| ]$/.test(value)) {
       
 18035 								value = ' ' + value;
       
 18036 							}
       
 18037 						}
       
 18038 					}
       
 18039 				}
       
 18040 
       
 18041 				function markInlineFormatElements(fragment) {
       
 18042 					if (merge) {
       
 18043 						for (node = fragment.firstChild; node; node = node.walk(true)) {
       
 18044 							if (textInlineElements[node.name]) {
       
 18045 								node.attr('data-mce-new', "true");
       
 18046 							}
       
 18047 						}
       
 18048 					}
       
 18049 				}
       
 18050 
       
 18051 				function reduceInlineTextElements() {
       
 18052 					if (merge) {
       
 18053 						var root = editor.getBody(), elementUtils = new ElementUtils(dom);
       
 18054 
       
 18055 						each(dom.select('*[data-mce-new]'), function(node) {
       
 18056 							node.removeAttribute('data-mce-new');
       
 18057 
       
 18058 							for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
       
 18059 								if (elementUtils.compare(testNode, node)) {
       
 18060 									dom.remove(node, true);
       
 18061 								}
       
 18062 							}
       
 18063 						});
       
 18064 					}
       
 18065 				}
       
 18066 
       
 18067 				if (typeof value != 'string') {
       
 18068 					merge = value.merge;
       
 18069 					value = value.content;
       
 18070 				}
       
 18071 
       
 18072 				// Check for whitespace before/after value
       
 18073 				if (/^ | $/.test(value)) {
       
 18074 					value = trimOrPaddLeftRight(value);
       
 18075 				}
       
 18076 
       
 18077 				// Setup parser and serializer
       
 18078 				parser = editor.parser;
       
 18079 				serializer = new Serializer({}, editor.schema);
       
 18080 				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>';
       
 18081 
       
 18082 				// Run beforeSetContent handlers on the HTML to be inserted
       
 18083 				args = {content: value, format: 'html', selection: true};
       
 18084 				editor.fire('BeforeSetContent', args);
       
 18085 				value = args.content;
       
 18086 
       
 18087 				// Add caret at end of contents if it's missing
       
 18088 				if (value.indexOf('{$caret}') == -1) {
       
 18089 					value += '{$caret}';
       
 18090 				}
       
 18091 
       
 18092 				// Replace the caret marker with a span bookmark element
       
 18093 				value = value.replace(/\{\$caret\}/, bookmarkHtml);
       
 18094 
       
 18095 				// If selection is at <body>|<p></p> then move it into <body><p>|</p>
       
 18096 				rng = selection.getRng();
       
 18097 				var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
       
 18098 				var body = editor.getBody();
       
 18099 				if (caretElement === body && selection.isCollapsed()) {
       
 18100 					if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
       
 18101 						rng = dom.createRng();
       
 18102 						rng.setStart(body.firstChild, 0);
       
 18103 						rng.setEnd(body.firstChild, 0);
       
 18104 						selection.setRng(rng);
       
 18105 					}
       
 18106 				}
       
 18107 
       
 18108 				// Insert node maker where we will insert the new HTML and get it's parent
       
 18109 				if (!selection.isCollapsed()) {
       
 18110 					editor.getDoc().execCommand('Delete', false, null);
       
 18111 					trimNbspAfterDeleteAndPaddValue();
       
 18112 				}
       
 18113 
       
 18114 				parentNode = selection.getNode();
       
 18115 
       
 18116 				// Parse the fragment within the context of the parent node
       
 18117 				var parserArgs = {context: parentNode.nodeName.toLowerCase()};
       
 18118 				fragment = parser.parse(value, parserArgs);
       
 18119 
       
 18120 				markInlineFormatElements(fragment);
       
 18121 
       
 18122 				// Move the caret to a more suitable location
       
 18123 				node = fragment.lastChild;
       
 18124 				if (node.attr('id') == 'mce_marker') {
       
 18125 					marker = node;
       
 18126 
       
 18127 					for (node = node.prev; node; node = node.walk(true)) {
       
 18128 						if (node.type == 3 || !dom.isBlock(node.name)) {
       
 18129 							if (editor.schema.isValidChild(node.parent.name, 'span')) {
       
 18130 								node.parent.insert(marker, node, node.name === 'br');
       
 18131 							}
       
 18132 							break;
       
 18133 						}
       
 18134 					}
       
 18135 				}
       
 18136 
       
 18137 				// If parser says valid we can insert the contents into that parent
       
 18138 				if (!parserArgs.invalid) {
       
 18139 					value = serializer.serialize(fragment);
       
 18140 
       
 18141 					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
       
 18142 					node = parentNode.firstChild;
       
 18143 					node2 = parentNode.lastChild;
       
 18144 					if (!node || (node === node2 && node.nodeName === 'BR')) {
       
 18145 						dom.setHTML(parentNode, value);
       
 18146 					} else {
       
 18147 						selection.setContent(value);
       
 18148 					}
       
 18149 				} else {
       
 18150 					// If the fragment was invalid within that context then we need
       
 18151 					// to parse and process the parent it's inserted into
       
 18152 
       
 18153 					// Insert bookmark node and get the parent
       
 18154 					selection.setContent(bookmarkHtml);
       
 18155 					parentNode = selection.getNode();
       
 18156 					rootNode = editor.getBody();
       
 18157 
       
 18158 					// Opera will return the document node when selection is in root
       
 18159 					if (parentNode.nodeType == 9) {
       
 18160 						parentNode = node = rootNode;
       
 18161 					} else {
       
 18162 						node = parentNode;
       
 18163 					}
       
 18164 
       
 18165 					// Find the ancestor just before the root element
       
 18166 					while (node !== rootNode) {
       
 18167 						parentNode = node;
       
 18168 						node = node.parentNode;
       
 18169 					}
       
 18170 
       
 18171 					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
       
 18172 					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
       
 18173 					value = serializer.serialize(
       
 18174 						parser.parse(
       
 18175 							// Need to replace by using a function since $ in the contents would otherwise be a problem
       
 18176 							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
       
 18177 								return serializer.serialize(fragment);
       
 18178 							})
       
 18179 						)
       
 18180 					);
       
 18181 
       
 18182 					// Set the inner/outer HTML depending on if we are in the root or not
       
 18183 					if (parentNode == rootNode) {
       
 18184 						dom.setHTML(rootNode, value);
       
 18185 					} else {
       
 18186 						dom.setOuterHTML(parentNode, value);
       
 18187 					}
       
 18188 				}
       
 18189 
       
 18190 				reduceInlineTextElements();
       
 18191 
       
 18192 				marker = dom.get('mce_marker');
       
 18193 				selection.scrollIntoView(marker);
       
 18194 
       
 18195 				// Move selection before marker and remove it
       
 18196 				rng = dom.createRng();
       
 18197 
       
 18198 				// If previous sibling is a text node set the selection to the end of that node
       
 18199 				node = marker.previousSibling;
       
 18200 				if (node && node.nodeType == 3) {
       
 18201 					rng.setStart(node, node.nodeValue.length);
       
 18202 
       
 18203 					// TODO: Why can't we normalize on IE
       
 18204 					if (!isIE) {
       
 18205 						node2 = marker.nextSibling;
       
 18206 						if (node2 && node2.nodeType == 3) {
       
 18207 							node.appendData(node2.data);
       
 18208 							node2.parentNode.removeChild(node2);
       
 18209 						}
       
 18210 					}
       
 18211 				} else {
       
 18212 					// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
       
 18213 					rng.setStartBefore(marker);
       
 18214 					rng.setEndBefore(marker);
       
 18215 				}
       
 18216 
       
 18217 				// Remove the marker node and set the new range
       
 18218 				dom.remove(marker);
       
 18219 				selection.setRng(rng);
       
 18220 
       
 18221 				// Dispatch after event and add any visual elements needed
       
 18222 				editor.fire('SetContent', args);
       
 18223 				editor.addVisual();
       
 18224 			},
       
 18225 
       
 18226 			mceInsertRawHTML: function(command, ui, value) {
       
 18227 				selection.setContent('tiny_mce_marker');
       
 18228 				editor.setContent(
       
 18229 					editor.getContent().replace(/tiny_mce_marker/g, function() {
       
 18230 						return value;
       
 18231 					})
       
 18232 				);
       
 18233 			},
       
 18234 
       
 18235 			mceToggleFormat: function(command, ui, value) {
       
 18236 				toggleFormat(value);
       
 18237 			},
       
 18238 
       
 18239 			mceSetContent: function(command, ui, value) {
       
 18240 				editor.setContent(value);
       
 18241 			},
       
 18242 
       
 18243 			'Indent,Outdent': function(command) {
       
 18244 				var intentValue, indentUnit, value;
       
 18245 
       
 18246 				// Setup indent level
       
 18247 				intentValue = settings.indentation;
       
 18248 				indentUnit = /[a-z%]+$/i.exec(intentValue);
       
 18249 				intentValue = parseInt(intentValue, 10);
       
 18250 
       
 18251 				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
       
 18252 					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
       
 18253 					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
       
 18254 						formatter.apply('div');
       
 18255 					}
       
 18256 
       
 18257 					each(selection.getSelectedBlocks(), function(element) {
       
 18258 						if (element.nodeName != "LI") {
       
 18259 							var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding';
       
 18260 
       
 18261 							indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left';
       
 18262 
       
 18263 							if (command == 'outdent') {
       
 18264 								value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
       
 18265 								dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
       
 18266 							} else {
       
 18267 								value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
       
 18268 								dom.setStyle(element, indentStyleName, value);
       
 18269 							}
       
 18270 						}
       
 18271 					});
       
 18272 				} else {
       
 18273 					execNativeCommand(command);
       
 18274 				}
       
 18275 			},
       
 18276 
       
 18277 			mceRepaint: function() {
       
 18278 				if (isGecko) {
       
 18279 					try {
       
 18280 						storeSelection(TRUE);
       
 18281 
       
 18282 						if (selection.getSel()) {
       
 18283 							selection.getSel().selectAllChildren(editor.getBody());
       
 18284 						}
       
 18285 
       
 18286 						selection.collapse(TRUE);
       
 18287 						restoreSelection();
       
 18288 					} catch (ex) {
       
 18289 						// Ignore
       
 18290 					}
       
 18291 				}
       
 18292 			},
       
 18293 
       
 18294 			InsertHorizontalRule: function() {
       
 18295 				editor.execCommand('mceInsertContent', false, '<hr />');
       
 18296 			},
       
 18297 
       
 18298 			mceToggleVisualAid: function() {
       
 18299 				editor.hasVisual = !editor.hasVisual;
       
 18300 				editor.addVisual();
       
 18301 			},
       
 18302 
       
 18303 			mceReplaceContent: function(command, ui, value) {
       
 18304 				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
       
 18305 			},
       
 18306 
       
 18307 			mceInsertLink: function(command, ui, value) {
       
 18308 				var anchor;
       
 18309 
       
 18310 				if (typeof value == 'string') {
       
 18311 					value = {href: value};
       
 18312 				}
       
 18313 
       
 18314 				anchor = dom.getParent(selection.getNode(), 'a');
       
 18315 
       
 18316 				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
       
 18317 				value.href = value.href.replace(' ', '%20');
       
 18318 
       
 18319 				// Remove existing links if there could be child links or that the href isn't specified
       
 18320 				if (!anchor || !value.href) {
       
 18321 					formatter.remove('link');
       
 18322 				}
       
 18323 
       
 18324 				// Apply new link to selection
       
 18325 				if (value.href) {
       
 18326 					formatter.apply('link', value, anchor);
       
 18327 				}
       
 18328 			},
       
 18329 
       
 18330 			selectAll: function() {
       
 18331 				var root = dom.getRoot(), rng;
       
 18332 
       
 18333 				if (selection.getRng().setStart) {
       
 18334 					rng = dom.createRng();
       
 18335 					rng.setStart(root, 0);
       
 18336 					rng.setEnd(root, root.childNodes.length);
       
 18337 					selection.setRng(rng);
       
 18338 				} else {
       
 18339 					// IE will render it's own root level block elements and sometimes
       
 18340 					// even put font elements in them when the user starts typing. So we need to
       
 18341 					// move the selection to a more suitable element from this:
       
 18342 					// <body>|<p></p></body> to this: <body><p>|</p></body>
       
 18343 					rng = selection.getRng();
       
 18344 					if (!rng.item) {
       
 18345 						rng.moveToElementText(root);
       
 18346 						rng.select();
       
 18347 					}
       
 18348 				}
       
 18349 			},
       
 18350 
       
 18351 			"delete": function() {
       
 18352 				execNativeCommand("Delete");
       
 18353 
       
 18354 				// Check if body is empty after the delete call if so then set the contents
       
 18355 				// to an empty string and move the caret to any block produced by that operation
       
 18356 				// this fixes the issue with root blocks not being properly produced after a delete call on IE
       
 18357 				var body = editor.getBody();
       
 18358 
       
 18359 				if (dom.isEmpty(body)) {
       
 18360 					editor.setContent('');
       
 18361 
       
 18362 					if (body.firstChild && dom.isBlock(body.firstChild)) {
       
 18363 						editor.selection.setCursorLocation(body.firstChild, 0);
       
 18364 					} else {
       
 18365 						editor.selection.setCursorLocation(body, 0);
       
 18366 					}
       
 18367 				}
       
 18368 			},
       
 18369 
       
 18370 			mceNewDocument: function() {
       
 18371 				editor.setContent('');
       
 18372 			},
       
 18373 
       
 18374 			InsertLineBreak: function(command, ui, value) {
       
 18375 				// We load the current event in from EnterKey.js when appropriate to heed
       
 18376 				// certain event-specific variations such as ctrl-enter in a list
       
 18377 				var evt = value;
       
 18378 				var brElm, extraBr, marker;
       
 18379 				var rng = selection.getRng(true);
       
 18380 				new RangeUtils(dom).normalize(rng);
       
 18381 
       
 18382 				var offset = rng.startOffset;
       
 18383 				var container = rng.startContainer;
       
 18384 
       
 18385 				// Resolve node index
       
 18386 				if (container.nodeType == 1 && container.hasChildNodes()) {
       
 18387 					var isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
       
 18388 
       
 18389 					container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
       
 18390 					if (isAfterLastNodeInContainer && container.nodeType == 3) {
       
 18391 						offset = container.nodeValue.length;
       
 18392 					} else {
       
 18393 						offset = 0;
       
 18394 					}
       
 18395 				}
       
 18396 
       
 18397 				var parentBlock = dom.getParent(container, dom.isBlock);
       
 18398 				var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 18399 				var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
       
 18400 				var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
 18401 
       
 18402 				// Enter inside block contained within a LI then split or insert before/after LI
       
 18403 				var isControlKey = evt && evt.ctrlKey;
       
 18404 				if (containerBlockName == 'LI' && !isControlKey) {
       
 18405 					parentBlock = containerBlock;
       
 18406 					parentBlockName = containerBlockName;
       
 18407 				}
       
 18408 
       
 18409 				// Walks the parent block to the right and look for BR elements
       
 18410 				function hasRightSideContent() {
       
 18411 					var walker = new TreeWalker(container, parentBlock), node;
       
 18412 					var nonEmptyElementsMap = editor.schema.getNonEmptyElements();
       
 18413 
       
 18414 					while ((node = walker.next())) {
       
 18415 						if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
       
 18416 							return true;
       
 18417 						}
       
 18418 					}
       
 18419 				}
       
 18420 
       
 18421 				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
       
 18422 					// Insert extra BR element at the end block elements
       
 18423 					if (!isOldIE && !hasRightSideContent()) {
       
 18424 						brElm = dom.create('br');
       
 18425 						rng.insertNode(brElm);
       
 18426 						rng.setStartAfter(brElm);
       
 18427 						rng.setEndAfter(brElm);
       
 18428 						extraBr = true;
       
 18429 					}
       
 18430 				}
       
 18431 
       
 18432 				brElm = dom.create('br');
       
 18433 				rng.insertNode(brElm);
       
 18434 
       
 18435 				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
       
 18436 				var documentMode = dom.doc.documentMode;
       
 18437 				if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
       
 18438 					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
       
 18439 				}
       
 18440 
       
 18441 				// Insert temp marker and scroll to that
       
 18442 				marker = dom.create('span', {}, '&nbsp;');
       
 18443 				brElm.parentNode.insertBefore(marker, brElm);
       
 18444 				selection.scrollIntoView(marker);
       
 18445 				dom.remove(marker);
       
 18446 
       
 18447 				if (!extraBr) {
       
 18448 					rng.setStartAfter(brElm);
       
 18449 					rng.setEndAfter(brElm);
       
 18450 				} else {
       
 18451 					rng.setStartBefore(brElm);
       
 18452 					rng.setEndBefore(brElm);
       
 18453 				}
       
 18454 
       
 18455 				selection.setRng(rng);
       
 18456 				editor.undoManager.add();
       
 18457 
       
 18458 				return TRUE;
       
 18459 			}
       
 18460 		});
       
 18461 
       
 18462 		// Add queryCommandState overrides
       
 18463 		addCommands({
       
 18464 			// Override justify commands
       
 18465 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
       
 18466 				var name = 'align' + command.substring(7);
       
 18467 				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
       
 18468 				var matches = map(nodes, function(node) {
       
 18469 					return !!formatter.matchNode(node, name);
       
 18470 				});
       
 18471 				return inArray(matches, TRUE) !== -1;
       
 18472 			},
       
 18473 
       
 18474 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
       
 18475 				return isFormatMatch(command);
       
 18476 			},
       
 18477 
       
 18478 			mceBlockQuote: function() {
       
 18479 				return isFormatMatch('blockquote');
       
 18480 			},
       
 18481 
       
 18482 			Outdent: function() {
       
 18483 				var node;
       
 18484 
       
 18485 				if (settings.inline_styles) {
       
 18486 					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
       
 18487 						return TRUE;
       
 18488 					}
       
 18489 
       
 18490 					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
       
 18491 						return TRUE;
       
 18492 					}
       
 18493 				}
       
 18494 
       
 18495 				return (
       
 18496 					queryCommandState('InsertUnorderedList') ||
       
 18497 					queryCommandState('InsertOrderedList') ||
       
 18498 					(!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
       
 18499 				);
       
 18500 			},
       
 18501 
       
 18502 			'InsertUnorderedList,InsertOrderedList': function(command) {
       
 18503 				var list = dom.getParent(selection.getNode(), 'ul,ol');
       
 18504 
       
 18505 				return list &&
       
 18506 					(
       
 18507 						command === 'insertunorderedlist' && list.tagName === 'UL' ||
       
 18508 						command === 'insertorderedlist' && list.tagName === 'OL'
       
 18509 					);
       
 18510 			}
       
 18511 		}, 'state');
       
 18512 
       
 18513 		// Add queryCommandValue overrides
       
 18514 		addCommands({
       
 18515 			'FontSize,FontName': function(command) {
       
 18516 				var value = 0, parent;
       
 18517 
       
 18518 				if ((parent = dom.getParent(selection.getNode(), 'span'))) {
       
 18519 					if (command == 'fontsize') {
       
 18520 						value = parent.style.fontSize;
       
 18521 					} else {
       
 18522 						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
       
 18523 					}
       
 18524 				}
       
 18525 
       
 18526 				return value;
       
 18527 			}
       
 18528 		}, 'value');
       
 18529 
       
 18530 		// Add undo manager logic
       
 18531 		addCommands({
       
 18532 			Undo: function() {
       
 18533 				editor.undoManager.undo();
       
 18534 			},
       
 18535 
       
 18536 			Redo: function() {
       
 18537 				editor.undoManager.redo();
       
 18538 			}
       
 18539 		});
       
 18540 	};
       
 18541 });
       
 18542 
       
 18543 // Included from: js/tinymce/classes/util/URI.js
       
 18544 
       
 18545 /**
       
 18546  * URI.js
       
 18547  *
       
 18548  * Copyright, Moxiecode Systems AB
       
 18549  * Released under LGPL License.
       
 18550  *
       
 18551  * License: http://www.tinymce.com/license
       
 18552  * Contributing: http://www.tinymce.com/contributing
       
 18553  */
       
 18554 
       
 18555 /**
       
 18556  * This class handles parsing, modification and serialization of URI/URL strings.
       
 18557  * @class tinymce.util.URI
       
 18558  */
       
 18559 define("tinymce/util/URI", [
       
 18560 	"tinymce/util/Tools"
       
 18561 ], function(Tools) {
       
 18562 	var each = Tools.each, trim = Tools.trim;
       
 18563 	var queryParts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(' ');
       
 18564 	var DEFAULT_PORTS = {
       
 18565 		'ftp': 21,
       
 18566 		'http': 80,
       
 18567 		'https': 443,
       
 18568 		'mailto': 25
       
 18569 	};
       
 18570 
       
 18571 	/**
       
 18572 	 * Constructs a new URI instance.
       
 18573 	 *
       
 18574 	 * @constructor
       
 18575 	 * @method URI
       
 18576 	 * @param {String} url URI string to parse.
       
 18577 	 * @param {Object} settings Optional settings object.
       
 18578 	 */
       
 18579 	function URI(url, settings) {
       
 18580 		var self = this, baseUri, base_url;
       
 18581 
       
 18582 		url = trim(url);
       
 18583 		settings = self.settings = settings || {};
       
 18584 		baseUri = settings.base_uri;
       
 18585 
       
 18586 		// Strange app protocol that isn't http/https or local anchor
       
 18587 		// For example: mailto,skype,tel etc.
       
 18588 		if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
       
 18589 			self.source = url;
       
 18590 			return;
       
 18591 		}
       
 18592 
       
 18593 		var isProtocolRelative = url.indexOf('//') === 0;
       
 18594 
       
 18595 		// Absolute path with no host, fake host and protocol
       
 18596 		if (url.indexOf('/') === 0 && !isProtocolRelative) {
       
 18597 			url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url;
       
 18598 		}
       
 18599 
       
 18600 		// Relative path http:// or protocol relative //path
       
 18601 		if (!/^[\w\-]*:?\/\//.test(url)) {
       
 18602 			base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
       
 18603 			if (settings.base_uri.protocol === "") {
       
 18604 				url = '//mce_host' + self.toAbsPath(base_url, url);
       
 18605 			} else {
       
 18606 				url = /([^#?]*)([#?]?.*)/.exec(url);
       
 18607 				url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url[1]) + url[2];
       
 18608 			}
       
 18609 		}
       
 18610 
       
 18611 		// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
       
 18612 		url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
       
 18613 
       
 18614 		/*jshint maxlen: 255 */
       
 18615 		/*eslint max-len: 0 */
       
 18616 		url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
       
 18617 
       
 18618 		each(queryParts, function(v, i) {
       
 18619 			var part = url[i];
       
 18620 
       
 18621 			// Zope 3 workaround, they use @@something
       
 18622 			if (part) {
       
 18623 				part = part.replace(/\(mce_at\)/g, '@@');
       
 18624 			}
       
 18625 
       
 18626 			self[v] = part;
       
 18627 		});
       
 18628 
       
 18629 		if (baseUri) {
       
 18630 			if (!self.protocol) {
       
 18631 				self.protocol = baseUri.protocol;
       
 18632 			}
       
 18633 
       
 18634 			if (!self.userInfo) {
       
 18635 				self.userInfo = baseUri.userInfo;
       
 18636 			}
       
 18637 
       
 18638 			if (!self.port && self.host === 'mce_host') {
       
 18639 				self.port = baseUri.port;
       
 18640 			}
       
 18641 
       
 18642 			if (!self.host || self.host === 'mce_host') {
       
 18643 				self.host = baseUri.host;
       
 18644 			}
       
 18645 
       
 18646 			self.source = '';
       
 18647 		}
       
 18648 
       
 18649 		if (isProtocolRelative) {
       
 18650 			self.protocol = '';
       
 18651 		}
       
 18652 
       
 18653 		//t.path = t.path || '/';
       
 18654 	}
       
 18655 
       
 18656 	URI.prototype = {
       
 18657 		/**
       
 18658 		 * Sets the internal path part of the URI.
       
 18659 		 *
       
 18660 		 * @method setPath
       
 18661 		 * @param {string} path Path string to set.
       
 18662 		 */
       
 18663 		setPath: function(path) {
       
 18664 			var self = this;
       
 18665 
       
 18666 			path = /^(.*?)\/?(\w+)?$/.exec(path);
       
 18667 
       
 18668 			// Update path parts
       
 18669 			self.path = path[0];
       
 18670 			self.directory = path[1];
       
 18671 			self.file = path[2];
       
 18672 
       
 18673 			// Rebuild source
       
 18674 			self.source = '';
       
 18675 			self.getURI();
       
 18676 		},
       
 18677 
       
 18678 		/**
       
 18679 		 * Converts the specified URI into a relative URI based on the current URI instance location.
       
 18680 		 *
       
 18681 		 * @method toRelative
       
 18682 		 * @param {String} uri URI to convert into a relative path/URI.
       
 18683 		 * @return {String} Relative URI from the point specified in the current URI instance.
       
 18684 		 * @example
       
 18685 		 * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
       
 18686 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
       
 18687 		 */
       
 18688 		toRelative: function(uri) {
       
 18689 			var self = this, output;
       
 18690 
       
 18691 			if (uri === "./") {
       
 18692 				return uri;
       
 18693 			}
       
 18694 
       
 18695 			uri = new URI(uri, {base_uri: self});
       
 18696 
       
 18697 			// Not on same domain/port or protocol
       
 18698 			if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
       
 18699 				(self.protocol != uri.protocol && uri.protocol !== "")) {
       
 18700 				return uri.getURI();
       
 18701 			}
       
 18702 
       
 18703 			var tu = self.getURI(), uu = uri.getURI();
       
 18704 
       
 18705 			// Allow usage of the base_uri when relative_urls = true
       
 18706 			if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
       
 18707 				return tu;
       
 18708 			}
       
 18709 
       
 18710 			output = self.toRelPath(self.path, uri.path);
       
 18711 
       
 18712 			// Add query
       
 18713 			if (uri.query) {
       
 18714 				output += '?' + uri.query;
       
 18715 			}
       
 18716 
       
 18717 			// Add anchor
       
 18718 			if (uri.anchor) {
       
 18719 				output += '#' + uri.anchor;
       
 18720 			}
       
 18721 
       
 18722 			return output;
       
 18723 		},
       
 18724 
       
 18725 		/**
       
 18726 		 * Converts the specified URI into a absolute URI based on the current URI instance location.
       
 18727 		 *
       
 18728 		 * @method toAbsolute
       
 18729 		 * @param {String} uri URI to convert into a relative path/URI.
       
 18730 		 * @param {Boolean} noHost No host and protocol prefix.
       
 18731 		 * @return {String} Absolute URI from the point specified in the current URI instance.
       
 18732 		 * @example
       
 18733 		 * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
       
 18734 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
       
 18735 		 */
       
 18736 		toAbsolute: function(uri, noHost) {
       
 18737 			uri = new URI(uri, {base_uri: this});
       
 18738 
       
 18739 			return uri.getURI(noHost && this.isSameOrigin(uri));
       
 18740 		},
       
 18741 
       
 18742 		/**
       
 18743 		 * Determine whether the given URI has the same origin as this URI.  Based on RFC-6454.
       
 18744 		 * Supports default ports for protocols listed in DEFAULT_PORTS.  Unsupported protocols will fail safe: they
       
 18745 		 * won't match, if the port specifications differ.
       
 18746 		 *
       
 18747 		 * @method isSameOrigin
       
 18748 		 * @param {tinymce.util.URI} uri Uri instance to compare.
       
 18749 		 * @returns {Boolean} True if the origins are the same.
       
 18750 		 */
       
 18751 		isSameOrigin: function(uri) {
       
 18752 			if (this.host == uri.host && this.protocol == uri.protocol) {
       
 18753 				if (this.port == uri.port) {
       
 18754 					return true;
       
 18755 				}
       
 18756 
       
 18757 				var defaultPort = DEFAULT_PORTS[this.protocol];
       
 18758 				if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) {
       
 18759 					return true;
       
 18760 				}
       
 18761 			}
       
 18762 
       
 18763 			return false;
       
 18764 		},
       
 18765 
       
 18766 		/**
       
 18767 		 * Converts a absolute path into a relative path.
       
 18768 		 *
       
 18769 		 * @method toRelPath
       
 18770 		 * @param {String} base Base point to convert the path from.
       
 18771 		 * @param {String} path Absolute path to convert into a relative path.
       
 18772 		 */
       
 18773 		toRelPath: function(base, path) {
       
 18774 			var items, breakPoint = 0, out = '', i, l;
       
 18775 
       
 18776 			// Split the paths
       
 18777 			base = base.substring(0, base.lastIndexOf('/'));
       
 18778 			base = base.split('/');
       
 18779 			items = path.split('/');
       
 18780 
       
 18781 			if (base.length >= items.length) {
       
 18782 				for (i = 0, l = base.length; i < l; i++) {
       
 18783 					if (i >= items.length || base[i] != items[i]) {
       
 18784 						breakPoint = i + 1;
       
 18785 						break;
       
 18786 					}
       
 18787 				}
       
 18788 			}
       
 18789 
       
 18790 			if (base.length < items.length) {
       
 18791 				for (i = 0, l = items.length; i < l; i++) {
       
 18792 					if (i >= base.length || base[i] != items[i]) {
       
 18793 						breakPoint = i + 1;
       
 18794 						break;
       
 18795 					}
       
 18796 				}
       
 18797 			}
       
 18798 
       
 18799 			if (breakPoint === 1) {
       
 18800 				return path;
       
 18801 			}
       
 18802 
       
 18803 			for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) {
       
 18804 				out += "../";
       
 18805 			}
       
 18806 
       
 18807 			for (i = breakPoint - 1, l = items.length; i < l; i++) {
       
 18808 				if (i != breakPoint - 1) {
       
 18809 					out += "/" + items[i];
       
 18810 				} else {
       
 18811 					out += items[i];
       
 18812 				}
       
 18813 			}
       
 18814 
       
 18815 			return out;
       
 18816 		},
       
 18817 
       
 18818 		/**
       
 18819 		 * Converts a relative path into a absolute path.
       
 18820 		 *
       
 18821 		 * @method toAbsPath
       
 18822 		 * @param {String} base Base point to convert the path from.
       
 18823 		 * @param {String} path Relative path to convert into an absolute path.
       
 18824 		 */
       
 18825 		toAbsPath: function(base, path) {
       
 18826 			var i, nb = 0, o = [], tr, outPath;
       
 18827 
       
 18828 			// Split paths
       
 18829 			tr = /\/$/.test(path) ? '/' : '';
       
 18830 			base = base.split('/');
       
 18831 			path = path.split('/');
       
 18832 
       
 18833 			// Remove empty chunks
       
 18834 			each(base, function(k) {
       
 18835 				if (k) {
       
 18836 					o.push(k);
       
 18837 				}
       
 18838 			});
       
 18839 
       
 18840 			base = o;
       
 18841 
       
 18842 			// Merge relURLParts chunks
       
 18843 			for (i = path.length - 1, o = []; i >= 0; i--) {
       
 18844 				// Ignore empty or .
       
 18845 				if (path[i].length === 0 || path[i] === ".") {
       
 18846 					continue;
       
 18847 				}
       
 18848 
       
 18849 				// Is parent
       
 18850 				if (path[i] === '..') {
       
 18851 					nb++;
       
 18852 					continue;
       
 18853 				}
       
 18854 
       
 18855 				// Move up
       
 18856 				if (nb > 0) {
       
 18857 					nb--;
       
 18858 					continue;
       
 18859 				}
       
 18860 
       
 18861 				o.push(path[i]);
       
 18862 			}
       
 18863 
       
 18864 			i = base.length - nb;
       
 18865 
       
 18866 			// If /a/b/c or /
       
 18867 			if (i <= 0) {
       
 18868 				outPath = o.reverse().join('/');
       
 18869 			} else {
       
 18870 				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
       
 18871 			}
       
 18872 
       
 18873 			// Add front / if it's needed
       
 18874 			if (outPath.indexOf('/') !== 0) {
       
 18875 				outPath = '/' + outPath;
       
 18876 			}
       
 18877 
       
 18878 			// Add traling / if it's needed
       
 18879 			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {
       
 18880 				outPath += tr;
       
 18881 			}
       
 18882 
       
 18883 			return outPath;
       
 18884 		},
       
 18885 
       
 18886 		/**
       
 18887 		 * Returns the full URI of the internal structure.
       
 18888 		 *
       
 18889 		 * @method getURI
       
 18890 		 * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
       
 18891 		 */
       
 18892 		getURI: function(noProtoHost) {
       
 18893 			var s, self = this;
       
 18894 
       
 18895 			// Rebuild source
       
 18896 			if (!self.source || noProtoHost) {
       
 18897 				s = '';
       
 18898 
       
 18899 				if (!noProtoHost) {
       
 18900 					if (self.protocol) {
       
 18901 						s += self.protocol + '://';
       
 18902 					} else {
       
 18903 						s += '//';
       
 18904 					}
       
 18905 
       
 18906 					if (self.userInfo) {
       
 18907 						s += self.userInfo + '@';
       
 18908 					}
       
 18909 
       
 18910 					if (self.host) {
       
 18911 						s += self.host;
       
 18912 					}
       
 18913 
       
 18914 					if (self.port) {
       
 18915 						s += ':' + self.port;
       
 18916 					}
       
 18917 				}
       
 18918 
       
 18919 				if (self.path) {
       
 18920 					s += self.path;
       
 18921 				}
       
 18922 
       
 18923 				if (self.query) {
       
 18924 					s += '?' + self.query;
       
 18925 				}
       
 18926 
       
 18927 				if (self.anchor) {
       
 18928 					s += '#' + self.anchor;
       
 18929 				}
       
 18930 
       
 18931 				self.source = s;
       
 18932 			}
       
 18933 
       
 18934 			return self.source;
       
 18935 		}
       
 18936 	};
       
 18937 
       
 18938 	return URI;
       
 18939 });
       
 18940 
       
 18941 // Included from: js/tinymce/classes/util/Class.js
       
 18942 
       
 18943 /**
       
 18944  * Class.js
       
 18945  *
       
 18946  * Copyright 2003-2012, Moxiecode Systems AB, All rights reserved.
       
 18947  */
       
 18948 
       
 18949 /**
       
 18950  * This utilitiy class is used for easier inheritage.
       
 18951  *
       
 18952  * Features:
       
 18953  * * Exposed super functions: this._super();
       
 18954  * * Mixins
       
 18955  * * Dummy functions
       
 18956  * * Property functions: var value = object.value(); and object.value(newValue);
       
 18957  * * Static functions
       
 18958  * * Defaults settings
       
 18959  */
       
 18960 define("tinymce/util/Class", [
       
 18961 	"tinymce/util/Tools"
       
 18962 ], function(Tools) {
       
 18963 	var each = Tools.each, extend = Tools.extend;
       
 18964 
       
 18965 	var extendClass, initializing;
       
 18966 
       
 18967 	function Class() {
       
 18968 	}
       
 18969 
       
 18970 	// Provides classical inheritance, based on code made by John Resig
       
 18971 	Class.extend = extendClass = function(prop) {
       
 18972 		var self = this, _super = self.prototype, prototype, name, member;
       
 18973 
       
 18974 		// The dummy class constructor
       
 18975 		function Class() {
       
 18976 			var i, mixins, mixin, self = this;
       
 18977 
       
 18978 			// All construction is actually done in the init method
       
 18979 			if (!initializing) {
       
 18980 				// Run class constuctor
       
 18981 				if (self.init) {
       
 18982 					self.init.apply(self, arguments);
       
 18983 				}
       
 18984 
       
 18985 				// Run mixin constructors
       
 18986 				mixins = self.Mixins;
       
 18987 				if (mixins) {
       
 18988 					i = mixins.length;
       
 18989 					while (i--) {
       
 18990 						mixin = mixins[i];
       
 18991 						if (mixin.init) {
       
 18992 							mixin.init.apply(self, arguments);
       
 18993 						}
       
 18994 					}
       
 18995 				}
       
 18996 			}
       
 18997 		}
       
 18998 
       
 18999 		// Dummy function, needs to be extended in order to provide functionality
       
 19000 		function dummy() {
       
 19001 			return this;
       
 19002 		}
       
 19003 
       
 19004 		// Creates a overloaded method for the class
       
 19005 		// this enables you to use this._super(); to call the super function
       
 19006 		function createMethod(name, fn) {
       
 19007 			return function() {
       
 19008 				var self = this, tmp = self._super, ret;
       
 19009 
       
 19010 				self._super = _super[name];
       
 19011 				ret = fn.apply(self, arguments);
       
 19012 				self._super = tmp;
       
 19013 
       
 19014 				return ret;
       
 19015 			};
       
 19016 		}
       
 19017 
       
 19018 		// Instantiate a base class (but only create the instance,
       
 19019 		// don't run the init constructor)
       
 19020 		initializing = true;
       
 19021 
       
 19022 		/*eslint new-cap:0 */
       
 19023 		prototype = new self();
       
 19024 		initializing = false;
       
 19025 
       
 19026 		// Add mixins
       
 19027 		if (prop.Mixins) {
       
 19028 			each(prop.Mixins, function(mixin) {
       
 19029 				mixin = mixin;
       
 19030 
       
 19031 				for (var name in mixin) {
       
 19032 					if (name !== "init") {
       
 19033 						prop[name] = mixin[name];
       
 19034 					}
       
 19035 				}
       
 19036 			});
       
 19037 
       
 19038 			if (_super.Mixins) {
       
 19039 				prop.Mixins = _super.Mixins.concat(prop.Mixins);
       
 19040 			}
       
 19041 		}
       
 19042 
       
 19043 		// Generate dummy methods
       
 19044 		if (prop.Methods) {
       
 19045 			each(prop.Methods.split(','), function(name) {
       
 19046 				prop[name] = dummy;
       
 19047 			});
       
 19048 		}
       
 19049 
       
 19050 		// Generate property methods
       
 19051 		if (prop.Properties) {
       
 19052 			each(prop.Properties.split(','), function(name) {
       
 19053 				var fieldName = '_' + name;
       
 19054 
       
 19055 				prop[name] = function(value) {
       
 19056 					var self = this, undef;
       
 19057 
       
 19058 					// Set value
       
 19059 					if (value !== undef) {
       
 19060 						self[fieldName] = value;
       
 19061 
       
 19062 						return self;
       
 19063 					}
       
 19064 
       
 19065 					// Get value
       
 19066 					return self[fieldName];
       
 19067 				};
       
 19068 			});
       
 19069 		}
       
 19070 
       
 19071 		// Static functions
       
 19072 		if (prop.Statics) {
       
 19073 			each(prop.Statics, function(func, name) {
       
 19074 				Class[name] = func;
       
 19075 			});
       
 19076 		}
       
 19077 
       
 19078 		// Default settings
       
 19079 		if (prop.Defaults && _super.Defaults) {
       
 19080 			prop.Defaults = extend({}, _super.Defaults, prop.Defaults);
       
 19081 		}
       
 19082 
       
 19083 		// Copy the properties over onto the new prototype
       
 19084 		for (name in prop) {
       
 19085 			member = prop[name];
       
 19086 
       
 19087 			if (typeof member == "function" && _super[name]) {
       
 19088 				prototype[name] = createMethod(name, member);
       
 19089 			} else {
       
 19090 				prototype[name] = member;
       
 19091 			}
       
 19092 		}
       
 19093 
       
 19094 		// Populate our constructed prototype object
       
 19095 		Class.prototype = prototype;
       
 19096 
       
 19097 		// Enforce the constructor to be what we expect
       
 19098 		Class.constructor = Class;
       
 19099 
       
 19100 		// And make this class extendible
       
 19101 		Class.extend = extendClass;
       
 19102 
       
 19103 		return Class;
       
 19104 	};
       
 19105 
       
 19106 	return Class;
       
 19107 });
       
 19108 
       
 19109 // Included from: js/tinymce/classes/util/EventDispatcher.js
       
 19110 
       
 19111 /**
       
 19112  * EventDispatcher.js
       
 19113  *
       
 19114  * Copyright, Moxiecode Systems AB
       
 19115  * Released under LGPL License.
       
 19116  *
       
 19117  * License: http://www.tinymce.com/license
       
 19118  * Contributing: http://www.tinymce.com/contributing
       
 19119  */
       
 19120 
       
 19121 /**
       
 19122  * This class lets you add/remove and fire events by name on the specified scope. This makes
       
 19123  * it easy to add event listener logic to any class.
       
 19124  *
       
 19125  * @class tinymce.util.EventDispatcher
       
 19126  * @example
       
 19127  *  var eventDispatcher = new EventDispatcher();
       
 19128  *
       
 19129  *  eventDispatcher.on('click', function() {console.log('data');});
       
 19130  *  eventDispatcher.fire('click', {data: 123});
       
 19131  */
       
 19132 define("tinymce/util/EventDispatcher", [
       
 19133 	"tinymce/util/Tools"
       
 19134 ], function(Tools) {
       
 19135 	var nativeEvents = Tools.makeMap(
       
 19136 		"focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
       
 19137 		"mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
       
 19138 		"draggesture dragdrop drop drag submit " +
       
 19139 		"compositionstart compositionend compositionupdate touchstart touchend",
       
 19140 		' '
       
 19141 	);
       
 19142 
       
 19143 	function Dispatcher(settings) {
       
 19144 		var self = this, scope, bindings = {}, toggleEvent;
       
 19145 
       
 19146 		function returnFalse() {
       
 19147 			return false;
       
 19148 		}
       
 19149 
       
 19150 		function returnTrue() {
       
 19151 			return true;
       
 19152 		}
       
 19153 
       
 19154 		settings = settings || {};
       
 19155 		scope = settings.scope || self;
       
 19156 		toggleEvent = settings.toggleEvent || returnFalse;
       
 19157 
       
 19158 		/**
       
 19159 		 * Fires the specified event by name.
       
 19160 		 *
       
 19161 		 * @method fire
       
 19162 		 * @param {String} name Name of the event to fire.
       
 19163 		 * @param {Object?} args Event arguments.
       
 19164 		 * @return {Object} Event args instance passed in.
       
 19165 		 * @example
       
 19166 		 * instance.fire('event', {...});
       
 19167 		 */
       
 19168 		function fire(name, args) {
       
 19169 			var handlers, i, l, callback;
       
 19170 
       
 19171 			name = name.toLowerCase();
       
 19172 			args = args || {};
       
 19173 			args.type = name;
       
 19174 
       
 19175 			// Setup target is there isn't one
       
 19176 			if (!args.target) {
       
 19177 				args.target = scope;
       
 19178 			}
       
 19179 
       
 19180 			// Add event delegation methods if they are missing
       
 19181 			if (!args.preventDefault) {
       
 19182 				// Add preventDefault method
       
 19183 				args.preventDefault = function() {
       
 19184 					args.isDefaultPrevented = returnTrue;
       
 19185 				};
       
 19186 
       
 19187 				// Add stopPropagation
       
 19188 				args.stopPropagation = function() {
       
 19189 					args.isPropagationStopped = returnTrue;
       
 19190 				};
       
 19191 
       
 19192 				// Add stopImmediatePropagation
       
 19193 				args.stopImmediatePropagation = function() {
       
 19194 					args.isImmediatePropagationStopped = returnTrue;
       
 19195 				};
       
 19196 
       
 19197 				// Add event delegation states
       
 19198 				args.isDefaultPrevented = returnFalse;
       
 19199 				args.isPropagationStopped = returnFalse;
       
 19200 				args.isImmediatePropagationStopped = returnFalse;
       
 19201 			}
       
 19202 
       
 19203 			if (settings.beforeFire) {
       
 19204 				settings.beforeFire(args);
       
 19205 			}
       
 19206 
       
 19207 			handlers = bindings[name];
       
 19208 			if (handlers) {
       
 19209 				for (i = 0, l = handlers.length; i < l; i++) {
       
 19210 					callback = handlers[i];
       
 19211 
       
 19212 					// Unbind handlers marked with "once"
       
 19213 					if (callback.once) {
       
 19214 						off(name, callback.func);
       
 19215 					}
       
 19216 
       
 19217 					// Stop immediate propagation if needed
       
 19218 					if (args.isImmediatePropagationStopped()) {
       
 19219 						args.stopPropagation();
       
 19220 						return args;
       
 19221 					}
       
 19222 
       
 19223 					// If callback returns false then prevent default and stop all propagation
       
 19224 					if (callback.func.call(scope, args) === false) {
       
 19225 						args.preventDefault();
       
 19226 						return args;
       
 19227 					}
       
 19228 				}
       
 19229 			}
       
 19230 
       
 19231 			return args;
       
 19232 		}
       
 19233 
       
 19234 		/**
       
 19235 		 * Binds an event listener to a specific event by name.
       
 19236 		 *
       
 19237 		 * @method on
       
 19238 		 * @param {String} name Event name or space separated list of events to bind.
       
 19239 		 * @param {callback} callback Callback to be executed when the event occurs.
       
 19240 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
       
 19241 		 * @return {Object} Current class instance.
       
 19242 		 * @example
       
 19243 		 * instance.on('event', function(e) {
       
 19244 		 *     // Callback logic
       
 19245 		 * });
       
 19246 		 */
       
 19247 		function on(name, callback, prepend, extra) {
       
 19248 			var handlers, names, i;
       
 19249 
       
 19250 			if (callback === false) {
       
 19251 				callback = returnFalse;
       
 19252 			}
       
 19253 
       
 19254 			if (callback) {
       
 19255 				callback = {
       
 19256 					func: callback
       
 19257 				};
       
 19258 
       
 19259 				if (extra) {
       
 19260 					Tools.extend(callback, extra);
       
 19261 				}
       
 19262 
       
 19263 				names = name.toLowerCase().split(' ');
       
 19264 				i = names.length;
       
 19265 				while (i--) {
       
 19266 					name = names[i];
       
 19267 					handlers = bindings[name];
       
 19268 					if (!handlers) {
       
 19269 						handlers = bindings[name] = [];
       
 19270 						toggleEvent(name, true);
       
 19271 					}
       
 19272 
       
 19273 					if (prepend) {
       
 19274 						handlers.unshift(callback);
       
 19275 					} else {
       
 19276 						handlers.push(callback);
       
 19277 					}
       
 19278 				}
       
 19279 			}
       
 19280 
       
 19281 			return self;
       
 19282 		}
       
 19283 
       
 19284 		/**
       
 19285 		 * Unbinds an event listener to a specific event by name.
       
 19286 		 *
       
 19287 		 * @method off
       
 19288 		 * @param {String?} name Name of the event to unbind.
       
 19289 		 * @param {callback?} callback Callback to unbind.
       
 19290 		 * @return {Object} Current class instance.
       
 19291 		 * @example
       
 19292 		 * // Unbind specific callback
       
 19293 		 * instance.off('event', handler);
       
 19294 		 *
       
 19295 		 * // Unbind all listeners by name
       
 19296 		 * instance.off('event');
       
 19297 		 *
       
 19298 		 * // Unbind all events
       
 19299 		 * instance.off();
       
 19300 		 */
       
 19301 		function off(name, callback) {
       
 19302 			var i, handlers, bindingName, names, hi;
       
 19303 
       
 19304 			if (name) {
       
 19305 				names = name.toLowerCase().split(' ');
       
 19306 				i = names.length;
       
 19307 				while (i--) {
       
 19308 					name = names[i];
       
 19309 					handlers = bindings[name];
       
 19310 
       
 19311 					// Unbind all handlers
       
 19312 					if (!name) {
       
 19313 						for (bindingName in bindings) {
       
 19314 							toggleEvent(bindingName, false);
       
 19315 							delete bindings[bindingName];
       
 19316 						}
       
 19317 
       
 19318 						return self;
       
 19319 					}
       
 19320 
       
 19321 					if (handlers) {
       
 19322 						// Unbind all by name
       
 19323 						if (!callback) {
       
 19324 							handlers.length = 0;
       
 19325 						} else {
       
 19326 							// Unbind specific ones
       
 19327 							hi = handlers.length;
       
 19328 							while (hi--) {
       
 19329 								if (handlers[hi].func === callback) {
       
 19330 									handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1));
       
 19331 									bindings[name] = handlers;
       
 19332 								}
       
 19333 							}
       
 19334 						}
       
 19335 
       
 19336 						if (!handlers.length) {
       
 19337 							toggleEvent(name, false);
       
 19338 							delete bindings[name];
       
 19339 						}
       
 19340 					}
       
 19341 				}
       
 19342 			} else {
       
 19343 				for (name in bindings) {
       
 19344 					toggleEvent(name, false);
       
 19345 				}
       
 19346 
       
 19347 				bindings = {};
       
 19348 			}
       
 19349 
       
 19350 			return self;
       
 19351 		}
       
 19352 
       
 19353 		/**
       
 19354 		 * Binds an event listener to a specific event by name
       
 19355 		 * and automatically unbind the event once the callback fires.
       
 19356 		 *
       
 19357 		 * @method once
       
 19358 		 * @param {String} name Event name or space separated list of events to bind.
       
 19359 		 * @param {callback} callback Callback to be executed when the event occurs.
       
 19360 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
       
 19361 		 * @return {Object} Current class instance.
       
 19362 		 * @example
       
 19363 		 * instance.once('event', function(e) {
       
 19364 		 *     // Callback logic
       
 19365 		 * });
       
 19366 		 */
       
 19367 		function once(name, callback, prepend) {
       
 19368 			return on(name, callback, prepend, {once: true});
       
 19369 		}
       
 19370 
       
 19371 		/**
       
 19372 		 * Returns true/false if the dispatcher has a event of the specified name.
       
 19373 		 *
       
 19374 		 * @method has
       
 19375 		 * @param {String} name Name of the event to check for.
       
 19376 		 * @return {Boolean} true/false if the event exists or not.
       
 19377 		 */
       
 19378 		function has(name) {
       
 19379 			name = name.toLowerCase();
       
 19380 			return !(!bindings[name] || bindings[name].length === 0);
       
 19381 		}
       
 19382 
       
 19383 		// Expose
       
 19384 		self.fire = fire;
       
 19385 		self.on = on;
       
 19386 		self.off = off;
       
 19387 		self.once = once;
       
 19388 		self.has = has;
       
 19389 	}
       
 19390 
       
 19391 	/**
       
 19392 	 * Returns true/false if the specified event name is a native browser event or not.
       
 19393 	 *
       
 19394 	 * @method isNative
       
 19395 	 * @param {String} name Name to check if it's native.
       
 19396 	 * @return {Boolean} true/false if the event is native or not.
       
 19397 	 * @static
       
 19398 	 */
       
 19399 	Dispatcher.isNative = function(name) {
       
 19400 		return !!nativeEvents[name.toLowerCase()];
       
 19401 	};
       
 19402 
       
 19403 	return Dispatcher;
       
 19404 });
       
 19405 
       
 19406 // Included from: js/tinymce/classes/ui/Selector.js
       
 19407 
       
 19408 /**
       
 19409  * Selector.js
       
 19410  *
       
 19411  * Copyright, Moxiecode Systems AB
       
 19412  * Released under LGPL License.
       
 19413  *
       
 19414  * License: http://www.tinymce.com/license
       
 19415  * Contributing: http://www.tinymce.com/contributing
       
 19416  */
       
 19417 
       
 19418 /*eslint no-nested-ternary:0 */
       
 19419 
       
 19420 /**
       
 19421  * Selector engine, enables you to select controls by using CSS like expressions.
       
 19422  * We currently only support basic CSS expressions to reduce the size of the core
       
 19423  * and the ones we support should be enough for most cases.
       
 19424  *
       
 19425  * @example
       
 19426  * Supported expressions:
       
 19427  *  element
       
 19428  *  element#name
       
 19429  *  element.class
       
 19430  *  element[attr]
       
 19431  *  element[attr*=value]
       
 19432  *  element[attr~=value]
       
 19433  *  element[attr!=value]
       
 19434  *  element[attr^=value]
       
 19435  *  element[attr$=value]
       
 19436  *  element:<state>
       
 19437  *  element:not(<expression>)
       
 19438  *  element:first
       
 19439  *  element:last
       
 19440  *  element:odd
       
 19441  *  element:even
       
 19442  *  element element
       
 19443  *  element > element
       
 19444  *
       
 19445  * @class tinymce.ui.Selector
       
 19446  */
       
 19447 define("tinymce/ui/Selector", [
       
 19448 	"tinymce/util/Class"
       
 19449 ], function(Class) {
       
 19450 	"use strict";
       
 19451 
       
 19452 	/**
       
 19453 	 * Produces an array with a unique set of objects. It will not compare the values
       
 19454 	 * but the references of the objects.
       
 19455 	 *
       
 19456 	 * @private
       
 19457 	 * @method unqiue
       
 19458 	 * @param {Array} array Array to make into an array with unique items.
       
 19459 	 * @return {Array} Array with unique items.
       
 19460 	 */
       
 19461 	function unique(array) {
       
 19462 		var uniqueItems = [], i = array.length, item;
       
 19463 
       
 19464 		while (i--) {
       
 19465 			item = array[i];
       
 19466 
       
 19467 			if (!item.__checked) {
       
 19468 				uniqueItems.push(item);
       
 19469 				item.__checked = 1;
       
 19470 			}
       
 19471 		}
       
 19472 
       
 19473 		i = uniqueItems.length;
       
 19474 		while (i--) {
       
 19475 			delete uniqueItems[i].__checked;
       
 19476 		}
       
 19477 
       
 19478 		return uniqueItems;
       
 19479 	}
       
 19480 
       
 19481 	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
       
 19482 
       
 19483 	/*jshint maxlen:255 */
       
 19484 	/*eslint max-len:0 */
       
 19485 	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
       
 19486 		whiteSpace = /^\s*|\s*$/g,
       
 19487 		Collection;
       
 19488 
       
 19489 	var Selector = Class.extend({
       
 19490 		/**
       
 19491 		 * Constructs a new Selector instance.
       
 19492 		 *
       
 19493 		 * @constructor
       
 19494 		 * @method init
       
 19495 		 * @param {String} selector CSS like selector expression.
       
 19496 		 */
       
 19497 		init: function(selector) {
       
 19498 			var match = this.match;
       
 19499 
       
 19500 			function compileNameFilter(name) {
       
 19501 				if (name) {
       
 19502 					name = name.toLowerCase();
       
 19503 
       
 19504 					return function(item) {
       
 19505 						return name === '*' || item.type === name;
       
 19506 					};
       
 19507 				}
       
 19508 			}
       
 19509 
       
 19510 			function compileIdFilter(id) {
       
 19511 				if (id) {
       
 19512 					return function(item) {
       
 19513 						return item._name === id;
       
 19514 					};
       
 19515 				}
       
 19516 			}
       
 19517 
       
 19518 			function compileClassesFilter(classes) {
       
 19519 				if (classes) {
       
 19520 					classes = classes.split('.');
       
 19521 
       
 19522 					return function(item) {
       
 19523 						var i = classes.length;
       
 19524 
       
 19525 						while (i--) {
       
 19526 							if (!item.hasClass(classes[i])) {
       
 19527 								return false;
       
 19528 							}
       
 19529 						}
       
 19530 
       
 19531 						return true;
       
 19532 					};
       
 19533 				}
       
 19534 			}
       
 19535 
       
 19536 			function compileAttrFilter(name, cmp, check) {
       
 19537 				if (name) {
       
 19538 					return function(item) {
       
 19539 						var value = item[name] ? item[name]() : '';
       
 19540 
       
 19541 						return !cmp ? !!check :
       
 19542 							cmp === "=" ? value === check :
       
 19543 							cmp === "*=" ? value.indexOf(check) >= 0 :
       
 19544 							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
       
 19545 							cmp === "!=" ? value != check :
       
 19546 							cmp === "^=" ? value.indexOf(check) === 0 :
       
 19547 							cmp === "$=" ? value.substr(value.length - check.length) === check :
       
 19548 							false;
       
 19549 					};
       
 19550 				}
       
 19551 			}
       
 19552 
       
 19553 			function compilePsuedoFilter(name) {
       
 19554 				var notSelectors;
       
 19555 
       
 19556 				if (name) {
       
 19557 					name = /(?:not\((.+)\))|(.+)/i.exec(name);
       
 19558 
       
 19559 					if (!name[1]) {
       
 19560 						name = name[2];
       
 19561 
       
 19562 						return function(item, index, length) {
       
 19563 							return name === 'first' ? index === 0 :
       
 19564 								name === 'last' ? index === length - 1 :
       
 19565 								name === 'even' ? index % 2 === 0 :
       
 19566 								name === 'odd' ? index % 2 === 1 :
       
 19567 								item[name] ? item[name]() :
       
 19568 								false;
       
 19569 						};
       
 19570 					} else {
       
 19571 						// Compile not expression
       
 19572 						notSelectors = parseChunks(name[1], []);
       
 19573 
       
 19574 						return function(item) {
       
 19575 							return !match(item, notSelectors);
       
 19576 						};
       
 19577 					}
       
 19578 				}
       
 19579 			}
       
 19580 
       
 19581 			function compile(selector, filters, direct) {
       
 19582 				var parts;
       
 19583 
       
 19584 				function add(filter) {
       
 19585 					if (filter) {
       
 19586 						filters.push(filter);
       
 19587 					}
       
 19588 				}
       
 19589 
       
 19590 				// Parse expression into parts
       
 19591 				parts = expression.exec(selector.replace(whiteSpace, ''));
       
 19592 
       
 19593 				add(compileNameFilter(parts[1]));
       
 19594 				add(compileIdFilter(parts[2]));
       
 19595 				add(compileClassesFilter(parts[3]));
       
 19596 				add(compileAttrFilter(parts[4], parts[5], parts[6]));
       
 19597 				add(compilePsuedoFilter(parts[7]));
       
 19598 
       
 19599 				// Mark the filter with psuedo for performance
       
 19600 				filters.psuedo = !!parts[7];
       
 19601 				filters.direct = direct;
       
 19602 
       
 19603 				return filters;
       
 19604 			}
       
 19605 
       
 19606 			// Parser logic based on Sizzle by John Resig
       
 19607 			function parseChunks(selector, selectors) {
       
 19608 				var parts = [], extra, matches, i;
       
 19609 
       
 19610 				do {
       
 19611 					chunker.exec("");
       
 19612 					matches = chunker.exec(selector);
       
 19613 
       
 19614 					if (matches) {
       
 19615 						selector = matches[3];
       
 19616 						parts.push(matches[1]);
       
 19617 
       
 19618 						if (matches[2]) {
       
 19619 							extra = matches[3];
       
 19620 							break;
       
 19621 						}
       
 19622 					}
       
 19623 				} while (matches);
       
 19624 
       
 19625 				if (extra) {
       
 19626 					parseChunks(extra, selectors);
       
 19627 				}
       
 19628 
       
 19629 				selector = [];
       
 19630 				for (i = 0; i < parts.length; i++) {
       
 19631 					if (parts[i] != '>') {
       
 19632 						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
       
 19633 					}
       
 19634 				}
       
 19635 
       
 19636 				selectors.push(selector);
       
 19637 
       
 19638 				return selectors;
       
 19639 			}
       
 19640 
       
 19641 			this._selectors = parseChunks(selector, []);
       
 19642 		},
       
 19643 
       
 19644 		/**
       
 19645 		 * Returns true/false if the selector matches the specified control.
       
 19646 		 *
       
 19647 		 * @method match
       
 19648 		 * @param {tinymce.ui.Control} control Control to match agains the selector.
       
 19649 		 * @param {Array} selectors Optional array of selectors, mostly used internally.
       
 19650 		 * @return {Boolean} true/false state if the control matches or not.
       
 19651 		 */
       
 19652 		match: function(control, selectors) {
       
 19653 			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
       
 19654 
       
 19655 			selectors = selectors || this._selectors;
       
 19656 			for (i = 0, l = selectors.length; i < l; i++) {
       
 19657 				selector = selectors[i];
       
 19658 				sl = selector.length;
       
 19659 				item = control;
       
 19660 				count = 0;
       
 19661 
       
 19662 				for (si = sl - 1; si >= 0; si--) {
       
 19663 					filters = selector[si];
       
 19664 
       
 19665 					while (item) {
       
 19666 						// Find the index and length since a psuedo filter like :first needs it
       
 19667 						if (filters.psuedo) {
       
 19668 							siblings = item.parent().items();
       
 19669 							index = length = siblings.length;
       
 19670 							while (index--) {
       
 19671 								if (siblings[index] === item) {
       
 19672 									break;
       
 19673 								}
       
 19674 							}
       
 19675 						}
       
 19676 
       
 19677 						for (fi = 0, fl = filters.length; fi < fl; fi++) {
       
 19678 							if (!filters[fi](item, index, length)) {
       
 19679 								fi = fl + 1;
       
 19680 								break;
       
 19681 							}
       
 19682 						}
       
 19683 
       
 19684 						if (fi === fl) {
       
 19685 							count++;
       
 19686 							break;
       
 19687 						} else {
       
 19688 							// If it didn't match the right most expression then
       
 19689 							// break since it's no point looking at the parents
       
 19690 							if (si === sl - 1) {
       
 19691 								break;
       
 19692 							}
       
 19693 						}
       
 19694 
       
 19695 						item = item.parent();
       
 19696 					}
       
 19697 				}
       
 19698 
       
 19699 				// If we found all selectors then return true otherwise continue looking
       
 19700 				if (count === sl) {
       
 19701 					return true;
       
 19702 				}
       
 19703 			}
       
 19704 
       
 19705 			return false;
       
 19706 		},
       
 19707 
       
 19708 		/**
       
 19709 		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
       
 19710 		 *
       
 19711 		 * @method find
       
 19712 		 * @param {tinymce.ui.Control} container Container to look for items in.
       
 19713 		 * @return {tinymce.ui.Collection} Collection with matched elements.
       
 19714 		 */
       
 19715 		find: function(container) {
       
 19716 			var matches = [], i, l, selectors = this._selectors;
       
 19717 
       
 19718 			function collect(items, selector, index) {
       
 19719 				var i, l, fi, fl, item, filters = selector[index];
       
 19720 
       
 19721 				for (i = 0, l = items.length; i < l; i++) {
       
 19722 					item = items[i];
       
 19723 
       
 19724 					// Run each filter agains the item
       
 19725 					for (fi = 0, fl = filters.length; fi < fl; fi++) {
       
 19726 						if (!filters[fi](item, i, l)) {
       
 19727 							fi = fl + 1;
       
 19728 							break;
       
 19729 						}
       
 19730 					}
       
 19731 
       
 19732 					// All filters matched the item
       
 19733 					if (fi === fl) {
       
 19734 						// Matched item is on the last expression like: panel toolbar [button]
       
 19735 						if (index == selector.length - 1) {
       
 19736 							matches.push(item);
       
 19737 						} else {
       
 19738 							// Collect next expression type
       
 19739 							if (item.items) {
       
 19740 								collect(item.items(), selector, index + 1);
       
 19741 							}
       
 19742 						}
       
 19743 					} else if (filters.direct) {
       
 19744 						return;
       
 19745 					}
       
 19746 
       
 19747 					// Collect child items
       
 19748 					if (item.items) {
       
 19749 						collect(item.items(), selector, index);
       
 19750 					}
       
 19751 				}
       
 19752 			}
       
 19753 
       
 19754 			if (container.items) {
       
 19755 				for (i = 0, l = selectors.length; i < l; i++) {
       
 19756 					collect(container.items(), selectors[i], 0);
       
 19757 				}
       
 19758 
       
 19759 				// Unique the matches if needed
       
 19760 				if (l > 1) {
       
 19761 					matches = unique(matches);
       
 19762 				}
       
 19763 			}
       
 19764 
       
 19765 			// Fix for circular reference
       
 19766 			if (!Collection) {
       
 19767 				// TODO: Fix me!
       
 19768 				Collection = Selector.Collection;
       
 19769 			}
       
 19770 
       
 19771 			return new Collection(matches);
       
 19772 		}
       
 19773 	});
       
 19774 
       
 19775 	return Selector;
       
 19776 });
       
 19777 
       
 19778 // Included from: js/tinymce/classes/ui/Collection.js
       
 19779 
       
 19780 /**
       
 19781  * Collection.js
       
 19782  *
       
 19783  * Copyright, Moxiecode Systems AB
       
 19784  * Released under LGPL License.
       
 19785  *
       
 19786  * License: http://www.tinymce.com/license
       
 19787  * Contributing: http://www.tinymce.com/contributing
       
 19788  */
       
 19789 
       
 19790 /**
       
 19791  * Control collection, this class contains control instances and it enables you to
       
 19792  * perform actions on all the contained items. This is very similar to how jQuery works.
       
 19793  *
       
 19794  * @example
       
 19795  * someCollection.show().disabled(true);
       
 19796  *
       
 19797  * @class tinymce.ui.Collection
       
 19798  */
       
 19799 define("tinymce/ui/Collection", [
       
 19800 	"tinymce/util/Tools",
       
 19801 	"tinymce/ui/Selector",
       
 19802 	"tinymce/util/Class"
       
 19803 ], function(Tools, Selector, Class) {
       
 19804 	"use strict";
       
 19805 
       
 19806 	var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice;
       
 19807 
       
 19808 	proto = {
       
 19809 		/**
       
 19810 		 * Current number of contained control instances.
       
 19811 		 *
       
 19812 		 * @field length
       
 19813 		 * @type Number
       
 19814 		 */
       
 19815 		length: 0,
       
 19816 
       
 19817 		/**
       
 19818 		 * Constructor for the collection.
       
 19819 		 *
       
 19820 		 * @constructor
       
 19821 		 * @method init
       
 19822 		 * @param {Array} items Optional array with items to add.
       
 19823 		 */
       
 19824 		init: function(items) {
       
 19825 			if (items) {
       
 19826 				this.add(items);
       
 19827 			}
       
 19828 		},
       
 19829 
       
 19830 		/**
       
 19831 		 * Adds new items to the control collection.
       
 19832 		 *
       
 19833 		 * @method add
       
 19834 		 * @param {Array} items Array if items to add to collection.
       
 19835 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 19836 		 */
       
 19837 		add: function(items) {
       
 19838 			var self = this;
       
 19839 
       
 19840 			// Force single item into array
       
 19841 			if (!Tools.isArray(items)) {
       
 19842 				if (items instanceof Collection) {
       
 19843 					self.add(items.toArray());
       
 19844 				} else {
       
 19845 					push.call(self, items);
       
 19846 				}
       
 19847 			} else {
       
 19848 				push.apply(self, items);
       
 19849 			}
       
 19850 
       
 19851 			return self;
       
 19852 		},
       
 19853 
       
 19854 		/**
       
 19855 		 * Sets the contents of the collection. This will remove any existing items
       
 19856 		 * and replace them with the ones specified in the input array.
       
 19857 		 *
       
 19858 		 * @method set
       
 19859 		 * @param {Array} items Array with items to set into the Collection.
       
 19860 		 * @return {tinymce.ui.Collection} Collection instance.
       
 19861 		 */
       
 19862 		set: function(items) {
       
 19863 			var self = this, len = self.length, i;
       
 19864 
       
 19865 			self.length = 0;
       
 19866 			self.add(items);
       
 19867 
       
 19868 			// Remove old entries
       
 19869 			for (i = self.length; i < len; i++) {
       
 19870 				delete self[i];
       
 19871 			}
       
 19872 
       
 19873 			return self;
       
 19874 		},
       
 19875 
       
 19876 		/**
       
 19877 		 * Filters the collection item based on the specified selector expression or selector function.
       
 19878 		 *
       
 19879 		 * @method filter
       
 19880 		 * @param {String} selector Selector expression to filter items by.
       
 19881 		 * @return {tinymce.ui.Collection} Collection containing the filtered items.
       
 19882 		 */
       
 19883 		filter: function(selector) {
       
 19884 			var self = this, i, l, matches = [], item, match;
       
 19885 
       
 19886 			// Compile string into selector expression
       
 19887 			if (typeof selector === "string") {
       
 19888 				selector = new Selector(selector);
       
 19889 
       
 19890 				match = function(item) {
       
 19891 					return selector.match(item);
       
 19892 				};
       
 19893 			} else {
       
 19894 				// Use selector as matching function
       
 19895 				match = selector;
       
 19896 			}
       
 19897 
       
 19898 			for (i = 0, l = self.length; i < l; i++) {
       
 19899 				item = self[i];
       
 19900 
       
 19901 				if (match(item)) {
       
 19902 					matches.push(item);
       
 19903 				}
       
 19904 			}
       
 19905 
       
 19906 			return new Collection(matches);
       
 19907 		},
       
 19908 
       
 19909 		/**
       
 19910 		 * Slices the items within the collection.
       
 19911 		 *
       
 19912 		 * @method slice
       
 19913 		 * @param {Number} index Index to slice at.
       
 19914 		 * @param {Number} len Optional length to slice.
       
 19915 		 * @return {tinymce.ui.Collection} Current collection.
       
 19916 		 */
       
 19917 		slice: function() {
       
 19918 			return new Collection(slice.apply(this, arguments));
       
 19919 		},
       
 19920 
       
 19921 		/**
       
 19922 		 * Makes the current collection equal to the specified index.
       
 19923 		 *
       
 19924 		 * @method eq
       
 19925 		 * @param {Number} index Index of the item to set the collection to.
       
 19926 		 * @return {tinymce.ui.Collection} Current collection.
       
 19927 		 */
       
 19928 		eq: function(index) {
       
 19929 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
       
 19930 		},
       
 19931 
       
 19932 		/**
       
 19933 		 * Executes the specified callback on each item in collection.
       
 19934 		 *
       
 19935 		 * @method each
       
 19936 		 * @param {function} callback Callback to execute for each item in collection.
       
 19937 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 19938 		 */
       
 19939 		each: function(callback) {
       
 19940 			Tools.each(this, callback);
       
 19941 
       
 19942 			return this;
       
 19943 		},
       
 19944 
       
 19945 		/**
       
 19946 		 * Returns an JavaScript array object of the contents inside the collection.
       
 19947 		 *
       
 19948 		 * @method toArray
       
 19949 		 * @return {Array} Array with all items from collection.
       
 19950 		 */
       
 19951 		toArray: function() {
       
 19952 			return Tools.toArray(this);
       
 19953 		},
       
 19954 
       
 19955 		/**
       
 19956 		 * Finds the index of the specified control or return -1 if it isn't in the collection.
       
 19957 		 *
       
 19958 		 * @method indexOf
       
 19959 		 * @param {Control} ctrl Control instance to look for.
       
 19960 		 * @return {Number} Index of the specified control or -1.
       
 19961 		 */
       
 19962 		indexOf: function(ctrl) {
       
 19963 			var self = this, i = self.length;
       
 19964 
       
 19965 			while (i--) {
       
 19966 				if (self[i] === ctrl) {
       
 19967 					break;
       
 19968 				}
       
 19969 			}
       
 19970 
       
 19971 			return i;
       
 19972 		},
       
 19973 
       
 19974 		/**
       
 19975 		 * Returns a new collection of the contents in reverse order.
       
 19976 		 *
       
 19977 		 * @method reverse
       
 19978 		 * @return {tinymce.ui.Collection} Collection instance with reversed items.
       
 19979 		 */
       
 19980 		reverse: function() {
       
 19981 			return new Collection(Tools.toArray(this).reverse());
       
 19982 		},
       
 19983 
       
 19984 		/**
       
 19985 		 * Returns true/false if the class exists or not.
       
 19986 		 *
       
 19987 		 * @method hasClass
       
 19988 		 * @param {String} cls Class to check for.
       
 19989 		 * @return {Boolean} true/false state if the class exists or not.
       
 19990 		 */
       
 19991 		hasClass: function(cls) {
       
 19992 			return this[0] ? this[0].hasClass(cls) : false;
       
 19993 		},
       
 19994 
       
 19995 		/**
       
 19996 		 * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>);
       
 19997 		 *
       
 19998 		 * @method prop
       
 19999 		 * @param {String} name Property name to get/set.
       
 20000 		 * @param {Object} value Optional object value to set.
       
 20001 		 * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation.
       
 20002 		 */
       
 20003 		prop: function(name, value) {
       
 20004 			var self = this, undef, item;
       
 20005 
       
 20006 			if (value !== undef) {
       
 20007 				self.each(function(item) {
       
 20008 					if (item[name]) {
       
 20009 						item[name](value);
       
 20010 					}
       
 20011 				});
       
 20012 
       
 20013 				return self;
       
 20014 			}
       
 20015 
       
 20016 			item = self[0];
       
 20017 
       
 20018 			if (item && item[name]) {
       
 20019 				return item[name]();
       
 20020 			}
       
 20021 		},
       
 20022 
       
 20023 		/**
       
 20024 		 * Executes the specific function name with optional arguments an all items in collection if it exists.
       
 20025 		 *
       
 20026 		 * @example collection.exec("myMethod", arg1, arg2, arg3);
       
 20027 		 * @method exec
       
 20028 		 * @param {String} name Name of the function to execute.
       
 20029 		 * @param {Object} ... Multiple arguments to pass to each function.
       
 20030 		 * @return {tinymce.ui.Collection} Current collection.
       
 20031 		 */
       
 20032 		exec: function(name) {
       
 20033 			var self = this, args = Tools.toArray(arguments).slice(1);
       
 20034 
       
 20035 			self.each(function(item) {
       
 20036 				if (item[name]) {
       
 20037 					item[name].apply(item, args);
       
 20038 				}
       
 20039 			});
       
 20040 
       
 20041 			return self;
       
 20042 		},
       
 20043 
       
 20044 		/**
       
 20045 		 * Remove all items from collection and DOM.
       
 20046 		 *
       
 20047 		 * @method remove
       
 20048 		 * @return {tinymce.ui.Collection} Current collection.
       
 20049 		 */
       
 20050 		remove: function() {
       
 20051 			var i = this.length;
       
 20052 
       
 20053 			while (i--) {
       
 20054 				this[i].remove();
       
 20055 			}
       
 20056 
       
 20057 			return this;
       
 20058 		}
       
 20059 
       
 20060 		/**
       
 20061 		 * Fires the specified event by name and arguments on the control. This will execute all
       
 20062 		 * bound event handlers.
       
 20063 		 *
       
 20064 		 * @method fire
       
 20065 		 * @param {String} name Name of the event to fire.
       
 20066 		 * @param {Object} args Optional arguments to pass to the event.
       
 20067 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20068 		 */
       
 20069 		// fire: function(event, args) {}, -- Generated by code below
       
 20070 
       
 20071 		/**
       
 20072 		 * Binds a callback to the specified event. This event can both be
       
 20073 		 * native browser events like "click" or custom ones like PostRender.
       
 20074 		 *
       
 20075 		 * The callback function will have two parameters the first one being the control that received the event
       
 20076 		 * the second one will be the event object either the browsers native event object or a custom JS object.
       
 20077 		 *
       
 20078 		 * @method on
       
 20079 		 * @param {String} name Name of the event to bind. For example "click".
       
 20080 		 * @param {String/function} callback Callback function to execute ones the event occurs.
       
 20081 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20082 		 */
       
 20083 		// on: function(name, callback) {}, -- Generated by code below
       
 20084 
       
 20085 		/**
       
 20086 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
       
 20087 		 * parameter all event handlers will be removed. If you omit the callback all event handles
       
 20088 		 * by the specified name will be removed.
       
 20089 		 *
       
 20090 		 * @method off
       
 20091 		 * @param {String} name Optional name for the event to unbind.
       
 20092 		 * @param {function} callback Optional callback function to unbind.
       
 20093 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20094 		 */
       
 20095 		// off: function(name, callback) {}, -- Generated by code below
       
 20096 
       
 20097 		/**
       
 20098 		 * Shows the items in the current collection.
       
 20099 		 *
       
 20100 		 * @method show
       
 20101 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20102 		 */
       
 20103 		// show: function() {}, -- Generated by code below
       
 20104 
       
 20105 		/**
       
 20106 		 * Hides the items in the current collection.
       
 20107 		 *
       
 20108 		 * @method hide
       
 20109 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20110 		 */
       
 20111 		// hide: function() {}, -- Generated by code below
       
 20112 
       
 20113 		/**
       
 20114 		 * Sets/gets the text contents of the items in the current collection.
       
 20115 		 *
       
 20116 		 * @method text
       
 20117 		 * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation.
       
 20118 		 */
       
 20119 		// text: function(value) {}, -- Generated by code below
       
 20120 
       
 20121 		/**
       
 20122 		 * Sets/gets the name contents of the items in the current collection.
       
 20123 		 *
       
 20124 		 * @method name
       
 20125 		 * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation.
       
 20126 		 */
       
 20127 		// name: function(value) {}, -- Generated by code below
       
 20128 
       
 20129 		/**
       
 20130 		 * Sets/gets the disabled state on the items in the current collection.
       
 20131 		 *
       
 20132 		 * @method disabled
       
 20133 		 * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation.
       
 20134 		 */
       
 20135 		// disabled: function(state) {}, -- Generated by code below
       
 20136 
       
 20137 		/**
       
 20138 		 * Sets/gets the active state on the items in the current collection.
       
 20139 		 *
       
 20140 		 * @method active
       
 20141 		 * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation.
       
 20142 		 */
       
 20143 		// active: function(state) {}, -- Generated by code below
       
 20144 
       
 20145 		/**
       
 20146 		 * Sets/gets the selected state on the items in the current collection.
       
 20147 		 *
       
 20148 		 * @method selected
       
 20149 		 * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation.
       
 20150 		 */
       
 20151 		// selected: function(state) {}, -- Generated by code below
       
 20152 
       
 20153 		/**
       
 20154 		 * Sets/gets the selected state on the items in the current collection.
       
 20155 		 *
       
 20156 		 * @method visible
       
 20157 		 * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation.
       
 20158 		 */
       
 20159 		// visible: function(state) {}, -- Generated by code below
       
 20160 
       
 20161 		/**
       
 20162 		 * Adds a class to all items in the collection.
       
 20163 		 *
       
 20164 		 * @method addClass
       
 20165 		 * @param {String} cls Class to add to each item.
       
 20166 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20167 		 */
       
 20168 		// addClass: function(cls) {}, -- Generated by code below
       
 20169 
       
 20170 		/**
       
 20171 		 * Removes the specified class from all items in collection.
       
 20172 		 *
       
 20173 		 * @method removeClass
       
 20174 		 * @param {String} cls Class to remove from each item.
       
 20175 		 * @return {tinymce.ui.Collection} Current collection instance.
       
 20176 		 */
       
 20177 		// removeClass: function(cls) {}, -- Generated by code below
       
 20178 	};
       
 20179 
       
 20180 	// Extend tinymce.ui.Collection prototype with some generated control specific methods
       
 20181 	Tools.each('fire on off show hide addClass removeClass append prepend before after reflow'.split(' '), function(name) {
       
 20182 		proto[name] = function() {
       
 20183 			var args = Tools.toArray(arguments);
       
 20184 
       
 20185 			this.each(function(ctrl) {
       
 20186 				if (name in ctrl) {
       
 20187 					ctrl[name].apply(ctrl, args);
       
 20188 				}
       
 20189 			});
       
 20190 
       
 20191 			return this;
       
 20192 		};
       
 20193 	});
       
 20194 
       
 20195 	// Extend tinymce.ui.Collection prototype with some property methods
       
 20196 	Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) {
       
 20197 		proto[name] = function(value) {
       
 20198 			return this.prop(name, value);
       
 20199 		};
       
 20200 	});
       
 20201 
       
 20202 	// Create class based on the new prototype
       
 20203 	Collection = Class.extend(proto);
       
 20204 
       
 20205 	// Stick Collection into Selector to prevent circual references
       
 20206 	Selector.Collection = Collection;
       
 20207 
       
 20208 	return Collection;
       
 20209 });
       
 20210 
       
 20211 // Included from: js/tinymce/classes/ui/DomUtils.js
       
 20212 
       
 20213 /**
       
 20214  * DOMUtils.js
       
 20215  *
       
 20216  * Copyright, Moxiecode Systems AB
       
 20217  * Released under LGPL License.
       
 20218  *
       
 20219  * License: http://www.tinymce.com/license
       
 20220  * Contributing: http://www.tinymce.com/contributing
       
 20221  */
       
 20222 
       
 20223 define("tinymce/ui/DomUtils", [
       
 20224 	"tinymce/util/Tools",
       
 20225 	"tinymce/dom/DOMUtils"
       
 20226 ], function(Tools, DOMUtils) {
       
 20227 	"use strict";
       
 20228 
       
 20229 	var count = 0;
       
 20230 
       
 20231 	return {
       
 20232 		id: function() {
       
 20233 			return 'mceu_' + (count++);
       
 20234 		},
       
 20235 
       
 20236 		createFragment: function(html) {
       
 20237 			return DOMUtils.DOM.createFragment(html);
       
 20238 		},
       
 20239 
       
 20240 		getWindowSize: function() {
       
 20241 			return DOMUtils.DOM.getViewPort();
       
 20242 		},
       
 20243 
       
 20244 		getSize: function(elm) {
       
 20245 			var width, height;
       
 20246 
       
 20247 			if (elm.getBoundingClientRect) {
       
 20248 				var rect = elm.getBoundingClientRect();
       
 20249 
       
 20250 				width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth);
       
 20251 				height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight);
       
 20252 			} else {
       
 20253 				width = elm.offsetWidth;
       
 20254 				height = elm.offsetHeight;
       
 20255 			}
       
 20256 
       
 20257 			return {width: width, height: height};
       
 20258 		},
       
 20259 
       
 20260 		getPos: function(elm, root) {
       
 20261 			return DOMUtils.DOM.getPos(elm, root);
       
 20262 		},
       
 20263 
       
 20264 		getViewPort: function(win) {
       
 20265 			return DOMUtils.DOM.getViewPort(win);
       
 20266 		},
       
 20267 
       
 20268 		get: function(id) {
       
 20269 			return document.getElementById(id);
       
 20270 		},
       
 20271 
       
 20272 		addClass: function(elm, cls) {
       
 20273 			return DOMUtils.DOM.addClass(elm, cls);
       
 20274 		},
       
 20275 
       
 20276 		removeClass: function(elm, cls) {
       
 20277 			return DOMUtils.DOM.removeClass(elm, cls);
       
 20278 		},
       
 20279 
       
 20280 		hasClass: function(elm, cls) {
       
 20281 			return DOMUtils.DOM.hasClass(elm, cls);
       
 20282 		},
       
 20283 
       
 20284 		toggleClass: function(elm, cls, state) {
       
 20285 			return DOMUtils.DOM.toggleClass(elm, cls, state);
       
 20286 		},
       
 20287 
       
 20288 		css: function(elm, name, value) {
       
 20289 			return DOMUtils.DOM.setStyle(elm, name, value);
       
 20290 		},
       
 20291 
       
 20292 		getRuntimeStyle: function(elm, name) {
       
 20293 			return DOMUtils.DOM.getStyle(elm, name, true);
       
 20294 		},
       
 20295 
       
 20296 		on: function(target, name, callback, scope) {
       
 20297 			return DOMUtils.DOM.bind(target, name, callback, scope);
       
 20298 		},
       
 20299 
       
 20300 		off: function(target, name, callback) {
       
 20301 			return DOMUtils.DOM.unbind(target, name, callback);
       
 20302 		},
       
 20303 
       
 20304 		fire: function(target, name, args) {
       
 20305 			return DOMUtils.DOM.fire(target, name, args);
       
 20306 		},
       
 20307 
       
 20308 		innerHtml: function(elm, html) {
       
 20309 			// Workaround for <div> in <p> bug on IE 8 #6178
       
 20310 			DOMUtils.DOM.setHTML(elm, html);
       
 20311 		}
       
 20312 	};
       
 20313 });
       
 20314 
       
 20315 // Included from: js/tinymce/classes/ui/Control.js
       
 20316 
       
 20317 /**
       
 20318  * Control.js
       
 20319  *
       
 20320  * Copyright, Moxiecode Systems AB
       
 20321  * Released under LGPL License.
       
 20322  *
       
 20323  * License: http://www.tinymce.com/license
       
 20324  * Contributing: http://www.tinymce.com/contributing
       
 20325  */
       
 20326 
       
 20327 /*eslint consistent-this:0 */
       
 20328 
       
 20329 /**
       
 20330  * This is the base class for all controls and containers. All UI control instances inherit
       
 20331  * from this one as it has the base logic needed by all of them.
       
 20332  *
       
 20333  * @class tinymce.ui.Control
       
 20334  */
       
 20335 define("tinymce/ui/Control", [
       
 20336 	"tinymce/util/Class",
       
 20337 	"tinymce/util/Tools",
       
 20338 	"tinymce/util/EventDispatcher",
       
 20339 	"tinymce/ui/Collection",
       
 20340 	"tinymce/ui/DomUtils"
       
 20341 ], function(Class, Tools, EventDispatcher, Collection, DomUtils) {
       
 20342 	"use strict";
       
 20343 
       
 20344 	var hasMouseWheelEventSupport = "onmousewheel" in document;
       
 20345 	var hasWheelEventSupport = false;
       
 20346 	var classPrefix = "mce-";
       
 20347 
       
 20348 	function getEventDispatcher(obj) {
       
 20349 		if (!obj._eventDispatcher) {
       
 20350 			obj._eventDispatcher = new EventDispatcher({
       
 20351 				scope: obj,
       
 20352 				toggleEvent: function(name, state) {
       
 20353 					if (state && EventDispatcher.isNative(name)) {
       
 20354 						if (!obj._nativeEvents) {
       
 20355 							obj._nativeEvents = {};
       
 20356 						}
       
 20357 
       
 20358 						obj._nativeEvents[name] = true;
       
 20359 
       
 20360 						if (obj._rendered) {
       
 20361 							obj.bindPendingEvents();
       
 20362 						}
       
 20363 					}
       
 20364 				}
       
 20365 			});
       
 20366 		}
       
 20367 
       
 20368 		return obj._eventDispatcher;
       
 20369 	}
       
 20370 
       
 20371 	var Control = Class.extend({
       
 20372 		Statics: {
       
 20373 			classPrefix: classPrefix
       
 20374 		},
       
 20375 
       
 20376 		isRtl: function() {
       
 20377 			return Control.rtl;
       
 20378 		},
       
 20379 
       
 20380 		/**
       
 20381 		 * Class/id prefix to use for all controls.
       
 20382 		 *
       
 20383 		 * @final
       
 20384 		 * @field {String} classPrefix
       
 20385 		 */
       
 20386 		classPrefix: classPrefix,
       
 20387 
       
 20388 		/**
       
 20389 		 * Constructs a new control instance with the specified settings.
       
 20390 		 *
       
 20391 		 * @constructor
       
 20392 		 * @param {Object} settings Name/value object with settings.
       
 20393 		 * @setting {String} style Style CSS properties to add.
       
 20394 		 * @setting {String} border Border box values example: 1 1 1 1
       
 20395 		 * @setting {String} padding Padding box values example: 1 1 1 1
       
 20396 		 * @setting {String} margin Margin box values example: 1 1 1 1
       
 20397 		 * @setting {Number} minWidth Minimal width for the control.
       
 20398 		 * @setting {Number} minHeight Minimal height for the control.
       
 20399 		 * @setting {String} classes Space separated list of classes to add.
       
 20400 		 * @setting {String} role WAI-ARIA role to use for control.
       
 20401 		 * @setting {Boolean} hidden Is the control hidden by default.
       
 20402 		 * @setting {Boolean} disabled Is the control disabled by default.
       
 20403 		 * @setting {String} name Name of the control instance.
       
 20404 		 */
       
 20405 		init: function(settings) {
       
 20406 			var self = this, classes, i;
       
 20407 
       
 20408 			self.settings = settings = Tools.extend({}, self.Defaults, settings);
       
 20409 
       
 20410 			// Initial states
       
 20411 			self._id = settings.id || DomUtils.id();
       
 20412 			self._text = self._name = '';
       
 20413 			self._width = self._height = 0;
       
 20414 			self._aria = {role: settings.role};
       
 20415 			this._elmCache = {};
       
 20416 
       
 20417 			// Setup classes
       
 20418 			classes = settings.classes;
       
 20419 			if (classes) {
       
 20420 				classes = classes.split(' ');
       
 20421 				classes.map = {};
       
 20422 				i = classes.length;
       
 20423 				while (i--) {
       
 20424 					classes.map[classes[i]] = true;
       
 20425 				}
       
 20426 			}
       
 20427 
       
 20428 			self._classes = classes || [];
       
 20429 			self.visible(true);
       
 20430 
       
 20431 			// Set some properties
       
 20432 			Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
       
 20433 				var value = settings[name], undef;
       
 20434 
       
 20435 				if (value !== undef) {
       
 20436 					self[name](value);
       
 20437 				} else if (self['_' + name] === undef) {
       
 20438 					self['_' + name] = false;
       
 20439 				}
       
 20440 			});
       
 20441 
       
 20442 			self.on('click', function() {
       
 20443 				if (self.disabled()) {
       
 20444 					return false;
       
 20445 				}
       
 20446 			});
       
 20447 
       
 20448 			// TODO: Is this needed duplicate code see above?
       
 20449 			if (settings.classes) {
       
 20450 				Tools.each(settings.classes.split(' '), function(cls) {
       
 20451 					self.addClass(cls);
       
 20452 				});
       
 20453 			}
       
 20454 
       
 20455 			/**
       
 20456 			 * Name/value object with settings for the current control.
       
 20457 			 *
       
 20458 			 * @field {Object} settings
       
 20459 			 */
       
 20460 			self.settings = settings;
       
 20461 
       
 20462 			self._borderBox = self.parseBox(settings.border);
       
 20463 			self._paddingBox = self.parseBox(settings.padding);
       
 20464 			self._marginBox = self.parseBox(settings.margin);
       
 20465 
       
 20466 			if (settings.hidden) {
       
 20467 				self.hide();
       
 20468 			}
       
 20469 		},
       
 20470 
       
 20471 		// Will generate getter/setter methods for these properties
       
 20472 		Properties: 'parent,title,text,width,height,disabled,active,name,value',
       
 20473 
       
 20474 		// Will generate empty dummy functions for these
       
 20475 		Methods: 'renderHtml',
       
 20476 
       
 20477 		/**
       
 20478 		 * Returns the root element to render controls into.
       
 20479 		 *
       
 20480 		 * @method getContainerElm
       
 20481 		 * @return {Element} HTML DOM element to render into.
       
 20482 		 */
       
 20483 		getContainerElm: function() {
       
 20484 			return document.body;
       
 20485 		},
       
 20486 
       
 20487 		/**
       
 20488 		 * Returns a control instance for the current DOM element.
       
 20489 		 *
       
 20490 		 * @method getParentCtrl
       
 20491 		 * @param {Element} elm HTML dom element to get parent control from.
       
 20492 		 * @return {tinymce.ui.Control} Control instance or undefined.
       
 20493 		 */
       
 20494 		getParentCtrl: function(elm) {
       
 20495 			var ctrl, lookup = this.getRoot().controlIdLookup;
       
 20496 
       
 20497 			while (elm && lookup) {
       
 20498 				ctrl = lookup[elm.id];
       
 20499 				if (ctrl) {
       
 20500 					break;
       
 20501 				}
       
 20502 
       
 20503 				elm = elm.parentNode;
       
 20504 			}
       
 20505 
       
 20506 			return ctrl;
       
 20507 		},
       
 20508 
       
 20509 		/**
       
 20510 		 * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
       
 20511 		 *
       
 20512 		 * @method parseBox
       
 20513 		 * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
       
 20514 		 * @return {Object} Object with top/right/bottom/left properties.
       
 20515 		 * @private
       
 20516 		 */
       
 20517 		parseBox: function(value) {
       
 20518 			var len, radix = 10;
       
 20519 
       
 20520 			if (!value) {
       
 20521 				return;
       
 20522 			}
       
 20523 
       
 20524 			if (typeof value === "number") {
       
 20525 				value = value || 0;
       
 20526 
       
 20527 				return {
       
 20528 					top: value,
       
 20529 					left: value,
       
 20530 					bottom: value,
       
 20531 					right: value
       
 20532 				};
       
 20533 			}
       
 20534 
       
 20535 			value = value.split(' ');
       
 20536 			len = value.length;
       
 20537 
       
 20538 			if (len === 1) {
       
 20539 				value[1] = value[2] = value[3] = value[0];
       
 20540 			} else if (len === 2) {
       
 20541 				value[2] = value[0];
       
 20542 				value[3] = value[1];
       
 20543 			} else if (len === 3) {
       
 20544 				value[3] = value[1];
       
 20545 			}
       
 20546 
       
 20547 			return {
       
 20548 				top: parseInt(value[0], radix) || 0,
       
 20549 				right: parseInt(value[1], radix) || 0,
       
 20550 				bottom: parseInt(value[2], radix) || 0,
       
 20551 				left: parseInt(value[3], radix) || 0
       
 20552 			};
       
 20553 		},
       
 20554 
       
 20555 		borderBox: function() {
       
 20556 			return this._borderBox;
       
 20557 		},
       
 20558 
       
 20559 		paddingBox: function() {
       
 20560 			return this._paddingBox;
       
 20561 		},
       
 20562 
       
 20563 		marginBox: function() {
       
 20564 			return this._marginBox;
       
 20565 		},
       
 20566 
       
 20567 		measureBox: function(elm, prefix) {
       
 20568 			function getStyle(name) {
       
 20569 				var defaultView = document.defaultView;
       
 20570 
       
 20571 				if (defaultView) {
       
 20572 					// Remove camelcase
       
 20573 					name = name.replace(/[A-Z]/g, function(a) {
       
 20574 						return '-' + a;
       
 20575 					});
       
 20576 
       
 20577 					return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
       
 20578 				}
       
 20579 
       
 20580 				return elm.currentStyle[name];
       
 20581 			}
       
 20582 
       
 20583 			function getSide(name) {
       
 20584 				var val = parseFloat(getStyle(name), 10);
       
 20585 
       
 20586 				return isNaN(val) ? 0 : val;
       
 20587 			}
       
 20588 
       
 20589 			return {
       
 20590 				top: getSide(prefix + "TopWidth"),
       
 20591 				right: getSide(prefix + "RightWidth"),
       
 20592 				bottom: getSide(prefix + "BottomWidth"),
       
 20593 				left: getSide(prefix + "LeftWidth")
       
 20594 			};
       
 20595 		},
       
 20596 
       
 20597 		/**
       
 20598 		 * Initializes the current controls layout rect.
       
 20599 		 * This will be executed by the layout managers to determine the
       
 20600 		 * default minWidth/minHeight etc.
       
 20601 		 *
       
 20602 		 * @method initLayoutRect
       
 20603 		 * @return {Object} Layout rect instance.
       
 20604 		 */
       
 20605 		initLayoutRect: function() {
       
 20606 			var self = this, settings = self.settings, borderBox, layoutRect;
       
 20607 			var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
       
 20608 			var startMinWidth, startMinHeight, initialSize;
       
 20609 
       
 20610 			// Measure the current element
       
 20611 			borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
       
 20612 			self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
       
 20613 			self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
       
 20614 			initialSize = DomUtils.getSize(elm);
       
 20615 
       
 20616 			// Setup minWidth/minHeight and width/height
       
 20617 			startMinWidth = settings.minWidth;
       
 20618 			startMinHeight = settings.minHeight;
       
 20619 			minWidth = startMinWidth || initialSize.width;
       
 20620 			minHeight = startMinHeight || initialSize.height;
       
 20621 			width = settings.width;
       
 20622 			height = settings.height;
       
 20623 			autoResize = settings.autoResize;
       
 20624 			autoResize = typeof autoResize != "undefined" ? autoResize : !width && !height;
       
 20625 
       
 20626 			width = width || minWidth;
       
 20627 			height = height || minHeight;
       
 20628 
       
 20629 			var deltaW = borderBox.left + borderBox.right;
       
 20630 			var deltaH = borderBox.top + borderBox.bottom;
       
 20631 
       
 20632 			var maxW = settings.maxWidth || 0xFFFF;
       
 20633 			var maxH = settings.maxHeight || 0xFFFF;
       
 20634 
       
 20635 			// Setup initial layout rect
       
 20636 			self._layoutRect = layoutRect = {
       
 20637 				x: settings.x || 0,
       
 20638 				y: settings.y || 0,
       
 20639 				w: width,
       
 20640 				h: height,
       
 20641 				deltaW: deltaW,
       
 20642 				deltaH: deltaH,
       
 20643 				contentW: width - deltaW,
       
 20644 				contentH: height - deltaH,
       
 20645 				innerW: width - deltaW,
       
 20646 				innerH: height - deltaH,
       
 20647 				startMinWidth: startMinWidth || 0,
       
 20648 				startMinHeight: startMinHeight || 0,
       
 20649 				minW: Math.min(minWidth, maxW),
       
 20650 				minH: Math.min(minHeight, maxH),
       
 20651 				maxW: maxW,
       
 20652 				maxH: maxH,
       
 20653 				autoResize: autoResize,
       
 20654 				scrollW: 0
       
 20655 			};
       
 20656 
       
 20657 			self._lastLayoutRect = {};
       
 20658 
       
 20659 			return layoutRect;
       
 20660 		},
       
 20661 
       
 20662 		/**
       
 20663 		 * Getter/setter for the current layout rect.
       
 20664 		 *
       
 20665 		 * @method layoutRect
       
 20666 		 * @param {Object} [newRect] Optional new layout rect.
       
 20667 		 * @return {tinymce.ui.Control/Object} Current control or rect object.
       
 20668 		 */
       
 20669 		layoutRect: function(newRect) {
       
 20670 			var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
       
 20671 
       
 20672 			// Initialize default layout rect
       
 20673 			if (!curRect) {
       
 20674 				curRect = self.initLayoutRect();
       
 20675 			}
       
 20676 
       
 20677 			// Set new rect values
       
 20678 			if (newRect) {
       
 20679 				// Calc deltas between inner and outer sizes
       
 20680 				deltaWidth = curRect.deltaW;
       
 20681 				deltaHeight = curRect.deltaH;
       
 20682 
       
 20683 				// Set x position
       
 20684 				if (newRect.x !== undef) {
       
 20685 					curRect.x = newRect.x;
       
 20686 				}
       
 20687 
       
 20688 				// Set y position
       
 20689 				if (newRect.y !== undef) {
       
 20690 					curRect.y = newRect.y;
       
 20691 				}
       
 20692 
       
 20693 				// Set minW
       
 20694 				if (newRect.minW !== undef) {
       
 20695 					curRect.minW = newRect.minW;
       
 20696 				}
       
 20697 
       
 20698 				// Set minH
       
 20699 				if (newRect.minH !== undef) {
       
 20700 					curRect.minH = newRect.minH;
       
 20701 				}
       
 20702 
       
 20703 				// Set new width and calculate inner width
       
 20704 				size = newRect.w;
       
 20705 				if (size !== undef) {
       
 20706 					size = size < curRect.minW ? curRect.minW : size;
       
 20707 					size = size > curRect.maxW ? curRect.maxW : size;
       
 20708 					curRect.w = size;
       
 20709 					curRect.innerW = size - deltaWidth;
       
 20710 				}
       
 20711 
       
 20712 				// Set new height and calculate inner height
       
 20713 				size = newRect.h;
       
 20714 				if (size !== undef) {
       
 20715 					size = size < curRect.minH ? curRect.minH : size;
       
 20716 					size = size > curRect.maxH ? curRect.maxH : size;
       
 20717 					curRect.h = size;
       
 20718 					curRect.innerH = size - deltaHeight;
       
 20719 				}
       
 20720 
       
 20721 				// Set new inner width and calculate width
       
 20722 				size = newRect.innerW;
       
 20723 				if (size !== undef) {
       
 20724 					size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
       
 20725 					size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
       
 20726 					curRect.innerW = size;
       
 20727 					curRect.w = size + deltaWidth;
       
 20728 				}
       
 20729 
       
 20730 				// Set new height and calculate inner height
       
 20731 				size = newRect.innerH;
       
 20732 				if (size !== undef) {
       
 20733 					size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
       
 20734 					size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
       
 20735 					curRect.innerH = size;
       
 20736 					curRect.h = size + deltaHeight;
       
 20737 				}
       
 20738 
       
 20739 				// Set new contentW
       
 20740 				if (newRect.contentW !== undef) {
       
 20741 					curRect.contentW = newRect.contentW;
       
 20742 				}
       
 20743 
       
 20744 				// Set new contentH
       
 20745 				if (newRect.contentH !== undef) {
       
 20746 					curRect.contentH = newRect.contentH;
       
 20747 				}
       
 20748 
       
 20749 				// Compare last layout rect with the current one to see if we need to repaint or not
       
 20750 				lastLayoutRect = self._lastLayoutRect;
       
 20751 				if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
       
 20752 					lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
       
 20753 					repaintControls = Control.repaintControls;
       
 20754 
       
 20755 					if (repaintControls) {
       
 20756 						if (repaintControls.map && !repaintControls.map[self._id]) {
       
 20757 							repaintControls.push(self);
       
 20758 							repaintControls.map[self._id] = true;
       
 20759 						}
       
 20760 					}
       
 20761 
       
 20762 					lastLayoutRect.x = curRect.x;
       
 20763 					lastLayoutRect.y = curRect.y;
       
 20764 					lastLayoutRect.w = curRect.w;
       
 20765 					lastLayoutRect.h = curRect.h;
       
 20766 				}
       
 20767 
       
 20768 				return self;
       
 20769 			}
       
 20770 
       
 20771 			return curRect;
       
 20772 		},
       
 20773 
       
 20774 		/**
       
 20775 		 * Repaints the control after a layout operation.
       
 20776 		 *
       
 20777 		 * @method repaint
       
 20778 		 */
       
 20779 		repaint: function() {
       
 20780 			var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
       
 20781 
       
 20782 			// Use Math.round on all values on IE < 9
       
 20783 			round = !document.createRange ? Math.round : function(value) {
       
 20784 				return value;
       
 20785 			};
       
 20786 
       
 20787 			style = self.getEl().style;
       
 20788 			rect = self._layoutRect;
       
 20789 			lastRepaintRect = self._lastRepaintRect || {};
       
 20790 
       
 20791 			borderBox = self._borderBox;
       
 20792 			borderW = borderBox.left + borderBox.right;
       
 20793 			borderH = borderBox.top + borderBox.bottom;
       
 20794 
       
 20795 			if (rect.x !== lastRepaintRect.x) {
       
 20796 				style.left = round(rect.x) + 'px';
       
 20797 				lastRepaintRect.x = rect.x;
       
 20798 			}
       
 20799 
       
 20800 			if (rect.y !== lastRepaintRect.y) {
       
 20801 				style.top = round(rect.y) + 'px';
       
 20802 				lastRepaintRect.y = rect.y;
       
 20803 			}
       
 20804 
       
 20805 			if (rect.w !== lastRepaintRect.w) {
       
 20806 				style.width = round(rect.w - borderW) + 'px';
       
 20807 				lastRepaintRect.w = rect.w;
       
 20808 			}
       
 20809 
       
 20810 			if (rect.h !== lastRepaintRect.h) {
       
 20811 				style.height = round(rect.h - borderH) + 'px';
       
 20812 				lastRepaintRect.h = rect.h;
       
 20813 			}
       
 20814 
       
 20815 			// Update body if needed
       
 20816 			if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
       
 20817 				bodyStyle = self.getEl('body').style;
       
 20818 				bodyStyle.width = round(rect.innerW) + 'px';
       
 20819 				lastRepaintRect.innerW = rect.innerW;
       
 20820 			}
       
 20821 
       
 20822 			if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
       
 20823 				bodyStyle = bodyStyle || self.getEl('body').style;
       
 20824 				bodyStyle.height = round(rect.innerH) + 'px';
       
 20825 				lastRepaintRect.innerH = rect.innerH;
       
 20826 			}
       
 20827 
       
 20828 			self._lastRepaintRect = lastRepaintRect;
       
 20829 			self.fire('repaint', {}, false);
       
 20830 		},
       
 20831 
       
 20832 		/**
       
 20833 		 * Binds a callback to the specified event. This event can both be
       
 20834 		 * native browser events like "click" or custom ones like PostRender.
       
 20835 		 *
       
 20836 		 * The callback function will be passed a DOM event like object that enables yout do stop propagation.
       
 20837 		 *
       
 20838 		 * @method on
       
 20839 		 * @param {String} name Name of the event to bind. For example "click".
       
 20840 		 * @param {String/function} callback Callback function to execute ones the event occurs.
       
 20841 		 * @return {tinymce.ui.Control} Current control object.
       
 20842 		 */
       
 20843 		on: function(name, callback) {
       
 20844 			var self = this;
       
 20845 
       
 20846 			function resolveCallbackName(name) {
       
 20847 				var callback, scope;
       
 20848 
       
 20849 				if (typeof name != 'string') {
       
 20850 					return name;
       
 20851 				}
       
 20852 
       
 20853 				return function(e) {
       
 20854 					if (!callback) {
       
 20855 						self.parentsAndSelf().each(function(ctrl) {
       
 20856 							var callbacks = ctrl.settings.callbacks;
       
 20857 
       
 20858 							if (callbacks && (callback = callbacks[name])) {
       
 20859 								scope = ctrl;
       
 20860 								return false;
       
 20861 							}
       
 20862 						});
       
 20863 					}
       
 20864 
       
 20865 					return callback.call(scope, e);
       
 20866 				};
       
 20867 			}
       
 20868 
       
 20869 			getEventDispatcher(self).on(name, resolveCallbackName(callback));
       
 20870 
       
 20871 			return self;
       
 20872 		},
       
 20873 
       
 20874 		/**
       
 20875 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
       
 20876 		 * parameter all event handlers will be removed. If you omit the callback all event handles
       
 20877 		 * by the specified name will be removed.
       
 20878 		 *
       
 20879 		 * @method off
       
 20880 		 * @param {String} [name] Name for the event to unbind.
       
 20881 		 * @param {function} [callback] Callback function to unbind.
       
 20882 		 * @return {mxex.ui.Control} Current control object.
       
 20883 		 */
       
 20884 		off: function(name, callback) {
       
 20885 			getEventDispatcher(this).off(name, callback);
       
 20886 			return this;
       
 20887 		},
       
 20888 
       
 20889 		/**
       
 20890 		 * Fires the specified event by name and arguments on the control. This will execute all
       
 20891 		 * bound event handlers.
       
 20892 		 *
       
 20893 		 * @method fire
       
 20894 		 * @param {String} name Name of the event to fire.
       
 20895 		 * @param {Object} [args] Arguments to pass to the event.
       
 20896 		 * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
       
 20897 		 * @return {Object} Current arguments object.
       
 20898 		 */
       
 20899 		fire: function(name, args, bubble) {
       
 20900 			var self = this;
       
 20901 
       
 20902 			args = args || {};
       
 20903 
       
 20904 			if (!args.control) {
       
 20905 				args.control = self;
       
 20906 			}
       
 20907 
       
 20908 			args = getEventDispatcher(self).fire(name, args);
       
 20909 
       
 20910 			// Bubble event up to parents
       
 20911 			if (bubble !== false && self.parent) {
       
 20912 				var parent = self.parent();
       
 20913 				while (parent && !args.isPropagationStopped()) {
       
 20914 					parent.fire(name, args, false);
       
 20915 					parent = parent.parent();
       
 20916 				}
       
 20917 			}
       
 20918 
       
 20919 			return args;
       
 20920 		},
       
 20921 
       
 20922 		/**
       
 20923 		 * Returns true/false if the specified event has any listeners.
       
 20924 		 *
       
 20925 		 * @method hasEventListeners
       
 20926 		 * @param {String} name Name of the event to check for.
       
 20927 		 * @return {Boolean} True/false state if the event has listeners.
       
 20928 		 */
       
 20929 		hasEventListeners: function(name) {
       
 20930 			return getEventDispatcher(this).has(name);
       
 20931 		},
       
 20932 
       
 20933 		/**
       
 20934 		 * Returns a control collection with all parent controls.
       
 20935 		 *
       
 20936 		 * @method parents
       
 20937 		 * @param {String} selector Optional selector expression to find parents.
       
 20938 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
       
 20939 		 */
       
 20940 		parents: function(selector) {
       
 20941 			var self = this, ctrl, parents = new Collection();
       
 20942 
       
 20943 			// Add each parent to collection
       
 20944 			for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) {
       
 20945 				parents.add(ctrl);
       
 20946 			}
       
 20947 
       
 20948 			// Filter away everything that doesn't match the selector
       
 20949 			if (selector) {
       
 20950 				parents = parents.filter(selector);
       
 20951 			}
       
 20952 
       
 20953 			return parents;
       
 20954 		},
       
 20955 
       
 20956 		/**
       
 20957 		 * Returns the current control and it's parents.
       
 20958 		 *
       
 20959 		 * @method parentsAndSelf
       
 20960 		 * @param {String} selector Optional selector expression to find parents.
       
 20961 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
       
 20962 		 */
       
 20963 		parentsAndSelf: function(selector) {
       
 20964 			return new Collection(this).add(this.parents(selector));
       
 20965 		},
       
 20966 
       
 20967 		/**
       
 20968 		 * Returns the control next to the current control.
       
 20969 		 *
       
 20970 		 * @method next
       
 20971 		 * @return {tinymce.ui.Control} Next control instance.
       
 20972 		 */
       
 20973 		next: function() {
       
 20974 			var parentControls = this.parent().items();
       
 20975 
       
 20976 			return parentControls[parentControls.indexOf(this) + 1];
       
 20977 		},
       
 20978 
       
 20979 		/**
       
 20980 		 * Returns the control previous to the current control.
       
 20981 		 *
       
 20982 		 * @method prev
       
 20983 		 * @return {tinymce.ui.Control} Previous control instance.
       
 20984 		 */
       
 20985 		prev: function() {
       
 20986 			var parentControls = this.parent().items();
       
 20987 
       
 20988 			return parentControls[parentControls.indexOf(this) - 1];
       
 20989 		},
       
 20990 
       
 20991 		/**
       
 20992 		 * Find the common ancestor for two control instances.
       
 20993 		 *
       
 20994 		 * @method findCommonAncestor
       
 20995 		 * @param {tinymce.ui.Control} ctrl1 First control.
       
 20996 		 * @param {tinymce.ui.Control} ctrl2 Second control.
       
 20997 		 * @return {tinymce.ui.Control} Ancestor control instance.
       
 20998 		 */
       
 20999 		findCommonAncestor: function(ctrl1, ctrl2) {
       
 21000 			var parentCtrl;
       
 21001 
       
 21002 			while (ctrl1) {
       
 21003 				parentCtrl = ctrl2;
       
 21004 
       
 21005 				while (parentCtrl && ctrl1 != parentCtrl) {
       
 21006 					parentCtrl = parentCtrl.parent();
       
 21007 				}
       
 21008 
       
 21009 				if (ctrl1 == parentCtrl) {
       
 21010 					break;
       
 21011 				}
       
 21012 
       
 21013 				ctrl1 = ctrl1.parent();
       
 21014 			}
       
 21015 
       
 21016 			return ctrl1;
       
 21017 		},
       
 21018 
       
 21019 		/**
       
 21020 		 * Returns true/false if the specific control has the specific class.
       
 21021 		 *
       
 21022 		 * @method hasClass
       
 21023 		 * @param {String} cls Class to check for.
       
 21024 		 * @param {String} [group] Sub element group name.
       
 21025 		 * @return {Boolean} True/false if the control has the specified class.
       
 21026 		 */
       
 21027 		hasClass: function(cls, group) {
       
 21028 			var classes = this._classes[group || 'control'];
       
 21029 
       
 21030 			cls = this.classPrefix + cls;
       
 21031 
       
 21032 			return classes && !!classes.map[cls];
       
 21033 		},
       
 21034 
       
 21035 		/**
       
 21036 		 * Adds the specified class to the control
       
 21037 		 *
       
 21038 		 * @method addClass
       
 21039 		 * @param {String} cls Class to check for.
       
 21040 		 * @param {String} [group] Sub element group name.
       
 21041 		 * @return {tinymce.ui.Control} Current control object.
       
 21042 		 */
       
 21043 		addClass: function(cls, group) {
       
 21044 			var self = this, classes, elm;
       
 21045 
       
 21046 			cls = this.classPrefix + cls;
       
 21047 			classes = self._classes[group || 'control'];
       
 21048 
       
 21049 			if (!classes) {
       
 21050 				classes = [];
       
 21051 				classes.map = {};
       
 21052 				self._classes[group || 'control'] = classes;
       
 21053 			}
       
 21054 
       
 21055 			if (!classes.map[cls]) {
       
 21056 				classes.map[cls] = cls;
       
 21057 				classes.push(cls);
       
 21058 
       
 21059 				if (self._rendered) {
       
 21060 					elm = self.getEl(group);
       
 21061 
       
 21062 					if (elm) {
       
 21063 						elm.className = classes.join(' ');
       
 21064 					}
       
 21065 				}
       
 21066 			}
       
 21067 
       
 21068 			return self;
       
 21069 		},
       
 21070 
       
 21071 		/**
       
 21072 		 * Removes the specified class from the control.
       
 21073 		 *
       
 21074 		 * @method removeClass
       
 21075 		 * @param {String} cls Class to remove.
       
 21076 		 * @param {String} [group] Sub element group name.
       
 21077 		 * @return {tinymce.ui.Control} Current control object.
       
 21078 		 */
       
 21079 		removeClass: function(cls, group) {
       
 21080 			var self = this, classes, i, elm;
       
 21081 
       
 21082 			cls = this.classPrefix + cls;
       
 21083 			classes = self._classes[group || 'control'];
       
 21084 			if (classes && classes.map[cls]) {
       
 21085 				delete classes.map[cls];
       
 21086 
       
 21087 				i = classes.length;
       
 21088 				while (i--) {
       
 21089 					if (classes[i] === cls) {
       
 21090 						classes.splice(i, 1);
       
 21091 					}
       
 21092 				}
       
 21093 			}
       
 21094 
       
 21095 			if (self._rendered) {
       
 21096 				elm = self.getEl(group);
       
 21097 
       
 21098 				if (elm) {
       
 21099 					elm.className = classes.join(' ');
       
 21100 				}
       
 21101 			}
       
 21102 
       
 21103 			return self;
       
 21104 		},
       
 21105 
       
 21106 		/**
       
 21107 		 * Toggles the specified class on the control.
       
 21108 		 *
       
 21109 		 * @method toggleClass
       
 21110 		 * @param {String} cls Class to remove.
       
 21111 		 * @param {Boolean} state True/false state to add/remove class.
       
 21112 		 * @param {String} [group] Sub element group name.
       
 21113 		 * @return {tinymce.ui.Control} Current control object.
       
 21114 		 */
       
 21115 		toggleClass: function(cls, state, group) {
       
 21116 			var self = this;
       
 21117 
       
 21118 			if (state) {
       
 21119 				self.addClass(cls, group);
       
 21120 			} else {
       
 21121 				self.removeClass(cls, group);
       
 21122 			}
       
 21123 
       
 21124 			return self;
       
 21125 		},
       
 21126 
       
 21127 		/**
       
 21128 		 * Returns the class string for the specified group name.
       
 21129 		 *
       
 21130 		 * @method classes
       
 21131 		 * @param {String} [group] Group to get clases by.
       
 21132 		 * @return {String} Classes for the specified group.
       
 21133 		 */
       
 21134 		classes: function(group) {
       
 21135 			var classes = this._classes[group || 'control'];
       
 21136 
       
 21137 			return classes ? classes.join(' ') : '';
       
 21138 		},
       
 21139 
       
 21140 		/**
       
 21141 		 * Sets the inner HTML of the control element.
       
 21142 		 *
       
 21143 		 * @method innerHtml
       
 21144 		 * @param {String} html Html string to set as inner html.
       
 21145 		 * @return {tinymce.ui.Control} Current control object.
       
 21146 		 */
       
 21147 		innerHtml: function(html) {
       
 21148 			DomUtils.innerHtml(this.getEl(), html);
       
 21149 			return this;
       
 21150 		},
       
 21151 
       
 21152 		/**
       
 21153 		 * Returns the control DOM element or sub element.
       
 21154 		 *
       
 21155 		 * @method getEl
       
 21156 		 * @param {String} [suffix] Suffix to get element by.
       
 21157 		 * @return {Element} HTML DOM element for the current control or it's children.
       
 21158 		 */
       
 21159 		getEl: function(suffix) {
       
 21160 			var id = suffix ? this._id + '-' + suffix : this._id;
       
 21161 
       
 21162 			if (!this._elmCache[id]) {
       
 21163 				this._elmCache[id] = DomUtils.get(id);
       
 21164 			}
       
 21165 
       
 21166 			return this._elmCache[id];
       
 21167 		},
       
 21168 
       
 21169 		/**
       
 21170 		 * Sets/gets the visible for the control.
       
 21171 		 *
       
 21172 		 * @method visible
       
 21173 		 * @param {Boolean} state Value to set to control.
       
 21174 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
       
 21175 		 */
       
 21176 		visible: function(state) {
       
 21177 			var self = this, parentCtrl;
       
 21178 
       
 21179 			if (typeof state !== "undefined") {
       
 21180 				if (self._visible !== state) {
       
 21181 					if (self._rendered) {
       
 21182 						self.getEl().style.display = state ? '' : 'none';
       
 21183 					}
       
 21184 
       
 21185 					self._visible = state;
       
 21186 
       
 21187 					// Parent container needs to reflow
       
 21188 					parentCtrl = self.parent();
       
 21189 					if (parentCtrl) {
       
 21190 						parentCtrl._lastRect = null;
       
 21191 					}
       
 21192 
       
 21193 					self.fire(state ? 'show' : 'hide');
       
 21194 				}
       
 21195 
       
 21196 				return self;
       
 21197 			}
       
 21198 
       
 21199 			return self._visible;
       
 21200 		},
       
 21201 
       
 21202 		/**
       
 21203 		 * Sets the visible state to true.
       
 21204 		 *
       
 21205 		 * @method show
       
 21206 		 * @return {tinymce.ui.Control} Current control instance.
       
 21207 		 */
       
 21208 		show: function() {
       
 21209 			return this.visible(true);
       
 21210 		},
       
 21211 
       
 21212 		/**
       
 21213 		 * Sets the visible state to false.
       
 21214 		 *
       
 21215 		 * @method hide
       
 21216 		 * @return {tinymce.ui.Control} Current control instance.
       
 21217 		 */
       
 21218 		hide: function() {
       
 21219 			return this.visible(false);
       
 21220 		},
       
 21221 
       
 21222 		/**
       
 21223 		 * Focuses the current control.
       
 21224 		 *
       
 21225 		 * @method focus
       
 21226 		 * @return {tinymce.ui.Control} Current control instance.
       
 21227 		 */
       
 21228 		focus: function() {
       
 21229 			try {
       
 21230 				this.getEl().focus();
       
 21231 			} catch (ex) {
       
 21232 				// Ignore IE error
       
 21233 			}
       
 21234 
       
 21235 			return this;
       
 21236 		},
       
 21237 
       
 21238 		/**
       
 21239 		 * Blurs the current control.
       
 21240 		 *
       
 21241 		 * @method blur
       
 21242 		 * @return {tinymce.ui.Control} Current control instance.
       
 21243 		 */
       
 21244 		blur: function() {
       
 21245 			this.getEl().blur();
       
 21246 
       
 21247 			return this;
       
 21248 		},
       
 21249 
       
 21250 		/**
       
 21251 		 * Sets the specified aria property.
       
 21252 		 *
       
 21253 		 * @method aria
       
 21254 		 * @param {String} name Name of the aria property to set.
       
 21255 		 * @param {String} value Value of the aria property.
       
 21256 		 * @return {tinymce.ui.Control} Current control instance.
       
 21257 		 */
       
 21258 		aria: function(name, value) {
       
 21259 			var self = this, elm = self.getEl(self.ariaTarget);
       
 21260 
       
 21261 			if (typeof value === "undefined") {
       
 21262 				return self._aria[name];
       
 21263 			} else {
       
 21264 				self._aria[name] = value;
       
 21265 			}
       
 21266 
       
 21267 			if (self._rendered) {
       
 21268 				elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
       
 21269 			}
       
 21270 
       
 21271 			return self;
       
 21272 		},
       
 21273 
       
 21274 		/**
       
 21275 		 * Encodes the specified string with HTML entities. It will also
       
 21276 		 * translate the string to different languages.
       
 21277 		 *
       
 21278 		 * @method encode
       
 21279 		 * @param {String/Object/Array} text Text to entity encode.
       
 21280 		 * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
       
 21281 		 * @return {String} Encoded and possible traslated string.
       
 21282 		 */
       
 21283 		encode: function(text, translate) {
       
 21284 			if (translate !== false) {
       
 21285 				text = this.translate(text);
       
 21286 			}
       
 21287 
       
 21288 			return (text || '').replace(/[&<>"]/g, function(match) {
       
 21289 				return '&#' + match.charCodeAt(0) + ';';
       
 21290 			});
       
 21291 		},
       
 21292 
       
 21293 		/**
       
 21294 		 * Returns the translated string.
       
 21295 		 *
       
 21296 		 * @method translate
       
 21297 		 * @param {String} text Text to translate.
       
 21298 		 * @return {String} Translated string or the same as the input.
       
 21299 		 */
       
 21300 		translate: function(text) {
       
 21301 			return Control.translate ? Control.translate(text) : text;
       
 21302 		},
       
 21303 
       
 21304 		/**
       
 21305 		 * Adds items before the current control.
       
 21306 		 *
       
 21307 		 * @method before
       
 21308 		 * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
       
 21309 		 * @return {tinymce.ui.Control} Current control instance.
       
 21310 		 */
       
 21311 		before: function(items) {
       
 21312 			var self = this, parent = self.parent();
       
 21313 
       
 21314 			if (parent) {
       
 21315 				parent.insert(items, parent.items().indexOf(self), true);
       
 21316 			}
       
 21317 
       
 21318 			return self;
       
 21319 		},
       
 21320 
       
 21321 		/**
       
 21322 		 * Adds items after the current control.
       
 21323 		 *
       
 21324 		 * @method after
       
 21325 		 * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
       
 21326 		 * @return {tinymce.ui.Control} Current control instance.
       
 21327 		 */
       
 21328 		after: function(items) {
       
 21329 			var self = this, parent = self.parent();
       
 21330 
       
 21331 			if (parent) {
       
 21332 				parent.insert(items, parent.items().indexOf(self));
       
 21333 			}
       
 21334 
       
 21335 			return self;
       
 21336 		},
       
 21337 
       
 21338 		/**
       
 21339 		 * Removes the current control from DOM and from UI collections.
       
 21340 		 *
       
 21341 		 * @method remove
       
 21342 		 * @return {tinymce.ui.Control} Current control instance.
       
 21343 		 */
       
 21344 		remove: function() {
       
 21345 			var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
       
 21346 
       
 21347 			if (self.items) {
       
 21348 				var controls = self.items().toArray();
       
 21349 				i = controls.length;
       
 21350 				while (i--) {
       
 21351 					controls[i].remove();
       
 21352 				}
       
 21353 			}
       
 21354 
       
 21355 			if (parent && parent.items) {
       
 21356 				newItems = [];
       
 21357 
       
 21358 				parent.items().each(function(item) {
       
 21359 					if (item !== self) {
       
 21360 						newItems.push(item);
       
 21361 					}
       
 21362 				});
       
 21363 
       
 21364 				parent.items().set(newItems);
       
 21365 				parent._lastRect = null;
       
 21366 			}
       
 21367 
       
 21368 			if (self._eventsRoot && self._eventsRoot == self) {
       
 21369 				DomUtils.off(elm);
       
 21370 			}
       
 21371 
       
 21372 			var lookup = self.getRoot().controlIdLookup;
       
 21373 			if (lookup) {
       
 21374 				delete lookup[self._id];
       
 21375 			}
       
 21376 
       
 21377 			if (elm && elm.parentNode) {
       
 21378 				elm.parentNode.removeChild(elm);
       
 21379 			}
       
 21380 
       
 21381 			self._rendered = false;
       
 21382 
       
 21383 			return self;
       
 21384 		},
       
 21385 
       
 21386 		/**
       
 21387 		 * Renders the control before the specified element.
       
 21388 		 *
       
 21389 		 * @method renderBefore
       
 21390 		 * @param {Element} elm Element to render before.
       
 21391 		 * @return {tinymce.ui.Control} Current control instance.
       
 21392 		 */
       
 21393 		renderBefore: function(elm) {
       
 21394 			var self = this;
       
 21395 
       
 21396 			elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
       
 21397 			self.postRender();
       
 21398 
       
 21399 			return self;
       
 21400 		},
       
 21401 
       
 21402 		/**
       
 21403 		 * Renders the control to the specified element.
       
 21404 		 *
       
 21405 		 * @method renderBefore
       
 21406 		 * @param {Element} elm Element to render to.
       
 21407 		 * @return {tinymce.ui.Control} Current control instance.
       
 21408 		 */
       
 21409 		renderTo: function(elm) {
       
 21410 			var self = this;
       
 21411 
       
 21412 			elm = elm || self.getContainerElm();
       
 21413 			elm.appendChild(DomUtils.createFragment(self.renderHtml()));
       
 21414 			self.postRender();
       
 21415 
       
 21416 			return self;
       
 21417 		},
       
 21418 
       
 21419 		/**
       
 21420 		 * Post render method. Called after the control has been rendered to the target.
       
 21421 		 *
       
 21422 		 * @method postRender
       
 21423 		 * @return {tinymce.ui.Control} Current control instance.
       
 21424 		 */
       
 21425 		postRender: function() {
       
 21426 			var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
       
 21427 
       
 21428 			// Bind on<event> settings
       
 21429 			for (name in settings) {
       
 21430 				if (name.indexOf("on") === 0) {
       
 21431 					self.on(name.substr(2), settings[name]);
       
 21432 				}
       
 21433 			}
       
 21434 
       
 21435 			if (self._eventsRoot) {
       
 21436 				for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
       
 21437 					parentEventsRoot = parent._eventsRoot;
       
 21438 				}
       
 21439 
       
 21440 				if (parentEventsRoot) {
       
 21441 					for (name in parentEventsRoot._nativeEvents) {
       
 21442 						self._nativeEvents[name] = true;
       
 21443 					}
       
 21444 				}
       
 21445 			}
       
 21446 
       
 21447 			self.bindPendingEvents();
       
 21448 
       
 21449 			if (settings.style) {
       
 21450 				elm = self.getEl();
       
 21451 				if (elm) {
       
 21452 					elm.setAttribute('style', settings.style);
       
 21453 					elm.style.cssText = settings.style;
       
 21454 				}
       
 21455 			}
       
 21456 
       
 21457 			if (!self._visible) {
       
 21458 				DomUtils.css(self.getEl(), 'display', 'none');
       
 21459 			}
       
 21460 
       
 21461 			if (self.settings.border) {
       
 21462 				box = self.borderBox();
       
 21463 				DomUtils.css(self.getEl(), {
       
 21464 					'border-top-width': box.top,
       
 21465 					'border-right-width': box.right,
       
 21466 					'border-bottom-width': box.bottom,
       
 21467 					'border-left-width': box.left
       
 21468 				});
       
 21469 			}
       
 21470 
       
 21471 			// Add instance to lookup
       
 21472 			var root = self.getRoot();
       
 21473 			if (!root.controlIdLookup) {
       
 21474 				root.controlIdLookup = {};
       
 21475 			}
       
 21476 
       
 21477 			root.controlIdLookup[self._id] = self;
       
 21478 
       
 21479 			for (var key in self._aria) {
       
 21480 				self.aria(key, self._aria[key]);
       
 21481 			}
       
 21482 
       
 21483 			self.fire('postrender', {}, false);
       
 21484 		},
       
 21485 
       
 21486 		/**
       
 21487 		 * Scrolls the current control into view.
       
 21488 		 *
       
 21489 		 * @method scrollIntoView
       
 21490 		 * @param {String} align Alignment in view top|center|bottom.
       
 21491 		 * @return {tinymce.ui.Control} Current control instance.
       
 21492 		 */
       
 21493 		scrollIntoView: function(align) {
       
 21494 			function getOffset(elm, rootElm) {
       
 21495 				var x, y, parent = elm;
       
 21496 
       
 21497 				x = y = 0;
       
 21498 				while (parent && parent != rootElm && parent.nodeType) {
       
 21499 					x += parent.offsetLeft || 0;
       
 21500 					y += parent.offsetTop || 0;
       
 21501 					parent = parent.offsetParent;
       
 21502 				}
       
 21503 
       
 21504 				return {x: x, y: y};
       
 21505 			}
       
 21506 
       
 21507 			var elm = this.getEl(), parentElm = elm.parentNode;
       
 21508 			var x, y, width, height, parentWidth, parentHeight;
       
 21509 			var pos = getOffset(elm, parentElm);
       
 21510 
       
 21511 			x = pos.x;
       
 21512 			y = pos.y;
       
 21513 			width = elm.offsetWidth;
       
 21514 			height = elm.offsetHeight;
       
 21515 			parentWidth = parentElm.clientWidth;
       
 21516 			parentHeight = parentElm.clientHeight;
       
 21517 
       
 21518 			if (align == "end") {
       
 21519 				x -= parentWidth - width;
       
 21520 				y -= parentHeight - height;
       
 21521 			} else if (align == "center") {
       
 21522 				x -= (parentWidth / 2) - (width / 2);
       
 21523 				y -= (parentHeight / 2) - (height / 2);
       
 21524 			}
       
 21525 
       
 21526 			parentElm.scrollLeft = x;
       
 21527 			parentElm.scrollTop = y;
       
 21528 
       
 21529 			return this;
       
 21530 		},
       
 21531 
       
 21532 		/**
       
 21533 		 * Binds pending DOM events.
       
 21534 		 *
       
 21535 		 * @private
       
 21536 		 */
       
 21537 		bindPendingEvents: function() {
       
 21538 			var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
       
 21539 
       
 21540 			function delegate(e) {
       
 21541 				var control = self.getParentCtrl(e.target);
       
 21542 
       
 21543 				if (control) {
       
 21544 					control.fire(e.type, e);
       
 21545 				}
       
 21546 			}
       
 21547 
       
 21548 			function mouseLeaveHandler() {
       
 21549 				var ctrl = eventRootCtrl._lastHoverCtrl;
       
 21550 
       
 21551 				if (ctrl) {
       
 21552 					ctrl.fire("mouseleave", {target: ctrl.getEl()});
       
 21553 
       
 21554 					ctrl.parents().each(function(ctrl) {
       
 21555 						ctrl.fire("mouseleave", {target: ctrl.getEl()});
       
 21556 					});
       
 21557 
       
 21558 					eventRootCtrl._lastHoverCtrl = null;
       
 21559 				}
       
 21560 			}
       
 21561 
       
 21562 			function mouseEnterHandler(e) {
       
 21563 				var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
       
 21564 
       
 21565 				// Over on a new control
       
 21566 				if (ctrl !== lastCtrl) {
       
 21567 					eventRootCtrl._lastHoverCtrl = ctrl;
       
 21568 
       
 21569 					parents = ctrl.parents().toArray().reverse();
       
 21570 					parents.push(ctrl);
       
 21571 
       
 21572 					if (lastCtrl) {
       
 21573 						lastParents = lastCtrl.parents().toArray().reverse();
       
 21574 						lastParents.push(lastCtrl);
       
 21575 
       
 21576 						for (idx = 0; idx < lastParents.length; idx++) {
       
 21577 							if (parents[idx] !== lastParents[idx]) {
       
 21578 								break;
       
 21579 							}
       
 21580 						}
       
 21581 
       
 21582 						for (i = lastParents.length - 1; i >= idx; i--) {
       
 21583 							lastCtrl = lastParents[i];
       
 21584 							lastCtrl.fire("mouseleave", {
       
 21585 								target: lastCtrl.getEl()
       
 21586 							});
       
 21587 						}
       
 21588 					}
       
 21589 
       
 21590 					for (i = idx; i < parents.length; i++) {
       
 21591 						ctrl = parents[i];
       
 21592 						ctrl.fire("mouseenter", {
       
 21593 							target: ctrl.getEl()
       
 21594 						});
       
 21595 					}
       
 21596 				}
       
 21597 			}
       
 21598 
       
 21599 			function fixWheelEvent(e) {
       
 21600 				e.preventDefault();
       
 21601 
       
 21602 				if (e.type == "mousewheel") {
       
 21603 					e.deltaY = -1 / 40 * e.wheelDelta;
       
 21604 
       
 21605 					if (e.wheelDeltaX) {
       
 21606 						e.deltaX = -1 / 40 * e.wheelDeltaX;
       
 21607 					}
       
 21608 				} else {
       
 21609 					e.deltaX = 0;
       
 21610 					e.deltaY = e.detail;
       
 21611 				}
       
 21612 
       
 21613 				e = self.fire("wheel", e);
       
 21614 			}
       
 21615 
       
 21616 			self._rendered = true;
       
 21617 
       
 21618 			nativeEvents = self._nativeEvents;
       
 21619 			if (nativeEvents) {
       
 21620 				// Find event root element if it exists
       
 21621 				parents = self.parents().toArray();
       
 21622 				parents.unshift(self);
       
 21623 				for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
       
 21624 					eventRootCtrl = parents[i]._eventsRoot;
       
 21625 				}
       
 21626 
       
 21627 				// Event root wasn't found the use the root control
       
 21628 				if (!eventRootCtrl) {
       
 21629 					eventRootCtrl = parents[parents.length - 1] || self;
       
 21630 				}
       
 21631 
       
 21632 				// Set the eventsRoot property on children that didn't have it
       
 21633 				self._eventsRoot = eventRootCtrl;
       
 21634 				for (l = i, i = 0; i < l; i++) {
       
 21635 					parents[i]._eventsRoot = eventRootCtrl;
       
 21636 				}
       
 21637 
       
 21638 				var eventRootDelegates = eventRootCtrl._delegates;
       
 21639 				if (!eventRootDelegates) {
       
 21640 					eventRootDelegates = eventRootCtrl._delegates = {};
       
 21641 				}
       
 21642 
       
 21643 				// Bind native event delegates
       
 21644 				for (name in nativeEvents) {
       
 21645 					if (!nativeEvents) {
       
 21646 						return false;
       
 21647 					}
       
 21648 
       
 21649 					if (name === "wheel" && !hasWheelEventSupport) {
       
 21650 						if (hasMouseWheelEventSupport) {
       
 21651 							DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
       
 21652 						} else {
       
 21653 							DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
       
 21654 						}
       
 21655 
       
 21656 						continue;
       
 21657 					}
       
 21658 
       
 21659 					// Special treatment for mousenter/mouseleave since these doesn't bubble
       
 21660 					if (name === "mouseenter" || name === "mouseleave") {
       
 21661 						// Fake mousenter/mouseleave
       
 21662 						if (!eventRootCtrl._hasMouseEnter) {
       
 21663 							DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
       
 21664 							DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
       
 21665 							eventRootCtrl._hasMouseEnter = 1;
       
 21666 						}
       
 21667 					} else if (!eventRootDelegates[name]) {
       
 21668 						DomUtils.on(eventRootCtrl.getEl(), name, delegate);
       
 21669 						eventRootDelegates[name] = true;
       
 21670 					}
       
 21671 
       
 21672 					// Remove the event once it's bound
       
 21673 					nativeEvents[name] = false;
       
 21674 				}
       
 21675 			}
       
 21676 		},
       
 21677 
       
 21678 		getRoot: function() {
       
 21679 			var ctrl = this, rootControl, parents = [];
       
 21680 
       
 21681 			while (ctrl) {
       
 21682 				if (ctrl.rootControl) {
       
 21683 					rootControl = ctrl.rootControl;
       
 21684 					break;
       
 21685 				}
       
 21686 
       
 21687 				parents.push(ctrl);
       
 21688 				rootControl = ctrl;
       
 21689 				ctrl = ctrl.parent();
       
 21690 			}
       
 21691 
       
 21692 			if (!rootControl) {
       
 21693 				rootControl = this;
       
 21694 			}
       
 21695 
       
 21696 			var i = parents.length;
       
 21697 			while (i--) {
       
 21698 				parents[i].rootControl = rootControl;
       
 21699 			}
       
 21700 
       
 21701 			return rootControl;
       
 21702 		},
       
 21703 
       
 21704 		/**
       
 21705 		 * Reflows the current control and it's parents.
       
 21706 		 * This should be used after you for example append children to the current control so
       
 21707 		 * that the layout managers know that they need to reposition everything.
       
 21708 		 *
       
 21709 		 * @example
       
 21710 		 * container.append({type: 'button', text: 'My button'}).reflow();
       
 21711 		 *
       
 21712 		 * @method reflow
       
 21713 		 * @return {tinymce.ui.Control} Current control instance.
       
 21714 		 */
       
 21715 		reflow: function() {
       
 21716 			this.repaint();
       
 21717 
       
 21718 			return this;
       
 21719 		}
       
 21720 
       
 21721 		/**
       
 21722 		 * Sets/gets the parent container for the control.
       
 21723 		 *
       
 21724 		 * @method parent
       
 21725 		 * @param {tinymce.ui.Container} parent Optional parent to set.
       
 21726 		 * @return {tinymce.ui.Control} Parent control or the current control on a set action.
       
 21727 		 */
       
 21728 		// parent: function(parent) {} -- Generated
       
 21729 
       
 21730 		/**
       
 21731 		 * Sets/gets the text for the control.
       
 21732 		 *
       
 21733 		 * @method text
       
 21734 		 * @param {String} value Value to set to control.
       
 21735 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 21736 		 */
       
 21737 		// text: function(value) {} -- Generated
       
 21738 
       
 21739 		/**
       
 21740 		 * Sets/gets the width for the control.
       
 21741 		 *
       
 21742 		 * @method width
       
 21743 		 * @param {Number} value Value to set to control.
       
 21744 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 21745 		 */
       
 21746 		// width: function(value) {} -- Generated
       
 21747 
       
 21748 		/**
       
 21749 		 * Sets/gets the height for the control.
       
 21750 		 *
       
 21751 		 * @method height
       
 21752 		 * @param {Number} value Value to set to control.
       
 21753 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 21754 		 */
       
 21755 		// height: function(value) {} -- Generated
       
 21756 
       
 21757 		/**
       
 21758 		 * Sets/gets the disabled state on the control.
       
 21759 		 *
       
 21760 		 * @method disabled
       
 21761 		 * @param {Boolean} state Value to set to control.
       
 21762 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
       
 21763 		 */
       
 21764 		// disabled: function(state) {} -- Generated
       
 21765 
       
 21766 		/**
       
 21767 		 * Sets/gets the active for the control.
       
 21768 		 *
       
 21769 		 * @method active
       
 21770 		 * @param {Boolean} state Value to set to control.
       
 21771 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
       
 21772 		 */
       
 21773 		// active: function(state) {} -- Generated
       
 21774 
       
 21775 		/**
       
 21776 		 * Sets/gets the name for the control.
       
 21777 		 *
       
 21778 		 * @method name
       
 21779 		 * @param {String} value Value to set to control.
       
 21780 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 21781 		 */
       
 21782 		// name: function(value) {} -- Generated
       
 21783 
       
 21784 		/**
       
 21785 		 * Sets/gets the title for the control.
       
 21786 		 *
       
 21787 		 * @method title
       
 21788 		 * @param {String} value Value to set to control.
       
 21789 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
       
 21790 		 */
       
 21791 		// title: function(value) {} -- Generated
       
 21792 	});
       
 21793 
       
 21794 	return Control;
       
 21795 });
       
 21796 
       
 21797 // Included from: js/tinymce/classes/ui/Factory.js
       
 21798 
       
 21799 /**
       
 21800  * Factory.js
       
 21801  *
       
 21802  * Copyright, Moxiecode Systems AB
       
 21803  * Released under LGPL License.
       
 21804  *
       
 21805  * License: http://www.tinymce.com/license
       
 21806  * Contributing: http://www.tinymce.com/contributing
       
 21807  */
       
 21808 
       
 21809 /*global tinymce:true */
       
 21810 
       
 21811 /**
       
 21812  * This class is a factory for control instances. This enables you
       
 21813  * to create instances of controls without having to require the UI controls directly.
       
 21814  *
       
 21815  * It also allow you to override or add new control types.
       
 21816  *
       
 21817  * @class tinymce.ui.Factory
       
 21818  */
       
 21819 define("tinymce/ui/Factory", [], function() {
       
 21820 	"use strict";
       
 21821 
       
 21822 	var types = {}, namespaceInit;
       
 21823 
       
 21824 	return {
       
 21825 		/**
       
 21826 		 * Adds a new control instance type to the factory.
       
 21827 		 *
       
 21828 		 * @method add
       
 21829 		 * @param {String} type Type name for example "button".
       
 21830 		 * @param {function} typeClass Class type function.
       
 21831 		 */
       
 21832 		add: function(type, typeClass) {
       
 21833 			types[type.toLowerCase()] = typeClass;
       
 21834 		},
       
 21835 
       
 21836 		/**
       
 21837 		 * Returns true/false if the specified type exists or not.
       
 21838 		 *
       
 21839 		 * @method has
       
 21840 		 * @param {String} type Type to look for.
       
 21841 		 * @return {Boolean} true/false if the control by name exists.
       
 21842 		 */
       
 21843 		has: function(type) {
       
 21844 			return !!types[type.toLowerCase()];
       
 21845 		},
       
 21846 
       
 21847 		/**
       
 21848 		 * Creates a new control instance based on the settings provided. The instance created will be
       
 21849 		 * based on the specified type property it can also create whole structures of components out of
       
 21850 		 * the specified JSON object.
       
 21851 		 *
       
 21852 		 * @example
       
 21853 		 * tinymce.ui.Factory.create({
       
 21854 		 *     type: 'button',
       
 21855 		 *     text: 'Hello world!'
       
 21856 		 * });
       
 21857 		 *
       
 21858 		 * @method create
       
 21859 		 * @param {Object/String} settings Name/Value object with items used to create the type.
       
 21860 		 * @return {tinymce.ui.Control} Control instance based on the specified type.
       
 21861 		 */
       
 21862 		create: function(type, settings) {
       
 21863 			var ControlType, name, namespace;
       
 21864 
       
 21865 			// Build type lookup
       
 21866 			if (!namespaceInit) {
       
 21867 				namespace = tinymce.ui;
       
 21868 
       
 21869 				for (name in namespace) {
       
 21870 					types[name.toLowerCase()] = namespace[name];
       
 21871 				}
       
 21872 
       
 21873 				namespaceInit = true;
       
 21874 			}
       
 21875 
       
 21876 			// If string is specified then use it as the type
       
 21877 			if (typeof type == 'string') {
       
 21878 				settings = settings || {};
       
 21879 				settings.type = type;
       
 21880 			} else {
       
 21881 				settings = type;
       
 21882 				type = settings.type;
       
 21883 			}
       
 21884 
       
 21885 			// Find control type
       
 21886 			type = type.toLowerCase();
       
 21887 			ControlType = types[type];
       
 21888 
       
 21889 			// #if debug
       
 21890 
       
 21891 			if (!ControlType) {
       
 21892 				throw new Error("Could not find control by type: " + type);
       
 21893 			}
       
 21894 
       
 21895 			// #endif
       
 21896 
       
 21897 			ControlType = new ControlType(settings);
       
 21898 			ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine
       
 21899 
       
 21900 			return ControlType;
       
 21901 		}
       
 21902 	};
       
 21903 });
       
 21904 
       
 21905 // Included from: js/tinymce/classes/ui/KeyboardNavigation.js
       
 21906 
       
 21907 /**
       
 21908  * KeyboardNavigation.js
       
 21909  *
       
 21910  * Copyright, Moxiecode Systems AB
       
 21911  * Released under LGPL License.
       
 21912  *
       
 21913  * License: http://www.tinymce.com/license
       
 21914  * Contributing: http://www.tinymce.com/contributing
       
 21915  */
       
 21916 
       
 21917 /**
       
 21918  * This class handles keyboard navigation of controls and elements.
       
 21919  *
       
 21920  * @class tinymce.ui.KeyboardNavigation
       
 21921  */
       
 21922 define("tinymce/ui/KeyboardNavigation", [
       
 21923 ], function() {
       
 21924 	"use strict";
       
 21925 
       
 21926 	/**
       
 21927 	 * This class handles all keyboard navigation for WAI-ARIA support. Each root container
       
 21928 	 * gets an instance of this class.
       
 21929 	 *
       
 21930 	 * @constructor
       
 21931 	 */
       
 21932 	return function(settings) {
       
 21933 		var root = settings.root, focusedElement, focusedControl;
       
 21934 
       
 21935 		try {
       
 21936 			focusedElement = document.activeElement;
       
 21937 		} catch (ex) {
       
 21938 			// IE sometimes fails to return a proper element
       
 21939 			focusedElement = document.body;
       
 21940 		}
       
 21941 
       
 21942 		focusedControl = root.getParentCtrl(focusedElement);
       
 21943 
       
 21944 		/**
       
 21945 		 * Returns the currently focused elements wai aria role of the currently
       
 21946 		 * focused element or specified element.
       
 21947 		 *
       
 21948 		 * @private
       
 21949 		 * @param {Element} elm Optional element to get role from.
       
 21950 		 * @return {String} Role of specified element.
       
 21951 		 */
       
 21952 		function getRole(elm) {
       
 21953 			elm = elm || focusedElement;
       
 21954 
       
 21955 			return elm && elm.getAttribute('role');
       
 21956 		}
       
 21957 
       
 21958 		/**
       
 21959 		 * Returns the wai role of the parent element of the currently
       
 21960 		 * focused element or specified element.
       
 21961 		 *
       
 21962 		 * @private
       
 21963 		 * @param {Element} elm Optional element to get parent role from.
       
 21964 		 * @return {String} Role of the first parent that has a role.
       
 21965 		 */
       
 21966 		function getParentRole(elm) {
       
 21967 			var role, parent = elm || focusedElement;
       
 21968 
       
 21969 			while ((parent = parent.parentNode)) {
       
 21970 				if ((role = getRole(parent))) {
       
 21971 					return role;
       
 21972 				}
       
 21973 			}
       
 21974 		}
       
 21975 
       
 21976 		/**
       
 21977 		 * Returns a wai aria property by name for example aria-selected.
       
 21978 		 *
       
 21979 		 * @private
       
 21980 		 * @param {String} name Name of the aria property to get for example "disabled".
       
 21981 		 * @return {String} Aria property value.
       
 21982 		 */
       
 21983 		function getAriaProp(name) {
       
 21984 			var elm = focusedElement;
       
 21985 
       
 21986 			if (elm) {
       
 21987 				return elm.getAttribute('aria-' + name);
       
 21988 			}
       
 21989 		}
       
 21990 
       
 21991 		/**
       
 21992 		 * Is the element a text input element or not.
       
 21993 		 *
       
 21994 		 * @private
       
 21995 		 * @param {Element} elm Element to check if it's an text input element or not.
       
 21996 		 * @return {Boolean} True/false if the element is a text element or not.
       
 21997 		 */
       
 21998 		function isTextInputElement(elm) {
       
 21999 			var tagName = elm.tagName.toUpperCase();
       
 22000 
       
 22001 			// Notice: since type can be "email" etc we don't check the type
       
 22002 			// So all input elements gets treated as text input elements
       
 22003 			return tagName == "INPUT" || tagName == "TEXTAREA";
       
 22004 		}
       
 22005 
       
 22006 		/**
       
 22007 		 * Returns true/false if the specified element can be focused or not.
       
 22008 		 *
       
 22009 		 * @private
       
 22010 		 * @param {Element} elm DOM element to check if it can be focused or not.
       
 22011 		 * @return {Boolean} True/false if the element can have focus.
       
 22012 		 */
       
 22013 		function canFocus(elm) {
       
 22014 			if (isTextInputElement(elm) && !elm.hidden) {
       
 22015 				return true;
       
 22016 			}
       
 22017 
       
 22018 			if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(getRole(elm))) {
       
 22019 				return true;
       
 22020 			}
       
 22021 
       
 22022 			return false;
       
 22023 		}
       
 22024 
       
 22025 		/**
       
 22026 		 * Returns an array of focusable visible elements within the specified container element.
       
 22027 		 *
       
 22028 		 * @private
       
 22029 		 * @param {Element} elm DOM element to find focusable elements within.
       
 22030 		 * @return {Array} Array of focusable elements.
       
 22031 		 */
       
 22032 		function getFocusElements(elm) {
       
 22033 			var elements = [];
       
 22034 
       
 22035 			function collect(elm) {
       
 22036 				if (elm.nodeType != 1 || elm.style.display == 'none') {
       
 22037 					return;
       
 22038 				}
       
 22039 
       
 22040 				if (canFocus(elm)) {
       
 22041 					elements.push(elm);
       
 22042 				}
       
 22043 
       
 22044 				for (var i = 0; i < elm.childNodes.length; i++) {
       
 22045 					collect(elm.childNodes[i]);
       
 22046 				}
       
 22047 			}
       
 22048 
       
 22049 			collect(elm || root.getEl());
       
 22050 
       
 22051 			return elements;
       
 22052 		}
       
 22053 
       
 22054 		/**
       
 22055 		 * Returns the navigation root control for the specified control. The navigation root
       
 22056 		 * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group.
       
 22057 		 * It will look for parents of the specified target control or the currently focused control if this option is omitted.
       
 22058 		 *
       
 22059 		 * @private
       
 22060 		 * @param {tinymce.ui.Control} targetControl Optional target control to find root of.
       
 22061 		 * @return {tinymce.ui.Control} Navigation root control.
       
 22062 		 */
       
 22063 		function getNavigationRoot(targetControl) {
       
 22064 			var navigationRoot, controls;
       
 22065 
       
 22066 			targetControl = targetControl || focusedControl;
       
 22067 			controls = targetControl.parents().toArray();
       
 22068 			controls.unshift(targetControl);
       
 22069 
       
 22070 			for (var i = 0; i < controls.length; i++) {
       
 22071 				navigationRoot = controls[i];
       
 22072 
       
 22073 				if (navigationRoot.settings.ariaRoot) {
       
 22074 					break;
       
 22075 				}
       
 22076 			}
       
 22077 
       
 22078 			return navigationRoot;
       
 22079 		}
       
 22080 
       
 22081 		/**
       
 22082 		 * Focuses the first item in the specified targetControl element or the last aria index if the
       
 22083 		 * navigation root has the ariaRemember option enabled.
       
 22084 		 *
       
 22085 		 * @private
       
 22086 		 * @param {tinymce.ui.Control} targetControl Target control to focus the first item in.
       
 22087 		 */
       
 22088 		function focusFirst(targetControl) {
       
 22089 			var navigationRoot = getNavigationRoot(targetControl);
       
 22090 			var focusElements = getFocusElements(navigationRoot.getEl());
       
 22091 
       
 22092 			if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) {
       
 22093 				moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements);
       
 22094 			} else {
       
 22095 				moveFocusToIndex(0, focusElements);
       
 22096 			}
       
 22097 		}
       
 22098 
       
 22099 		/**
       
 22100 		 * Moves the focus to the specified index within the elements list.
       
 22101 		 * This will scope the index to the size of the element list if it changed.
       
 22102 		 *
       
 22103 		 * @private
       
 22104 		 * @param {Number} idx Specified index to move to.
       
 22105 		 * @param {Array} elements Array with dom elements to move focus within.
       
 22106 		 * @return {Number} Input index or a changed index if it was out of range.
       
 22107 		 */
       
 22108 		function moveFocusToIndex(idx, elements) {
       
 22109 			if (idx < 0) {
       
 22110 				idx = elements.length - 1;
       
 22111 			} else if (idx >= elements.length) {
       
 22112 				idx = 0;
       
 22113 			}
       
 22114 
       
 22115 			if (elements[idx]) {
       
 22116 				elements[idx].focus();
       
 22117 			}
       
 22118 
       
 22119 			return idx;
       
 22120 		}
       
 22121 
       
 22122 		/**
       
 22123 		 * Moves the focus forwards or backwards.
       
 22124 		 *
       
 22125 		 * @private
       
 22126 		 * @param {Number} dir Direction to move in positive means forward, negative means backwards.
       
 22127 		 * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements.
       
 22128 		 */
       
 22129 		function moveFocus(dir, elements) {
       
 22130 			var idx = -1, navigationRoot = getNavigationRoot();
       
 22131 
       
 22132 			elements = elements || getFocusElements(navigationRoot.getEl());
       
 22133 
       
 22134 			for (var i = 0; i < elements.length; i++) {
       
 22135 				if (elements[i] === focusedElement) {
       
 22136 					idx = i;
       
 22137 				}
       
 22138 			}
       
 22139 
       
 22140 			idx += dir;
       
 22141 			navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements);
       
 22142 		}
       
 22143 
       
 22144 		/**
       
 22145 		 * Moves the focus to the left this is called by the left key.
       
 22146 		 *
       
 22147 		 * @private
       
 22148 		 */
       
 22149 		function left() {
       
 22150 			var parentRole = getParentRole();
       
 22151 
       
 22152 			if (parentRole == "tablist") {
       
 22153 				moveFocus(-1, getFocusElements(focusedElement.parentNode));
       
 22154 			} else if (focusedControl.parent().submenu) {
       
 22155 				cancel();
       
 22156 			} else {
       
 22157 				moveFocus(-1);
       
 22158 			}
       
 22159 		}
       
 22160 
       
 22161 		/**
       
 22162 		 * Moves the focus to the right this is called by the right key.
       
 22163 		 *
       
 22164 		 * @private
       
 22165 		 */
       
 22166 		function right() {
       
 22167 			var role = getRole(), parentRole = getParentRole();
       
 22168 
       
 22169 			if (parentRole == "tablist") {
       
 22170 				moveFocus(1, getFocusElements(focusedElement.parentNode));
       
 22171 			} else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) {
       
 22172 				enter();
       
 22173 			} else {
       
 22174 				moveFocus(1);
       
 22175 			}
       
 22176 		}
       
 22177 
       
 22178 		/**
       
 22179 		 * Moves the focus to the up this is called by the up key.
       
 22180 		 *
       
 22181 		 * @private
       
 22182 		 */
       
 22183 		function up() {
       
 22184 			moveFocus(-1);
       
 22185 		}
       
 22186 
       
 22187 		/**
       
 22188 		 * Moves the focus to the up this is called by the down key.
       
 22189 		 *
       
 22190 		 * @private
       
 22191 		 */
       
 22192 		function down() {
       
 22193 			var role = getRole(), parentRole = getParentRole();
       
 22194 
       
 22195 			if (role == "menuitem" && parentRole == "menubar") {
       
 22196 				enter();
       
 22197 			} else if (role == "button" && getAriaProp('haspopup')) {
       
 22198 				enter({key: 'down'});
       
 22199 			} else {
       
 22200 				moveFocus(1);
       
 22201 			}
       
 22202 		}
       
 22203 
       
 22204 		/**
       
 22205 		 * Moves the focus to the next item or previous item depending on shift key.
       
 22206 		 *
       
 22207 		 * @private
       
 22208 		 * @param {DOMEvent} e DOM event object.
       
 22209 		 */
       
 22210 		function tab(e) {
       
 22211 			var parentRole = getParentRole();
       
 22212 
       
 22213 			if (parentRole == "tablist") {
       
 22214 				var elm = getFocusElements(focusedControl.getEl('body'))[0];
       
 22215 
       
 22216 				if (elm) {
       
 22217 					elm.focus();
       
 22218 				}
       
 22219 			} else {
       
 22220 				moveFocus(e.shiftKey ? -1 : 1);
       
 22221 			}
       
 22222 		}
       
 22223 
       
 22224 		/**
       
 22225 		 * Calls the cancel event on the currently focused control. This is normally done using the Esc key.
       
 22226 		 *
       
 22227 		 * @private
       
 22228 		 */
       
 22229 		function cancel() {
       
 22230 			focusedControl.fire('cancel');
       
 22231 		}
       
 22232 
       
 22233 		/**
       
 22234 		 * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys.
       
 22235 		 *
       
 22236 		 * @private
       
 22237 		 * @param {Object} aria Optional aria data to pass along with the enter event.
       
 22238 		 */
       
 22239 		function enter(aria) {
       
 22240 			aria = aria || {};
       
 22241 			focusedControl.fire('click', {target: focusedElement, aria: aria});
       
 22242 		}
       
 22243 
       
 22244 		root.on('keydown', function(e) {
       
 22245 			function handleNonTabOrEscEvent(e, handler) {
       
 22246 				// Ignore non tab keys for text elements
       
 22247 				if (isTextInputElement(focusedElement)) {
       
 22248 					return;
       
 22249 				}
       
 22250 
       
 22251 				if (handler(e) !== false) {
       
 22252 					e.preventDefault();
       
 22253 				}
       
 22254 			}
       
 22255 
       
 22256 			if (e.isDefaultPrevented()) {
       
 22257 				return;
       
 22258 			}
       
 22259 
       
 22260 			switch (e.keyCode) {
       
 22261 				case 37: // DOM_VK_LEFT
       
 22262 					handleNonTabOrEscEvent(e, left);
       
 22263 					break;
       
 22264 
       
 22265 				case 39: // DOM_VK_RIGHT
       
 22266 					handleNonTabOrEscEvent(e, right);
       
 22267 					break;
       
 22268 
       
 22269 				case 38: // DOM_VK_UP
       
 22270 					handleNonTabOrEscEvent(e, up);
       
 22271 					break;
       
 22272 
       
 22273 				case 40: // DOM_VK_DOWN
       
 22274 					handleNonTabOrEscEvent(e, down);
       
 22275 					break;
       
 22276 
       
 22277 				case 27: // DOM_VK_ESCAPE
       
 22278 					cancel();
       
 22279 					break;
       
 22280 
       
 22281 				case 14: // DOM_VK_ENTER
       
 22282 				case 13: // DOM_VK_RETURN
       
 22283 				case 32: // DOM_VK_SPACE
       
 22284 					handleNonTabOrEscEvent(e, enter);
       
 22285 					break;
       
 22286 
       
 22287 				case 9: // DOM_VK_TAB
       
 22288 					if (tab(e) !== false) {
       
 22289 						e.preventDefault();
       
 22290 					}
       
 22291 					break;
       
 22292 			}
       
 22293 		});
       
 22294 
       
 22295 		root.on('focusin', function(e) {
       
 22296 			focusedElement = e.target;
       
 22297 			focusedControl = e.control;
       
 22298 		});
       
 22299 
       
 22300 		return {
       
 22301 			focusFirst: focusFirst
       
 22302 		};
       
 22303 	};
       
 22304 });
       
 22305 
       
 22306 // Included from: js/tinymce/classes/ui/Container.js
       
 22307 
       
 22308 /**
       
 22309  * Container.js
       
 22310  *
       
 22311  * Copyright, Moxiecode Systems AB
       
 22312  * Released under LGPL License.
       
 22313  *
       
 22314  * License: http://www.tinymce.com/license
       
 22315  * Contributing: http://www.tinymce.com/contributing
       
 22316  */
       
 22317 
       
 22318 /**
       
 22319  * Container control. This is extended by all controls that can have
       
 22320  * children such as panels etc. You can also use this class directly as an
       
 22321  * generic container instance. The container doesn't have any specific role or style.
       
 22322  *
       
 22323  * @-x-less Container.less
       
 22324  * @class tinymce.ui.Container
       
 22325  * @extends tinymce.ui.Control
       
 22326  */
       
 22327 define("tinymce/ui/Container", [
       
 22328 	"tinymce/ui/Control",
       
 22329 	"tinymce/ui/Collection",
       
 22330 	"tinymce/ui/Selector",
       
 22331 	"tinymce/ui/Factory",
       
 22332 	"tinymce/ui/KeyboardNavigation",
       
 22333 	"tinymce/util/Tools",
       
 22334 	"tinymce/ui/DomUtils"
       
 22335 ], function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, DomUtils) {
       
 22336 	"use strict";
       
 22337 
       
 22338 	var selectorCache = {};
       
 22339 
       
 22340 	return Control.extend({
       
 22341 		layout: '',
       
 22342 		innerClass: 'container-inner',
       
 22343 
       
 22344 		/**
       
 22345 		 * Constructs a new control instance with the specified settings.
       
 22346 		 *
       
 22347 		 * @constructor
       
 22348 		 * @param {Object} settings Name/value object with settings.
       
 22349 		 * @setting {Array} items Items to add to container in JSON format or control instances.
       
 22350 		 * @setting {String} layout Layout manager by name to use.
       
 22351 		 * @setting {Object} defaults Default settings to apply to all items.
       
 22352 		 */
       
 22353 		init: function(settings) {
       
 22354 			var self = this;
       
 22355 
       
 22356 			self._super(settings);
       
 22357 			settings = self.settings;
       
 22358 			self._fixed = settings.fixed;
       
 22359 			self._items = new Collection();
       
 22360 
       
 22361 			if (self.isRtl()) {
       
 22362 				self.addClass('rtl');
       
 22363 			}
       
 22364 
       
 22365 			self.addClass('container');
       
 22366 			self.addClass('container-body', 'body');
       
 22367 
       
 22368 			if (settings.containerCls) {
       
 22369 				self.addClass(settings.containerCls);
       
 22370 			}
       
 22371 
       
 22372 			self._layout = Factory.create((settings.layout || self.layout) + 'layout');
       
 22373 
       
 22374 			if (self.settings.items) {
       
 22375 				self.add(self.settings.items);
       
 22376 			}
       
 22377 
       
 22378 			// TODO: Fix this!
       
 22379 			self._hasBody = true;
       
 22380 		},
       
 22381 
       
 22382 		/**
       
 22383 		 * Returns a collection of child items that the container currently have.
       
 22384 		 *
       
 22385 		 * @method items
       
 22386 		 * @return {tinymce.ui.Collection} Control collection direct child controls.
       
 22387 		 */
       
 22388 		items: function() {
       
 22389 			return this._items;
       
 22390 		},
       
 22391 
       
 22392 		/**
       
 22393 		 * Find child controls by selector.
       
 22394 		 *
       
 22395 		 * @method find
       
 22396 		 * @param {String} selector Selector CSS pattern to find children by.
       
 22397 		 * @return {tinymce.ui.Collection} Control collection with child controls.
       
 22398 		 */
       
 22399 		find: function(selector) {
       
 22400 			selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector);
       
 22401 
       
 22402 			return selector.find(this);
       
 22403 		},
       
 22404 
       
 22405 		/**
       
 22406 		 * Adds one or many items to the current container. This will create instances of
       
 22407 		 * the object representations if needed.
       
 22408 		 *
       
 22409 		 * @method add
       
 22410 		 * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container.
       
 22411 		 * @return {tinymce.ui.Collection} Current collection control.
       
 22412 		 */
       
 22413 		add: function(items) {
       
 22414 			var self = this;
       
 22415 
       
 22416 			self.items().add(self.create(items)).parent(self);
       
 22417 
       
 22418 			return self;
       
 22419 		},
       
 22420 
       
 22421 		/**
       
 22422 		 * Focuses the current container instance. This will look
       
 22423 		 * for the first control in the container and focus that.
       
 22424 		 *
       
 22425 		 * @method focus
       
 22426 		 * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not.
       
 22427 		 * @return {tinymce.ui.Collection} Current instance.
       
 22428 		 */
       
 22429 		focus: function(keyboard) {
       
 22430 			var self = this, focusCtrl, keyboardNav, items;
       
 22431 
       
 22432 			if (keyboard) {
       
 22433 				keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav;
       
 22434 
       
 22435 				if (keyboardNav) {
       
 22436 					keyboardNav.focusFirst(self);
       
 22437 					return;
       
 22438 				}
       
 22439 			}
       
 22440 
       
 22441 			items = self.find('*');
       
 22442 
       
 22443 			// TODO: Figure out a better way to auto focus alert dialog buttons
       
 22444 			if (self.statusbar) {
       
 22445 				items.add(self.statusbar.items());
       
 22446 			}
       
 22447 
       
 22448 			items.each(function(ctrl) {
       
 22449 				if (ctrl.settings.autofocus) {
       
 22450 					focusCtrl = null;
       
 22451 					return false;
       
 22452 				}
       
 22453 
       
 22454 				if (ctrl.canFocus) {
       
 22455 					focusCtrl = focusCtrl || ctrl;
       
 22456 				}
       
 22457 			});
       
 22458 
       
 22459 			if (focusCtrl) {
       
 22460 				focusCtrl.focus();
       
 22461 			}
       
 22462 
       
 22463 			return self;
       
 22464 		},
       
 22465 
       
 22466 		/**
       
 22467 		 * Replaces the specified child control with a new control.
       
 22468 		 *
       
 22469 		 * @method replace
       
 22470 		 * @param {tinymce.ui.Control} oldItem Old item to be replaced.
       
 22471 		 * @param {tinymce.ui.Control} newItem New item to be inserted.
       
 22472 		 */
       
 22473 		replace: function(oldItem, newItem) {
       
 22474 			var ctrlElm, items = this.items(), i = items.length;
       
 22475 
       
 22476 			// Replace the item in collection
       
 22477 			while (i--) {
       
 22478 				if (items[i] === oldItem) {
       
 22479 					items[i] = newItem;
       
 22480 					break;
       
 22481 				}
       
 22482 			}
       
 22483 
       
 22484 			if (i >= 0) {
       
 22485 				// Remove new item from DOM
       
 22486 				ctrlElm = newItem.getEl();
       
 22487 				if (ctrlElm) {
       
 22488 					ctrlElm.parentNode.removeChild(ctrlElm);
       
 22489 				}
       
 22490 
       
 22491 				// Remove old item from DOM
       
 22492 				ctrlElm = oldItem.getEl();
       
 22493 				if (ctrlElm) {
       
 22494 					ctrlElm.parentNode.removeChild(ctrlElm);
       
 22495 				}
       
 22496 			}
       
 22497 
       
 22498 			// Adopt the item
       
 22499 			newItem.parent(this);
       
 22500 		},
       
 22501 
       
 22502 		/**
       
 22503 		 * Creates the specified items. If any of the items is plain JSON style objects
       
 22504 		 * it will convert these into real tinymce.ui.Control instances.
       
 22505 		 *
       
 22506 		 * @method create
       
 22507 		 * @param {Array} items Array of items to convert into control instances.
       
 22508 		 * @return {Array} Array with control instances.
       
 22509 		 */
       
 22510 		create: function(items) {
       
 22511 			var self = this, settings, ctrlItems = [];
       
 22512 
       
 22513 			// Non array structure, then force it into an array
       
 22514 			if (!Tools.isArray(items)) {
       
 22515 				items = [items];
       
 22516 			}
       
 22517 
       
 22518 			// Add default type to each child control
       
 22519 			Tools.each(items, function(item) {
       
 22520 				if (item) {
       
 22521 					// Construct item if needed
       
 22522 					if (!(item instanceof Control)) {
       
 22523 						// Name only then convert it to an object
       
 22524 						if (typeof item == "string") {
       
 22525 							item = {type: item};
       
 22526 						}
       
 22527 
       
 22528 						// Create control instance based on input settings and default settings
       
 22529 						settings = Tools.extend({}, self.settings.defaults, item);
       
 22530 						item.type = settings.type = settings.type || item.type || self.settings.defaultType ||
       
 22531 							(settings.defaults ? settings.defaults.type : null);
       
 22532 						item = Factory.create(settings);
       
 22533 					}
       
 22534 
       
 22535 					ctrlItems.push(item);
       
 22536 				}
       
 22537 			});
       
 22538 
       
 22539 			return ctrlItems;
       
 22540 		},
       
 22541 
       
 22542 		/**
       
 22543 		 * Renders new control instances.
       
 22544 		 *
       
 22545 		 * @private
       
 22546 		 */
       
 22547 		renderNew: function() {
       
 22548 			var self = this;
       
 22549 
       
 22550 			// Render any new items
       
 22551 			self.items().each(function(ctrl, index) {
       
 22552 				var containerElm, fragment;
       
 22553 
       
 22554 				ctrl.parent(self);
       
 22555 
       
 22556 				if (!ctrl._rendered) {
       
 22557 					containerElm = self.getEl('body');
       
 22558 					fragment = DomUtils.createFragment(ctrl.renderHtml());
       
 22559 
       
 22560 					// Insert or append the item
       
 22561 					if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) {
       
 22562 						containerElm.insertBefore(fragment, containerElm.childNodes[index]);
       
 22563 					} else {
       
 22564 						containerElm.appendChild(fragment);
       
 22565 					}
       
 22566 
       
 22567 					ctrl.postRender();
       
 22568 				}
       
 22569 			});
       
 22570 
       
 22571 			self._layout.applyClasses(self);
       
 22572 			self._lastRect = null;
       
 22573 
       
 22574 			return self;
       
 22575 		},
       
 22576 
       
 22577 		/**
       
 22578 		 * Appends new instances to the current container.
       
 22579 		 *
       
 22580 		 * @method append
       
 22581 		 * @param {Array/tinymce.ui.Collection} items Array if controls to append.
       
 22582 		 * @return {tinymce.ui.Container} Current container instance.
       
 22583 		 */
       
 22584 		append: function(items) {
       
 22585 			return this.add(items).renderNew();
       
 22586 		},
       
 22587 
       
 22588 		/**
       
 22589 		 * Prepends new instances to the current container.
       
 22590 		 *
       
 22591 		 * @method prepend
       
 22592 		 * @param {Array/tinymce.ui.Collection} items Array if controls to prepend.
       
 22593 		 * @return {tinymce.ui.Container} Current container instance.
       
 22594 		 */
       
 22595 		prepend: function(items) {
       
 22596 			var self = this;
       
 22597 
       
 22598 			self.items().set(self.create(items).concat(self.items().toArray()));
       
 22599 
       
 22600 			return self.renderNew();
       
 22601 		},
       
 22602 
       
 22603 		/**
       
 22604 		 * Inserts an control at a specific index.
       
 22605 		 *
       
 22606 		 * @method insert
       
 22607 		 * @param {Array/tinymce.ui.Collection} items Array if controls to insert.
       
 22608 		 * @param {Number} index Index to insert controls at.
       
 22609 		 * @param {Boolean} [before=false] Inserts controls before the index.
       
 22610 		 */
       
 22611 		insert: function(items, index, before) {
       
 22612 			var self = this, curItems, beforeItems, afterItems;
       
 22613 
       
 22614 			items = self.create(items);
       
 22615 			curItems = self.items();
       
 22616 
       
 22617 			if (!before && index < curItems.length - 1) {
       
 22618 				index += 1;
       
 22619 			}
       
 22620 
       
 22621 			if (index >= 0 && index < curItems.length) {
       
 22622 				beforeItems = curItems.slice(0, index).toArray();
       
 22623 				afterItems = curItems.slice(index).toArray();
       
 22624 				curItems.set(beforeItems.concat(items, afterItems));
       
 22625 			}
       
 22626 
       
 22627 			return self.renderNew();
       
 22628 		},
       
 22629 
       
 22630 		/**
       
 22631 		 * Populates the form fields from the specified JSON data object.
       
 22632 		 *
       
 22633 		 * Control items in the form that matches the data will have it's value set.
       
 22634 		 *
       
 22635 		 * @method fromJSON
       
 22636 		 * @param {Object} data JSON data object to set control values by.
       
 22637 		 * @return {tinymce.ui.Container} Current form instance.
       
 22638 		 */
       
 22639 		fromJSON: function(data) {
       
 22640 			var self = this;
       
 22641 
       
 22642 			for (var name in data) {
       
 22643 				self.find('#' + name).value(data[name]);
       
 22644 			}
       
 22645 
       
 22646 			return self;
       
 22647 		},
       
 22648 
       
 22649 		/**
       
 22650 		 * Serializes the form into a JSON object by getting all items
       
 22651 		 * that has a name and a value.
       
 22652 		 *
       
 22653 		 * @method toJSON
       
 22654 		 * @return {Object} JSON object with form data.
       
 22655 		 */
       
 22656 		toJSON: function() {
       
 22657 			var self = this, data = {};
       
 22658 
       
 22659 			self.find('*').each(function(ctrl) {
       
 22660 				var name = ctrl.name(), value = ctrl.value();
       
 22661 
       
 22662 				if (name && typeof value != "undefined") {
       
 22663 					data[name] = value;
       
 22664 				}
       
 22665 			});
       
 22666 
       
 22667 			return data;
       
 22668 		},
       
 22669 
       
 22670 		preRender: function() {
       
 22671 		},
       
 22672 
       
 22673 		/**
       
 22674 		 * Renders the control as a HTML string.
       
 22675 		 *
       
 22676 		 * @method renderHtml
       
 22677 		 * @return {String} HTML representing the control.
       
 22678 		 */
       
 22679 		renderHtml: function() {
       
 22680 			var self = this, layout = self._layout, role = this.settings.role;
       
 22681 
       
 22682 			self.preRender();
       
 22683 			layout.preRender(self);
       
 22684 
       
 22685 			return (
       
 22686 				'<div id="' + self._id + '" class="' + self.classes() + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' +
       
 22687 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 22688 						(self.settings.html || '') + layout.renderHtml(self) +
       
 22689 					'</div>' +
       
 22690 				'</div>'
       
 22691 			);
       
 22692 		},
       
 22693 
       
 22694 		/**
       
 22695 		 * Post render method. Called after the control has been rendered to the target.
       
 22696 		 *
       
 22697 		 * @method postRender
       
 22698 		 * @return {tinymce.ui.Container} Current combobox instance.
       
 22699 		 */
       
 22700 		postRender: function() {
       
 22701 			var self = this, box;
       
 22702 
       
 22703 			self.items().exec('postRender');
       
 22704 			self._super();
       
 22705 
       
 22706 			self._layout.postRender(self);
       
 22707 			self._rendered = true;
       
 22708 
       
 22709 			if (self.settings.style) {
       
 22710 				DomUtils.css(self.getEl(), self.settings.style);
       
 22711 			}
       
 22712 
       
 22713 			if (self.settings.border) {
       
 22714 				box = self.borderBox();
       
 22715 				DomUtils.css(self.getEl(), {
       
 22716 					'border-top-width': box.top,
       
 22717 					'border-right-width': box.right,
       
 22718 					'border-bottom-width': box.bottom,
       
 22719 					'border-left-width': box.left
       
 22720 				});
       
 22721 			}
       
 22722 
       
 22723 			if (!self.parent()) {
       
 22724 				self.keyboardNav = new KeyboardNavigation({
       
 22725 					root: self
       
 22726 				});
       
 22727 			}
       
 22728 
       
 22729 			return self;
       
 22730 		},
       
 22731 
       
 22732 		/**
       
 22733 		 * Initializes the current controls layout rect.
       
 22734 		 * This will be executed by the layout managers to determine the
       
 22735 		 * default minWidth/minHeight etc.
       
 22736 		 *
       
 22737 		 * @method initLayoutRect
       
 22738 		 * @return {Object} Layout rect instance.
       
 22739 		 */
       
 22740 		initLayoutRect: function() {
       
 22741 			var self = this, layoutRect = self._super();
       
 22742 
       
 22743 			// Recalc container size by asking layout manager
       
 22744 			self._layout.recalc(self);
       
 22745 
       
 22746 			return layoutRect;
       
 22747 		},
       
 22748 
       
 22749 		/**
       
 22750 		 * Recalculates the positions of the controls in the current container.
       
 22751 		 * This is invoked by the reflow method and shouldn't be called directly.
       
 22752 		 *
       
 22753 		 * @method recalc
       
 22754 		 */
       
 22755 		recalc: function() {
       
 22756 			var self = this, rect = self._layoutRect, lastRect = self._lastRect;
       
 22757 
       
 22758 			if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
       
 22759 				self._layout.recalc(self);
       
 22760 				rect = self.layoutRect();
       
 22761 				self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h};
       
 22762 				return true;
       
 22763 			}
       
 22764 		},
       
 22765 
       
 22766 		/**
       
 22767 		 * Reflows the current container and it's children and possible parents.
       
 22768 		 * This should be used after you for example append children to the current control so
       
 22769 		 * that the layout managers know that they need to reposition everything.
       
 22770 		 *
       
 22771 		 * @example
       
 22772 		 * container.append({type: 'button', text: 'My button'}).reflow();
       
 22773 		 *
       
 22774 		 * @method reflow
       
 22775 		 * @return {tinymce.ui.Container} Current container instance.
       
 22776 		 */
       
 22777 		reflow: function() {
       
 22778 			var i;
       
 22779 
       
 22780 			if (this.visible()) {
       
 22781 				Control.repaintControls = [];
       
 22782 				Control.repaintControls.map = {};
       
 22783 
       
 22784 				this.recalc();
       
 22785 				i = Control.repaintControls.length;
       
 22786 
       
 22787 				while (i--) {
       
 22788 					Control.repaintControls[i].repaint();
       
 22789 				}
       
 22790 
       
 22791 				// TODO: Fix me!
       
 22792 				if (this.settings.layout !== "flow" && this.settings.layout !== "stack") {
       
 22793 					this.repaint();
       
 22794 				}
       
 22795 
       
 22796 				Control.repaintControls = [];
       
 22797 			}
       
 22798 
       
 22799 			return this;
       
 22800 		}
       
 22801 	});
       
 22802 });
       
 22803 
       
 22804 // Included from: js/tinymce/classes/ui/DragHelper.js
       
 22805 
       
 22806 /**
       
 22807  * DragHelper.js
       
 22808  *
       
 22809  * Copyright, Moxiecode Systems AB
       
 22810  * Released under LGPL License.
       
 22811  *
       
 22812  * License: http://www.tinymce.com/license
       
 22813  * Contributing: http://www.tinymce.com/contributing
       
 22814  */
       
 22815 
       
 22816 /**
       
 22817  * Drag/drop helper class.
       
 22818  *
       
 22819  * @example
       
 22820  * var dragHelper = new tinymce.ui.DragHelper('mydiv', {
       
 22821  *     start: function(evt) {
       
 22822  *     },
       
 22823  *
       
 22824  *     drag: function(evt) {
       
 22825  *     },
       
 22826  *
       
 22827  *     end: function(evt) {
       
 22828  *     }
       
 22829  * });
       
 22830  *
       
 22831  * @class tinymce.ui.DragHelper
       
 22832  */
       
 22833 define("tinymce/ui/DragHelper", [
       
 22834 	"tinymce/ui/DomUtils"
       
 22835 ], function(DomUtils) {
       
 22836 	"use strict";
       
 22837 
       
 22838 	function getDocumentSize() {
       
 22839 		var doc = document, documentElement, body, scrollWidth, clientWidth;
       
 22840 		var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max;
       
 22841 
       
 22842 		documentElement = doc.documentElement;
       
 22843 		body = doc.body;
       
 22844 
       
 22845 		scrollWidth = max(documentElement.scrollWidth, body.scrollWidth);
       
 22846 		clientWidth = max(documentElement.clientWidth, body.clientWidth);
       
 22847 		offsetWidth = max(documentElement.offsetWidth, body.offsetWidth);
       
 22848 
       
 22849 		scrollHeight = max(documentElement.scrollHeight, body.scrollHeight);
       
 22850 		clientHeight = max(documentElement.clientHeight, body.clientHeight);
       
 22851 		offsetHeight = max(documentElement.offsetHeight, body.offsetHeight);
       
 22852 
       
 22853 		return {
       
 22854 			width: scrollWidth < offsetWidth ? clientWidth : scrollWidth,
       
 22855 			height: scrollHeight < offsetHeight ? clientHeight : scrollHeight
       
 22856 		};
       
 22857 	}
       
 22858 
       
 22859 	return function(id, settings) {
       
 22860 		var eventOverlayElm, doc = document, downButton, start, stop, drag, startX, startY;
       
 22861 
       
 22862 		settings = settings || {};
       
 22863 
       
 22864 		function getHandleElm() {
       
 22865 			return doc.getElementById(settings.handle || id);
       
 22866 		}
       
 22867 
       
 22868 		start = function(e) {
       
 22869 			var docSize = getDocumentSize(), handleElm, cursor;
       
 22870 
       
 22871 			e.preventDefault();
       
 22872 			downButton = e.button;
       
 22873 			handleElm = getHandleElm();
       
 22874 			startX = e.screenX;
       
 22875 			startY = e.screenY;
       
 22876 
       
 22877 			// Grab cursor from handle
       
 22878 			if (window.getComputedStyle) {
       
 22879 				cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor");
       
 22880 			} else {
       
 22881 				cursor = handleElm.runtimeStyle.cursor;
       
 22882 			}
       
 22883 
       
 22884 			// Create event overlay and add it to document
       
 22885 			eventOverlayElm = doc.createElement('div');
       
 22886 			DomUtils.css(eventOverlayElm, {
       
 22887 				position: "absolute",
       
 22888 				top: 0, left: 0,
       
 22889 				width: docSize.width,
       
 22890 				height: docSize.height,
       
 22891 				zIndex: 0x7FFFFFFF,
       
 22892 				opacity: 0.0001,
       
 22893 				cursor: cursor
       
 22894 			});
       
 22895 
       
 22896 			doc.body.appendChild(eventOverlayElm);
       
 22897 
       
 22898 			// Bind mouse events
       
 22899 			DomUtils.on(doc, 'mousemove', drag);
       
 22900 			DomUtils.on(doc, 'mouseup', stop);
       
 22901 
       
 22902 			// Begin drag
       
 22903 			settings.start(e);
       
 22904 		};
       
 22905 
       
 22906 		drag = function(e) {
       
 22907 			if (e.button !== downButton) {
       
 22908 				return stop(e);
       
 22909 			}
       
 22910 
       
 22911 			e.deltaX = e.screenX - startX;
       
 22912 			e.deltaY = e.screenY - startY;
       
 22913 
       
 22914 			e.preventDefault();
       
 22915 			settings.drag(e);
       
 22916 		};
       
 22917 
       
 22918 		stop = function(e) {
       
 22919 			DomUtils.off(doc, 'mousemove', drag);
       
 22920 			DomUtils.off(doc, 'mouseup', stop);
       
 22921 
       
 22922 			eventOverlayElm.parentNode.removeChild(eventOverlayElm);
       
 22923 
       
 22924 			if (settings.stop) {
       
 22925 				settings.stop(e);
       
 22926 			}
       
 22927 		};
       
 22928 
       
 22929 		/**
       
 22930 		 * Destroys the drag/drop helper instance.
       
 22931 		 *
       
 22932 		 * @method destroy
       
 22933 		 */
       
 22934 		this.destroy = function() {
       
 22935 			DomUtils.off(getHandleElm());
       
 22936 		};
       
 22937 
       
 22938 		DomUtils.on(getHandleElm(), 'mousedown', start);
       
 22939 	};
       
 22940 });
       
 22941 
       
 22942 // Included from: js/tinymce/classes/ui/Scrollable.js
       
 22943 
       
 22944 /**
       
 22945  * Scrollable.js
       
 22946  *
       
 22947  * Copyright, Moxiecode Systems AB
       
 22948  * Released under LGPL License.
       
 22949  *
       
 22950  * License: http://www.tinymce.com/license
       
 22951  * Contributing: http://www.tinymce.com/contributing
       
 22952  */
       
 22953 
       
 22954 /**
       
 22955  * This mixin makes controls scrollable using custom scrollbars.
       
 22956  *
       
 22957  * @-x-less Scrollable.less
       
 22958  * @mixin tinymce.ui.Scrollable
       
 22959  */
       
 22960 define("tinymce/ui/Scrollable", [
       
 22961 	"tinymce/ui/DomUtils",
       
 22962 	"tinymce/ui/DragHelper"
       
 22963 ], function(DomUtils, DragHelper) {
       
 22964 	"use strict";
       
 22965 
       
 22966 	return {
       
 22967 		init: function() {
       
 22968 			var self = this;
       
 22969 			self.on('repaint', self.renderScroll);
       
 22970 		},
       
 22971 
       
 22972 		renderScroll: function() {
       
 22973 			var self = this, margin = 2;
       
 22974 
       
 22975 			function repaintScroll() {
       
 22976 				var hasScrollH, hasScrollV, bodyElm;
       
 22977 
       
 22978 				function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
       
 22979 					var containerElm, scrollBarElm, scrollThumbElm;
       
 22980 					var containerSize, scrollSize, ratio, rect;
       
 22981 					var posNameLower, sizeNameLower;
       
 22982 
       
 22983 					scrollBarElm = self.getEl('scroll' + axisName);
       
 22984 					if (scrollBarElm) {
       
 22985 						posNameLower = posName.toLowerCase();
       
 22986 						sizeNameLower = sizeName.toLowerCase();
       
 22987 
       
 22988 						if (self.getEl('absend')) {
       
 22989 							DomUtils.css(self.getEl('absend'), posNameLower, self.layoutRect()[contentSizeName] - 1);
       
 22990 						}
       
 22991 
       
 22992 						if (!hasScroll) {
       
 22993 							DomUtils.css(scrollBarElm, 'display', 'none');
       
 22994 							return;
       
 22995 						}
       
 22996 
       
 22997 						DomUtils.css(scrollBarElm, 'display', 'block');
       
 22998 						containerElm = self.getEl('body');
       
 22999 						scrollThumbElm = self.getEl('scroll' + axisName + "t");
       
 23000 						containerSize = containerElm["client" + sizeName] - (margin * 2);
       
 23001 						containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
       
 23002 						scrollSize = containerElm["scroll" + sizeName];
       
 23003 						ratio = containerSize / scrollSize;
       
 23004 
       
 23005 						rect = {};
       
 23006 						rect[posNameLower] = containerElm["offset" + posName] + margin;
       
 23007 						rect[sizeNameLower] = containerSize;
       
 23008 						DomUtils.css(scrollBarElm, rect);
       
 23009 
       
 23010 						rect = {};
       
 23011 						rect[posNameLower] = containerElm["scroll" + posName] * ratio;
       
 23012 						rect[sizeNameLower] = containerSize * ratio;
       
 23013 						DomUtils.css(scrollThumbElm, rect);
       
 23014 					}
       
 23015 				}
       
 23016 
       
 23017 				bodyElm = self.getEl('body');
       
 23018 				hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
       
 23019 				hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
       
 23020 
       
 23021 				repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
       
 23022 				repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
       
 23023 			}
       
 23024 
       
 23025 			function addScroll() {
       
 23026 				function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
       
 23027 					var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
       
 23028 
       
 23029 					self.getEl().appendChild(DomUtils.createFragment(
       
 23030 						'<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
       
 23031 							'<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
       
 23032 						'</div>'
       
 23033 					));
       
 23034 
       
 23035 					self.draghelper = new DragHelper(axisId + 't', {
       
 23036 						start: function() {
       
 23037 							scrollStart = self.getEl('body')["scroll" + posName];
       
 23038 							DomUtils.addClass(DomUtils.get(axisId), prefix + 'active');
       
 23039 						},
       
 23040 
       
 23041 						drag: function(e) {
       
 23042 							var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
       
 23043 
       
 23044 							hasScrollH = layoutRect.contentW > layoutRect.innerW;
       
 23045 							hasScrollV = layoutRect.contentH > layoutRect.innerH;
       
 23046 							containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
       
 23047 							containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
       
 23048 
       
 23049 							ratio = containerSize / self.getEl('body')["scroll" + sizeName];
       
 23050 							self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
       
 23051 						},
       
 23052 
       
 23053 						stop: function() {
       
 23054 							DomUtils.removeClass(DomUtils.get(axisId), prefix + 'active');
       
 23055 						}
       
 23056 					});
       
 23057 /*
       
 23058 					self.on('click', function(e) {
       
 23059 						if (e.target.id == self._id + '-scrollv') {
       
 23060 
       
 23061 						}
       
 23062 					});*/
       
 23063 				}
       
 23064 
       
 23065 				self.addClass('scroll');
       
 23066 
       
 23067 				addScrollAxis("v", "Top", "Height", "Y", "Width");
       
 23068 				addScrollAxis("h", "Left", "Width", "X", "Height");
       
 23069 			}
       
 23070 
       
 23071 			if (self.settings.autoScroll) {
       
 23072 				if (!self._hasScroll) {
       
 23073 					self._hasScroll = true;
       
 23074 					addScroll();
       
 23075 
       
 23076 					self.on('wheel', function(e) {
       
 23077 						var bodyEl = self.getEl('body');
       
 23078 
       
 23079 						bodyEl.scrollLeft += (e.deltaX || 0) * 10;
       
 23080 						bodyEl.scrollTop += e.deltaY * 10;
       
 23081 
       
 23082 						repaintScroll();
       
 23083 					});
       
 23084 
       
 23085 					DomUtils.on(self.getEl('body'), "scroll", repaintScroll);
       
 23086 				}
       
 23087 
       
 23088 				repaintScroll();
       
 23089 			}
       
 23090 		}
       
 23091 	};
       
 23092 });
       
 23093 
       
 23094 // Included from: js/tinymce/classes/ui/Panel.js
       
 23095 
       
 23096 /**
       
 23097  * Panel.js
       
 23098  *
       
 23099  * Copyright, Moxiecode Systems AB
       
 23100  * Released under LGPL License.
       
 23101  *
       
 23102  * License: http://www.tinymce.com/license
       
 23103  * Contributing: http://www.tinymce.com/contributing
       
 23104  */
       
 23105 
       
 23106 /**
       
 23107  * Creates a new panel.
       
 23108  *
       
 23109  * @-x-less Panel.less
       
 23110  * @class tinymce.ui.Panel
       
 23111  * @extends tinymce.ui.Container
       
 23112  * @mixes tinymce.ui.Scrollable
       
 23113  */
       
 23114 define("tinymce/ui/Panel", [
       
 23115 	"tinymce/ui/Container",
       
 23116 	"tinymce/ui/Scrollable"
       
 23117 ], function(Container, Scrollable) {
       
 23118 	"use strict";
       
 23119 
       
 23120 	return Container.extend({
       
 23121 		Defaults: {
       
 23122 			layout: 'fit',
       
 23123 			containerCls: 'panel'
       
 23124 		},
       
 23125 
       
 23126 		Mixins: [Scrollable],
       
 23127 
       
 23128 		/**
       
 23129 		 * Renders the control as a HTML string.
       
 23130 		 *
       
 23131 		 * @method renderHtml
       
 23132 		 * @return {String} HTML representing the control.
       
 23133 		 */
       
 23134 		renderHtml: function() {
       
 23135 			var self = this, layout = self._layout, innerHtml = self.settings.html;
       
 23136 
       
 23137 			self.preRender();
       
 23138 			layout.preRender(self);
       
 23139 
       
 23140 			if (typeof innerHtml == "undefined") {
       
 23141 				innerHtml = (
       
 23142 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 23143 						layout.renderHtml(self) +
       
 23144 					'</div>'
       
 23145 				);
       
 23146 			} else {
       
 23147 				if (typeof innerHtml == 'function') {
       
 23148 					innerHtml = innerHtml.call(self);
       
 23149 				}
       
 23150 
       
 23151 				self._hasBody = false;
       
 23152 			}
       
 23153 
       
 23154 			return (
       
 23155 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1" role="group">' +
       
 23156 					(self._preBodyHtml || '') +
       
 23157 					innerHtml +
       
 23158 				'</div>'
       
 23159 			);
       
 23160 		}
       
 23161 	});
       
 23162 });
       
 23163 
       
 23164 // Included from: js/tinymce/classes/ui/Movable.js
       
 23165 
       
 23166 /**
       
 23167  * Movable.js
       
 23168  *
       
 23169  * Copyright, Moxiecode Systems AB
       
 23170  * Released under LGPL License.
       
 23171  *
       
 23172  * License: http://www.tinymce.com/license
       
 23173  * Contributing: http://www.tinymce.com/contributing
       
 23174  */
       
 23175 
       
 23176 /**
       
 23177  * Movable mixin. Makes controls movable absolute and relative to other elements.
       
 23178  *
       
 23179  * @mixin tinymce.ui.Movable
       
 23180  */
       
 23181 define("tinymce/ui/Movable", [
       
 23182 	"tinymce/ui/DomUtils"
       
 23183 ], function(DomUtils) {
       
 23184 	"use strict";
       
 23185 
       
 23186 	function calculateRelativePosition(ctrl, targetElm, rel) {
       
 23187 		var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
       
 23188 
       
 23189 		viewport = DomUtils.getViewPort();
       
 23190 
       
 23191 		// Get pos of target
       
 23192 		pos = DomUtils.getPos(targetElm);
       
 23193 		x = pos.x;
       
 23194 		y = pos.y;
       
 23195 
       
 23196 		if (ctrl._fixed && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') {
       
 23197 			x -= viewport.x;
       
 23198 			y -= viewport.y;
       
 23199 		}
       
 23200 
       
 23201 		// Get size of self
       
 23202 		ctrlElm = ctrl.getEl();
       
 23203 		size = DomUtils.getSize(ctrlElm);
       
 23204 		selfW = size.width;
       
 23205 		selfH = size.height;
       
 23206 
       
 23207 		// Get size of target
       
 23208 		size = DomUtils.getSize(targetElm);
       
 23209 		targetW = size.width;
       
 23210 		targetH = size.height;
       
 23211 
       
 23212 		// Parse align string
       
 23213 		rel = (rel || '').split('');
       
 23214 
       
 23215 		// Target corners
       
 23216 		if (rel[0] === 'b') {
       
 23217 			y += targetH;
       
 23218 		}
       
 23219 
       
 23220 		if (rel[1] === 'r') {
       
 23221 			x += targetW;
       
 23222 		}
       
 23223 
       
 23224 		if (rel[0] === 'c') {
       
 23225 			y += Math.round(targetH / 2);
       
 23226 		}
       
 23227 
       
 23228 		if (rel[1] === 'c') {
       
 23229 			x += Math.round(targetW / 2);
       
 23230 		}
       
 23231 
       
 23232 		// Self corners
       
 23233 		if (rel[3] === 'b') {
       
 23234 			y -= selfH;
       
 23235 		}
       
 23236 
       
 23237 		if (rel[4] === 'r') {
       
 23238 			x -= selfW;
       
 23239 		}
       
 23240 
       
 23241 		if (rel[3] === 'c') {
       
 23242 			y -= Math.round(selfH / 2);
       
 23243 		}
       
 23244 
       
 23245 		if (rel[4] === 'c') {
       
 23246 			x -= Math.round(selfW / 2);
       
 23247 		}
       
 23248 
       
 23249 		return {
       
 23250 			x: x,
       
 23251 			y: y,
       
 23252 			w: selfW,
       
 23253 			h: selfH
       
 23254 		};
       
 23255 	}
       
 23256 
       
 23257 	return {
       
 23258 		/**
       
 23259 		 * Tests various positions to get the most suitable one.
       
 23260 		 *
       
 23261 		 * @method testMoveRel
       
 23262 		 * @param {DOMElement} elm Element to position against.
       
 23263 		 * @param {Array} rels Array with relative positions.
       
 23264 		 * @return {String} Best suitable relative position.
       
 23265 		 */
       
 23266 		testMoveRel: function(elm, rels) {
       
 23267 			var viewPortRect = DomUtils.getViewPort();
       
 23268 
       
 23269 			for (var i = 0; i < rels.length; i++) {
       
 23270 				var pos = calculateRelativePosition(this, elm, rels[i]);
       
 23271 
       
 23272 				if (this._fixed) {
       
 23273 					if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
       
 23274 						return rels[i];
       
 23275 					}
       
 23276 				} else {
       
 23277 					if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
       
 23278 						pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
       
 23279 						return rels[i];
       
 23280 					}
       
 23281 				}
       
 23282 			}
       
 23283 
       
 23284 			return rels[0];
       
 23285 		},
       
 23286 
       
 23287 		/**
       
 23288 		 * Move relative to the specified element.
       
 23289 		 *
       
 23290 		 * @method moveRel
       
 23291 		 * @param {Element} elm Element to move relative to.
       
 23292 		 * @param {String} rel Relative mode. For example: br-tl.
       
 23293 		 * @return {tinymce.ui.Control} Current control instance.
       
 23294 		 */
       
 23295 		moveRel: function(elm, rel) {
       
 23296 			if (typeof rel != 'string') {
       
 23297 				rel = this.testMoveRel(elm, rel);
       
 23298 			}
       
 23299 
       
 23300 			var pos = calculateRelativePosition(this, elm, rel);
       
 23301 			return this.moveTo(pos.x, pos.y);
       
 23302 		},
       
 23303 
       
 23304 		/**
       
 23305 		 * Move by a relative x, y values.
       
 23306 		 *
       
 23307 		 * @method moveBy
       
 23308 		 * @param {Number} dx Relative x position.
       
 23309 		 * @param {Number} dy Relative y position.
       
 23310 		 * @return {tinymce.ui.Control} Current control instance.
       
 23311 		 */
       
 23312 		moveBy: function(dx, dy) {
       
 23313 			var self = this, rect = self.layoutRect();
       
 23314 
       
 23315 			self.moveTo(rect.x + dx, rect.y + dy);
       
 23316 
       
 23317 			return self;
       
 23318 		},
       
 23319 
       
 23320 		/**
       
 23321 		 * Move to absolute position.
       
 23322 		 *
       
 23323 		 * @method moveTo
       
 23324 		 * @param {Number} x Absolute x position.
       
 23325 		 * @param {Number} y Absolute y position.
       
 23326 		 * @return {tinymce.ui.Control} Current control instance.
       
 23327 		 */
       
 23328 		moveTo: function(x, y) {
       
 23329 			var self = this;
       
 23330 
       
 23331 			// TODO: Move this to some global class
       
 23332 			function contrain(value, max, size) {
       
 23333 				if (value < 0) {
       
 23334 					return 0;
       
 23335 				}
       
 23336 
       
 23337 				if (value + size > max) {
       
 23338 					value = max - size;
       
 23339 					return value < 0 ? 0 : value;
       
 23340 				}
       
 23341 
       
 23342 				return value;
       
 23343 			}
       
 23344 
       
 23345 			if (self.settings.constrainToViewport) {
       
 23346 				var viewPortRect = DomUtils.getViewPort(window);
       
 23347 				var layoutRect = self.layoutRect();
       
 23348 
       
 23349 				x = contrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
       
 23350 				y = contrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
       
 23351 			}
       
 23352 
       
 23353 			if (self._rendered) {
       
 23354 				self.layoutRect({x: x, y: y}).repaint();
       
 23355 			} else {
       
 23356 				self.settings.x = x;
       
 23357 				self.settings.y = y;
       
 23358 			}
       
 23359 
       
 23360 			self.fire('move', {x: x, y: y});
       
 23361 
       
 23362 			return self;
       
 23363 		}
       
 23364 	};
       
 23365 });
       
 23366 
       
 23367 // Included from: js/tinymce/classes/ui/Resizable.js
       
 23368 
       
 23369 /**
       
 23370  * Resizable.js
       
 23371  *
       
 23372  * Copyright, Moxiecode Systems AB
       
 23373  * Released under LGPL License.
       
 23374  *
       
 23375  * License: http://www.tinymce.com/license
       
 23376  * Contributing: http://www.tinymce.com/contributing
       
 23377  */
       
 23378 
       
 23379 /**
       
 23380  * Resizable mixin. Enables controls to be resized.
       
 23381  *
       
 23382  * @mixin tinymce.ui.Resizable
       
 23383  */
       
 23384 define("tinymce/ui/Resizable", [
       
 23385 	"tinymce/ui/DomUtils"
       
 23386 ], function(DomUtils) {
       
 23387 	"use strict";
       
 23388 
       
 23389 	return {
       
 23390 		/**
       
 23391 		 * Resizes the control to contents.
       
 23392 		 *
       
 23393 		 * @method resizeToContent
       
 23394 		 */
       
 23395 		resizeToContent: function() {
       
 23396 			this._layoutRect.autoResize = true;
       
 23397 			this._lastRect = null;
       
 23398 			this.reflow();
       
 23399 		},
       
 23400 
       
 23401 		/**
       
 23402 		 * Resizes the control to a specific width/height.
       
 23403 		 *
       
 23404 		 * @method resizeTo
       
 23405 		 * @param {Number} w Control width.
       
 23406 		 * @param {Number} h Control height.
       
 23407 		 * @return {tinymce.ui.Control} Current control instance.
       
 23408 		 */
       
 23409 		resizeTo: function(w, h) {
       
 23410 			// TODO: Fix hack
       
 23411 			if (w <= 1 || h <= 1) {
       
 23412 				var rect = DomUtils.getWindowSize();
       
 23413 
       
 23414 				w = w <= 1 ? w * rect.w : w;
       
 23415 				h = h <= 1 ? h * rect.h : h;
       
 23416 			}
       
 23417 
       
 23418 			this._layoutRect.autoResize = false;
       
 23419 			return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow();
       
 23420 		},
       
 23421 
       
 23422 		/**
       
 23423 		 * Resizes the control to a specific relative width/height.
       
 23424 		 *
       
 23425 		 * @method resizeBy
       
 23426 		 * @param {Number} dw Relative control width.
       
 23427 		 * @param {Number} dh Relative control height.
       
 23428 		 * @return {tinymce.ui.Control} Current control instance.
       
 23429 		 */
       
 23430 		resizeBy: function(dw, dh) {
       
 23431 			var self = this, rect = self.layoutRect();
       
 23432 
       
 23433 			return self.resizeTo(rect.w + dw, rect.h + dh);
       
 23434 		}
       
 23435 	};
       
 23436 });
       
 23437 
       
 23438 // Included from: js/tinymce/classes/ui/FloatPanel.js
       
 23439 
       
 23440 /**
       
 23441  * FloatPanel.js
       
 23442  *
       
 23443  * Copyright, Moxiecode Systems AB
       
 23444  * Released under LGPL License.
       
 23445  *
       
 23446  * License: http://www.tinymce.com/license
       
 23447  * Contributing: http://www.tinymce.com/contributing
       
 23448  */
       
 23449 
       
 23450 /**
       
 23451  * This class creates a floating panel.
       
 23452  *
       
 23453  * @-x-less FloatPanel.less
       
 23454  * @class tinymce.ui.FloatPanel
       
 23455  * @extends tinymce.ui.Panel
       
 23456  * @mixes tinymce.ui.Movable
       
 23457  * @mixes tinymce.ui.Resizable
       
 23458  */
       
 23459 define("tinymce/ui/FloatPanel", [
       
 23460 	"tinymce/ui/Panel",
       
 23461 	"tinymce/ui/Movable",
       
 23462 	"tinymce/ui/Resizable",
       
 23463 	"tinymce/ui/DomUtils"
       
 23464 ], function(Panel, Movable, Resizable, DomUtils) {
       
 23465 	"use strict";
       
 23466 
       
 23467 	var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = [];
       
 23468 	var zOrder = [], hasModal;
       
 23469 
       
 23470 	function bindDocumentClickHandler() {
       
 23471 		function isChildOf(ctrl, parent) {
       
 23472 			while (ctrl) {
       
 23473 				if (ctrl == parent) {
       
 23474 					return true;
       
 23475 				}
       
 23476 
       
 23477 				ctrl = ctrl.parent();
       
 23478 			}
       
 23479 		}
       
 23480 
       
 23481 		if (!documentClickHandler) {
       
 23482 			documentClickHandler = function(e) {
       
 23483 				// Gecko fires click event and in the wrong order on Mac so lets normalize
       
 23484 				if (e.button == 2) {
       
 23485 					return;
       
 23486 				}
       
 23487 
       
 23488 				// Hide any float panel when a click is out side that float panel and the
       
 23489 				// float panels direct parent for example a click on a menu button
       
 23490 				var i = visiblePanels.length;
       
 23491 				while (i--) {
       
 23492 					var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
       
 23493 
       
 23494 					if (panel.settings.autohide) {
       
 23495 						if (clickCtrl) {
       
 23496 							if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
       
 23497 								continue;
       
 23498 							}
       
 23499 						}
       
 23500 
       
 23501 						e = panel.fire('autohide', {target: e.target});
       
 23502 						if (!e.isDefaultPrevented()) {
       
 23503 							panel.hide();
       
 23504 						}
       
 23505 					}
       
 23506 				}
       
 23507 			};
       
 23508 
       
 23509 			DomUtils.on(document, 'click', documentClickHandler);
       
 23510 		}
       
 23511 	}
       
 23512 
       
 23513 	function bindDocumentScrollHandler() {
       
 23514 		if (!documentScrollHandler) {
       
 23515 			documentScrollHandler = function() {
       
 23516 				var i;
       
 23517 
       
 23518 				i = visiblePanels.length;
       
 23519 				while (i--) {
       
 23520 					repositionPanel(visiblePanels[i]);
       
 23521 				}
       
 23522 			};
       
 23523 
       
 23524 			DomUtils.on(window, 'scroll', documentScrollHandler);
       
 23525 		}
       
 23526 	}
       
 23527 
       
 23528 	function bindWindowResizeHandler() {
       
 23529 		if (!windowResizeHandler) {
       
 23530 			var docElm = document.documentElement, clientWidth = docElm.clientWidth, clientHeight = docElm.clientHeight;
       
 23531 
       
 23532 			windowResizeHandler = function() {
       
 23533 				// Workaround for #7065 IE 7 fires resize events event though the window wasn't resized
       
 23534 				if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) {
       
 23535 					clientWidth = docElm.clientWidth;
       
 23536 					clientHeight = docElm.clientHeight;
       
 23537 					FloatPanel.hideAll();
       
 23538 				}
       
 23539 			};
       
 23540 
       
 23541 			DomUtils.on(window, 'resize', windowResizeHandler);
       
 23542 		}
       
 23543 	}
       
 23544 
       
 23545 	/**
       
 23546 	 * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
       
 23547 	 * also reposition all child panels of the current panel.
       
 23548 	 */
       
 23549 	function repositionPanel(panel) {
       
 23550 		var scrollY = DomUtils.getViewPort().y;
       
 23551 
       
 23552 		function toggleFixedChildPanels(fixed, deltaY) {
       
 23553 			var parent;
       
 23554 
       
 23555 			for (var i = 0; i < visiblePanels.length; i++) {
       
 23556 				if (visiblePanels[i] != panel) {
       
 23557 					parent = visiblePanels[i].parent();
       
 23558 
       
 23559 					while (parent && (parent = parent.parent())) {
       
 23560 						if (parent == panel) {
       
 23561 							visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
       
 23562 						}
       
 23563 					}
       
 23564 				}
       
 23565 			}
       
 23566 		}
       
 23567 
       
 23568 		if (panel.settings.autofix) {
       
 23569 			if (!panel._fixed) {
       
 23570 				panel._autoFixY = panel.layoutRect().y;
       
 23571 
       
 23572 				if (panel._autoFixY < scrollY) {
       
 23573 					panel.fixed(true).layoutRect({y: 0}).repaint();
       
 23574 					toggleFixedChildPanels(true, scrollY - panel._autoFixY);
       
 23575 				}
       
 23576 			} else {
       
 23577 				if (panel._autoFixY > scrollY) {
       
 23578 					panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
       
 23579 					toggleFixedChildPanels(false, panel._autoFixY - scrollY);
       
 23580 				}
       
 23581 			}
       
 23582 		}
       
 23583 	}
       
 23584 
       
 23585 	function addRemove(add, ctrl) {
       
 23586 		var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
       
 23587 
       
 23588 		if (add) {
       
 23589 			zOrder.push(ctrl);
       
 23590 		} else {
       
 23591 			i = zOrder.length;
       
 23592 
       
 23593 			while (i--) {
       
 23594 				if (zOrder[i] === ctrl) {
       
 23595 					zOrder.splice(i, 1);
       
 23596 				}
       
 23597 			}
       
 23598 		}
       
 23599 
       
 23600 		if (zOrder.length) {
       
 23601 			for (i = 0; i < zOrder.length; i++) {
       
 23602 				if (zOrder[i].modal) {
       
 23603 					zIndex++;
       
 23604 					topModal = zOrder[i];
       
 23605 				}
       
 23606 
       
 23607 				zOrder[i].getEl().style.zIndex = zIndex;
       
 23608 				zOrder[i].zIndex = zIndex;
       
 23609 				zIndex++;
       
 23610 			}
       
 23611 		}
       
 23612 
       
 23613 		var modalBlockEl = document.getElementById(ctrl.classPrefix + 'modal-block');
       
 23614 
       
 23615 		if (topModal) {
       
 23616 			DomUtils.css(modalBlockEl, 'z-index', topModal.zIndex - 1);
       
 23617 		} else if (modalBlockEl) {
       
 23618 			modalBlockEl.parentNode.removeChild(modalBlockEl);
       
 23619 			hasModal = false;
       
 23620 		}
       
 23621 
       
 23622 		FloatPanel.currentZIndex = zIndex;
       
 23623 	}
       
 23624 
       
 23625 	var FloatPanel = Panel.extend({
       
 23626 		Mixins: [Movable, Resizable],
       
 23627 
       
 23628 		/**
       
 23629 		 * Constructs a new control instance with the specified settings.
       
 23630 		 *
       
 23631 		 * @constructor
       
 23632 		 * @param {Object} settings Name/value object with settings.
       
 23633 		 * @setting {Boolean} autohide Automatically hide the panel.
       
 23634 		 */
       
 23635 		init: function(settings) {
       
 23636 			var self = this;
       
 23637 
       
 23638 			self._super(settings);
       
 23639 			self._eventsRoot = self;
       
 23640 
       
 23641 			self.addClass('floatpanel');
       
 23642 
       
 23643 			// Hide floatpanes on click out side the root button
       
 23644 			if (settings.autohide) {
       
 23645 				bindDocumentClickHandler();
       
 23646 				bindWindowResizeHandler();
       
 23647 				visiblePanels.push(self);
       
 23648 			}
       
 23649 
       
 23650 			if (settings.autofix) {
       
 23651 				bindDocumentScrollHandler();
       
 23652 
       
 23653 				self.on('move', function() {
       
 23654 					repositionPanel(this);
       
 23655 				});
       
 23656 			}
       
 23657 
       
 23658 			self.on('postrender show', function(e) {
       
 23659 				if (e.control == self) {
       
 23660 					var modalBlockEl, prefix = self.classPrefix;
       
 23661 
       
 23662 					if (self.modal && !hasModal) {
       
 23663 						modalBlockEl = DomUtils.createFragment('<div id="' + prefix + 'modal-block" class="' +
       
 23664 							prefix + 'reset ' + prefix + 'fade"></div>');
       
 23665 						modalBlockEl = modalBlockEl.firstChild;
       
 23666 
       
 23667 						self.getContainerElm().appendChild(modalBlockEl);
       
 23668 
       
 23669 						setTimeout(function() {
       
 23670 							DomUtils.addClass(modalBlockEl, prefix + 'in');
       
 23671 							DomUtils.addClass(self.getEl(), prefix + 'in');
       
 23672 						}, 0);
       
 23673 
       
 23674 						hasModal = true;
       
 23675 					}
       
 23676 
       
 23677 					addRemove(true, self);
       
 23678 				}
       
 23679 			});
       
 23680 
       
 23681 			self.on('show', function() {
       
 23682 				self.parents().each(function(ctrl) {
       
 23683 					if (ctrl._fixed) {
       
 23684 						self.fixed(true);
       
 23685 						return false;
       
 23686 					}
       
 23687 				});
       
 23688 			});
       
 23689 
       
 23690 			if (settings.popover) {
       
 23691 				self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
       
 23692 				self.addClass('popover').addClass('bottom').addClass(self.isRtl() ? 'end' : 'start');
       
 23693 			}
       
 23694 		},
       
 23695 
       
 23696 		fixed: function(state) {
       
 23697 			var self = this;
       
 23698 
       
 23699 			if (self._fixed != state) {
       
 23700 				if (self._rendered) {
       
 23701 					var viewport = DomUtils.getViewPort();
       
 23702 
       
 23703 					if (state) {
       
 23704 						self.layoutRect().y -= viewport.y;
       
 23705 					} else {
       
 23706 						self.layoutRect().y += viewport.y;
       
 23707 					}
       
 23708 				}
       
 23709 
       
 23710 				self.toggleClass('fixed', state);
       
 23711 				self._fixed = state;
       
 23712 			}
       
 23713 
       
 23714 			return self;
       
 23715 		},
       
 23716 
       
 23717 		/**
       
 23718 		 * Shows the current float panel.
       
 23719 		 *
       
 23720 		 * @method show
       
 23721 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
       
 23722 		 */
       
 23723 		show: function() {
       
 23724 			var self = this, i, state = self._super();
       
 23725 
       
 23726 			i = visiblePanels.length;
       
 23727 			while (i--) {
       
 23728 				if (visiblePanels[i] === self) {
       
 23729 					break;
       
 23730 				}
       
 23731 			}
       
 23732 
       
 23733 			if (i === -1) {
       
 23734 				visiblePanels.push(self);
       
 23735 			}
       
 23736 
       
 23737 			return state;
       
 23738 		},
       
 23739 
       
 23740 		/**
       
 23741 		 * Hides the current float panel.
       
 23742 		 *
       
 23743 		 * @method hide
       
 23744 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
       
 23745 		 */
       
 23746 		hide: function() {
       
 23747 			removeVisiblePanel(this);
       
 23748 			addRemove(false, this);
       
 23749 
       
 23750 			return this._super();
       
 23751 		},
       
 23752 
       
 23753 		/**
       
 23754 		 * Hide all visible float panels with he autohide setting enabled. This is for
       
 23755 		 * manually hiding floating menus or panels.
       
 23756 		 *
       
 23757 		 * @method hideAll
       
 23758 		 */
       
 23759 		hideAll: function() {
       
 23760 			FloatPanel.hideAll();
       
 23761 		},
       
 23762 
       
 23763 		/**
       
 23764 		 * Closes the float panel. This will remove the float panel from page and fire the close event.
       
 23765 		 *
       
 23766 		 * @method close
       
 23767 		 */
       
 23768 		close: function() {
       
 23769 			var self = this;
       
 23770 
       
 23771 			if (!self.fire('close').isDefaultPrevented()) {
       
 23772 				self.remove();
       
 23773 				addRemove(false, self);
       
 23774 			}
       
 23775 
       
 23776 			return self;
       
 23777 		},
       
 23778 
       
 23779 		/**
       
 23780 		 * Removes the float panel from page.
       
 23781 		 *
       
 23782 		 * @method remove
       
 23783 		 */
       
 23784 		remove: function() {
       
 23785 			removeVisiblePanel(this);
       
 23786 			this._super();
       
 23787 		},
       
 23788 
       
 23789 		postRender: function() {
       
 23790 			var self = this;
       
 23791 
       
 23792 			if (self.settings.bodyRole) {
       
 23793 				this.getEl('body').setAttribute('role', self.settings.bodyRole);
       
 23794 			}
       
 23795 
       
 23796 			return self._super();
       
 23797 		}
       
 23798 	});
       
 23799 
       
 23800 	/**
       
 23801 	 * Hide all visible float panels with he autohide setting enabled. This is for
       
 23802 	 * manually hiding floating menus or panels.
       
 23803 	 *
       
 23804 	 * @static
       
 23805 	 * @method hideAll
       
 23806 	 */
       
 23807 	FloatPanel.hideAll = function() {
       
 23808 		var i = visiblePanels.length;
       
 23809 
       
 23810 		while (i--) {
       
 23811 			var panel = visiblePanels[i];
       
 23812 
       
 23813 			if (panel && panel.settings.autohide) {
       
 23814 				panel.hide();
       
 23815 				visiblePanels.splice(i, 1);
       
 23816 			}
       
 23817 		}
       
 23818 	};
       
 23819 
       
 23820 	function removeVisiblePanel(panel) {
       
 23821 		var i;
       
 23822 
       
 23823 		i = visiblePanels.length;
       
 23824 		while (i--) {
       
 23825 			if (visiblePanels[i] === panel) {
       
 23826 				visiblePanels.splice(i, 1);
       
 23827 			}
       
 23828 		}
       
 23829 
       
 23830 		i = zOrder.length;
       
 23831 		while (i--) {
       
 23832 			if (zOrder[i] === panel) {
       
 23833 				zOrder.splice(i, 1);
       
 23834 			}
       
 23835 		}
       
 23836 	}
       
 23837 
       
 23838 	return FloatPanel;
       
 23839 });
       
 23840 
       
 23841 // Included from: js/tinymce/classes/ui/Window.js
       
 23842 
       
 23843 /**
       
 23844  * Window.js
       
 23845  *
       
 23846  * Copyright, Moxiecode Systems AB
       
 23847  * Released under LGPL License.
       
 23848  *
       
 23849  * License: http://www.tinymce.com/license
       
 23850  * Contributing: http://www.tinymce.com/contributing
       
 23851  */
       
 23852 
       
 23853 /**
       
 23854  * Creates a new window.
       
 23855  *
       
 23856  * @-x-less Window.less
       
 23857  * @class tinymce.ui.Window
       
 23858  * @extends tinymce.ui.FloatPanel
       
 23859  */
       
 23860 define("tinymce/ui/Window", [
       
 23861 	"tinymce/ui/FloatPanel",
       
 23862 	"tinymce/ui/Panel",
       
 23863 	"tinymce/ui/DomUtils",
       
 23864 	"tinymce/ui/DragHelper"
       
 23865 ], function(FloatPanel, Panel, DomUtils, DragHelper) {
       
 23866 	"use strict";
       
 23867 
       
 23868 	var Window = FloatPanel.extend({
       
 23869 		modal: true,
       
 23870 
       
 23871 		Defaults: {
       
 23872 			border: 1,
       
 23873 			layout: 'flex',
       
 23874 			containerCls: 'panel',
       
 23875 			role: 'dialog',
       
 23876 			callbacks: {
       
 23877 				submit: function() {
       
 23878 					this.fire('submit', {data: this.toJSON()});
       
 23879 				},
       
 23880 
       
 23881 				close: function() {
       
 23882 					this.close();
       
 23883 				}
       
 23884 			}
       
 23885 		},
       
 23886 
       
 23887 		/**
       
 23888 		 * Constructs a instance with the specified settings.
       
 23889 		 *
       
 23890 		 * @constructor
       
 23891 		 * @param {Object} settings Name/value object with settings.
       
 23892 		 */
       
 23893 		init: function(settings) {
       
 23894 			var self = this;
       
 23895 
       
 23896 			self._super(settings);
       
 23897 
       
 23898 			if (self.isRtl()) {
       
 23899 				self.addClass('rtl');
       
 23900 			}
       
 23901 
       
 23902 			self.addClass('window');
       
 23903 			self._fixed = true;
       
 23904 
       
 23905 			// Create statusbar
       
 23906 			if (settings.buttons) {
       
 23907 				self.statusbar = new Panel({
       
 23908 					layout: 'flex',
       
 23909 					border: '1 0 0 0',
       
 23910 					spacing: 3,
       
 23911 					padding: 10,
       
 23912 					align: 'center',
       
 23913 					pack: self.isRtl() ? 'start' : 'end',
       
 23914 					defaults: {
       
 23915 						type: 'button'
       
 23916 					},
       
 23917 					items: settings.buttons
       
 23918 				});
       
 23919 
       
 23920 				self.statusbar.addClass('foot');
       
 23921 				self.statusbar.parent(self);
       
 23922 			}
       
 23923 
       
 23924 			self.on('click', function(e) {
       
 23925 				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
       
 23926 					self.close();
       
 23927 				}
       
 23928 			});
       
 23929 
       
 23930 			self.on('cancel', function() {
       
 23931 				self.close();
       
 23932 			});
       
 23933 
       
 23934 			self.aria('describedby', self.describedBy || self._id + '-none');
       
 23935 			self.aria('label', settings.title);
       
 23936 			self._fullscreen = false;
       
 23937 		},
       
 23938 
       
 23939 		/**
       
 23940 		 * Recalculates the positions of the controls in the current container.
       
 23941 		 * This is invoked by the reflow method and shouldn't be called directly.
       
 23942 		 *
       
 23943 		 * @method recalc
       
 23944 		 */
       
 23945 		recalc: function() {
       
 23946 			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
       
 23947 
       
 23948 			if (self._fullscreen) {
       
 23949 				self.layoutRect(DomUtils.getWindowSize());
       
 23950 				self.layoutRect().contentH = self.layoutRect().innerH;
       
 23951 			}
       
 23952 
       
 23953 			self._super();
       
 23954 
       
 23955 			layoutRect = self.layoutRect();
       
 23956 
       
 23957 			// Resize window based on title width
       
 23958 			if (self.settings.title && !self._fullscreen) {
       
 23959 				width = layoutRect.headerW;
       
 23960 				if (width > layoutRect.w) {
       
 23961 					x = layoutRect.x - Math.max(0, width / 2);
       
 23962 					self.layoutRect({w: width, x: x});
       
 23963 					needsRecalc = true;
       
 23964 				}
       
 23965 			}
       
 23966 
       
 23967 			// Resize window based on statusbar width
       
 23968 			if (statusbar) {
       
 23969 				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
       
 23970 
       
 23971 				width = statusbar.layoutRect().minW + layoutRect.deltaW;
       
 23972 				if (width > layoutRect.w) {
       
 23973 					x = layoutRect.x - Math.max(0, width - layoutRect.w);
       
 23974 					self.layoutRect({w: width, x: x});
       
 23975 					needsRecalc = true;
       
 23976 				}
       
 23977 			}
       
 23978 
       
 23979 			// Recalc body and disable auto resize
       
 23980 			if (needsRecalc) {
       
 23981 				self.recalc();
       
 23982 			}
       
 23983 		},
       
 23984 
       
 23985 		/**
       
 23986 		 * Initializes the current controls layout rect.
       
 23987 		 * This will be executed by the layout managers to determine the
       
 23988 		 * default minWidth/minHeight etc.
       
 23989 		 *
       
 23990 		 * @method initLayoutRect
       
 23991 		 * @return {Object} Layout rect instance.
       
 23992 		 */
       
 23993 		initLayoutRect: function() {
       
 23994 			var self = this, layoutRect = self._super(), deltaH = 0, headEl;
       
 23995 
       
 23996 			// Reserve vertical space for title
       
 23997 			if (self.settings.title && !self._fullscreen) {
       
 23998 				headEl = self.getEl('head');
       
 23999 
       
 24000 				var size = DomUtils.getSize(headEl);
       
 24001 
       
 24002 				layoutRect.headerW = size.width;
       
 24003 				layoutRect.headerH = size.height;
       
 24004 
       
 24005 				deltaH += layoutRect.headerH;
       
 24006 			}
       
 24007 
       
 24008 			// Reserve vertical space for statusbar
       
 24009 			if (self.statusbar) {
       
 24010 				deltaH += self.statusbar.layoutRect().h;
       
 24011 			}
       
 24012 
       
 24013 			layoutRect.deltaH += deltaH;
       
 24014 			layoutRect.minH += deltaH;
       
 24015 			//layoutRect.innerH -= deltaH;
       
 24016 			layoutRect.h += deltaH;
       
 24017 
       
 24018 			var rect = DomUtils.getWindowSize();
       
 24019 
       
 24020 			layoutRect.x = Math.max(0, rect.w / 2 - layoutRect.w / 2);
       
 24021 			layoutRect.y = Math.max(0, rect.h / 2 - layoutRect.h / 2);
       
 24022 
       
 24023 			return layoutRect;
       
 24024 		},
       
 24025 
       
 24026 		/**
       
 24027 		 * Renders the control as a HTML string.
       
 24028 		 *
       
 24029 		 * @method renderHtml
       
 24030 		 * @return {String} HTML representing the control.
       
 24031 		 */
       
 24032 		renderHtml: function() {
       
 24033 			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
       
 24034 			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
       
 24035 
       
 24036 			self.preRender();
       
 24037 			layout.preRender(self);
       
 24038 
       
 24039 			if (settings.title) {
       
 24040 				headerHtml = (
       
 24041 					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
       
 24042 						'<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
       
 24043 						'<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>' +
       
 24044 						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
       
 24045 					'</div>'
       
 24046 				);
       
 24047 			}
       
 24048 
       
 24049 			if (settings.url) {
       
 24050 				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
       
 24051 			}
       
 24052 
       
 24053 			if (typeof html == "undefined") {
       
 24054 				html = layout.renderHtml(self);
       
 24055 			}
       
 24056 
       
 24057 			if (self.statusbar) {
       
 24058 				footerHtml = self.statusbar.renderHtml();
       
 24059 			}
       
 24060 
       
 24061 			return (
       
 24062 				'<div id="' + id + '" class="' + self.classes() + '" hidefocus="1">' +
       
 24063 					'<div class="' + self.classPrefix + 'reset" role="application">' +
       
 24064 						headerHtml +
       
 24065 						'<div id="' + id + '-body" class="' + self.classes('body') + '">' +
       
 24066 							html +
       
 24067 						'</div>' +
       
 24068 						footerHtml +
       
 24069 					'</div>' +
       
 24070 				'</div>'
       
 24071 			);
       
 24072 		},
       
 24073 
       
 24074 		/**
       
 24075 		 * Switches the window fullscreen mode.
       
 24076 		 *
       
 24077 		 * @method fullscreen
       
 24078 		 * @param {Boolean} state True/false state.
       
 24079 		 * @return {tinymce.ui.Window} Current window instance.
       
 24080 		 */
       
 24081 		fullscreen: function(state) {
       
 24082 			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
       
 24083 
       
 24084 			if (state != self._fullscreen) {
       
 24085 				DomUtils.on(window, 'resize', function() {
       
 24086 					var time;
       
 24087 
       
 24088 					if (self._fullscreen) {
       
 24089 						// Time the layout time if it's to slow use a timeout to not hog the CPU
       
 24090 						if (!slowRendering) {
       
 24091 							time = new Date().getTime();
       
 24092 
       
 24093 							var rect = DomUtils.getWindowSize();
       
 24094 							self.moveTo(0, 0).resizeTo(rect.w, rect.h);
       
 24095 
       
 24096 							if ((new Date().getTime()) - time > 50) {
       
 24097 								slowRendering = true;
       
 24098 							}
       
 24099 						} else {
       
 24100 							if (!self._timer) {
       
 24101 								self._timer = setTimeout(function() {
       
 24102 									var rect = DomUtils.getWindowSize();
       
 24103 									self.moveTo(0, 0).resizeTo(rect.w, rect.h);
       
 24104 
       
 24105 									self._timer = 0;
       
 24106 								}, 50);
       
 24107 							}
       
 24108 						}
       
 24109 					}
       
 24110 				});
       
 24111 
       
 24112 				layoutRect = self.layoutRect();
       
 24113 				self._fullscreen = state;
       
 24114 
       
 24115 				if (!state) {
       
 24116 					self._borderBox = self.parseBox(self.settings.border);
       
 24117 					self.getEl('head').style.display = '';
       
 24118 					layoutRect.deltaH += layoutRect.headerH;
       
 24119 					DomUtils.removeClass(documentElement, prefix + 'fullscreen');
       
 24120 					DomUtils.removeClass(document.body, prefix + 'fullscreen');
       
 24121 					self.removeClass('fullscreen');
       
 24122 					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
       
 24123 				} else {
       
 24124 					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
       
 24125 
       
 24126 					self._borderBox = self.parseBox('0');
       
 24127 					self.getEl('head').style.display = 'none';
       
 24128 					layoutRect.deltaH -= layoutRect.headerH + 2;
       
 24129 					DomUtils.addClass(documentElement, prefix + 'fullscreen');
       
 24130 					DomUtils.addClass(document.body, prefix + 'fullscreen');
       
 24131 					self.addClass('fullscreen');
       
 24132 
       
 24133 					var rect = DomUtils.getWindowSize();
       
 24134 					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
       
 24135 				}
       
 24136 			}
       
 24137 
       
 24138 			return self.reflow();
       
 24139 		},
       
 24140 
       
 24141 		/**
       
 24142 		 * Called after the control has been rendered.
       
 24143 		 *
       
 24144 		 * @method postRender
       
 24145 		 */
       
 24146 		postRender: function() {
       
 24147 			var self = this, startPos;
       
 24148 
       
 24149 			setTimeout(function() {
       
 24150 				self.addClass('in');
       
 24151 			}, 0);
       
 24152 
       
 24153 			self._super();
       
 24154 
       
 24155 			if (self.statusbar) {
       
 24156 				self.statusbar.postRender();
       
 24157 			}
       
 24158 
       
 24159 			self.focus();
       
 24160 
       
 24161 			this.dragHelper = new DragHelper(self._id + '-dragh', {
       
 24162 				start: function() {
       
 24163 					startPos = {
       
 24164 						x: self.layoutRect().x,
       
 24165 						y: self.layoutRect().y
       
 24166 					};
       
 24167 				},
       
 24168 
       
 24169 				drag: function(e) {
       
 24170 					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
       
 24171 				}
       
 24172 			});
       
 24173 
       
 24174 			self.on('submit', function(e) {
       
 24175 				if (!e.isDefaultPrevented()) {
       
 24176 					self.close();
       
 24177 				}
       
 24178 			});
       
 24179 		},
       
 24180 
       
 24181 		/**
       
 24182 		 * Fires a submit event with the serialized form.
       
 24183 		 *
       
 24184 		 * @method submit
       
 24185 		 * @return {Object} Event arguments object.
       
 24186 		 */
       
 24187 		submit: function() {
       
 24188 			return this.fire('submit', {data: this.toJSON()});
       
 24189 		},
       
 24190 
       
 24191 		/**
       
 24192 		 * Removes the current control from DOM and from UI collections.
       
 24193 		 *
       
 24194 		 * @method remove
       
 24195 		 * @return {tinymce.ui.Control} Current control instance.
       
 24196 		 */
       
 24197 		remove: function() {
       
 24198 			var self = this, prefix = self.classPrefix;
       
 24199 
       
 24200 			self.dragHelper.destroy();
       
 24201 			self._super();
       
 24202 
       
 24203 			if (self.statusbar) {
       
 24204 				this.statusbar.remove();
       
 24205 			}
       
 24206 
       
 24207 			if (self._fullscreen) {
       
 24208 				DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
       
 24209 				DomUtils.removeClass(document.body, prefix + 'fullscreen');
       
 24210 			}
       
 24211 		},
       
 24212 
       
 24213 		/**
       
 24214 		 * Returns the contentWindow object of the iframe if it exists.
       
 24215 		 *
       
 24216 		 * @method getContentWindow
       
 24217 		 * @return {Window} window object or null.
       
 24218 		 */
       
 24219 		getContentWindow: function() {
       
 24220 			var ifr = this.getEl().getElementsByTagName('iframe')[0];
       
 24221 			return ifr ? ifr.contentWindow : null;
       
 24222 		}
       
 24223 	});
       
 24224 
       
 24225 	return Window;
       
 24226 });
       
 24227 
       
 24228 // Included from: js/tinymce/classes/ui/MessageBox.js
       
 24229 
       
 24230 /**
       
 24231  * MessageBox.js
       
 24232  *
       
 24233  * Copyright, Moxiecode Systems AB
       
 24234  * Released under LGPL License.
       
 24235  *
       
 24236  * License: http://www.tinymce.com/license
       
 24237  * Contributing: http://www.tinymce.com/contributing
       
 24238  */
       
 24239 
       
 24240 /**
       
 24241  * This class is used to create MessageBoxes like alerts/confirms etc.
       
 24242  *
       
 24243  * @class tinymce.ui.MessageBox
       
 24244  * @extends tinymce.ui.Window
       
 24245  */
       
 24246 define("tinymce/ui/MessageBox", [
       
 24247 	"tinymce/ui/Window"
       
 24248 ], function(Window) {
       
 24249 	"use strict";
       
 24250 
       
 24251 	var MessageBox = Window.extend({
       
 24252 		/**
       
 24253 		 * Constructs a instance with the specified settings.
       
 24254 		 *
       
 24255 		 * @constructor
       
 24256 		 * @param {Object} settings Name/value object with settings.
       
 24257 		 */
       
 24258 		init: function(settings) {
       
 24259 			settings = {
       
 24260 				border: 1,
       
 24261 				padding: 20,
       
 24262 				layout: 'flex',
       
 24263 				pack: "center",
       
 24264 				align: "center",
       
 24265 				containerCls: 'panel',
       
 24266 				autoScroll: true,
       
 24267 				buttons: {type: "button", text: "Ok", action: "ok"},
       
 24268 				items: {
       
 24269 					type: "label",
       
 24270 					multiline: true,
       
 24271 					maxWidth: 500,
       
 24272 					maxHeight: 200
       
 24273 				}
       
 24274 			};
       
 24275 
       
 24276 			this._super(settings);
       
 24277 		},
       
 24278 
       
 24279 		Statics: {
       
 24280 			/**
       
 24281 			 * Ok buttons constant.
       
 24282 			 *
       
 24283 			 * @static
       
 24284 			 * @final
       
 24285 			 * @field {Number} OK
       
 24286 			 */
       
 24287 			OK: 1,
       
 24288 
       
 24289 			/**
       
 24290 			 * Ok/cancel buttons constant.
       
 24291 			 *
       
 24292 			 * @static
       
 24293 			 * @final
       
 24294 			 * @field {Number} OK_CANCEL
       
 24295 			 */
       
 24296 			OK_CANCEL: 2,
       
 24297 
       
 24298 			/**
       
 24299 			 * yes/no buttons constant.
       
 24300 			 *
       
 24301 			 * @static
       
 24302 			 * @final
       
 24303 			 * @field {Number} YES_NO
       
 24304 			 */
       
 24305 			YES_NO: 3,
       
 24306 
       
 24307 			/**
       
 24308 			 * yes/no/cancel buttons constant.
       
 24309 			 *
       
 24310 			 * @static
       
 24311 			 * @final
       
 24312 			 * @field {Number} YES_NO_CANCEL
       
 24313 			 */
       
 24314 			YES_NO_CANCEL: 4,
       
 24315 
       
 24316 			/**
       
 24317 			 * Constructs a new message box and renders it to the body element.
       
 24318 			 *
       
 24319 			 * @static
       
 24320 			 * @method msgBox
       
 24321 			 * @param {Object} settings Name/value object with settings.
       
 24322 			 */
       
 24323 			msgBox: function(settings) {
       
 24324 				var buttons, callback = settings.callback || function() {};
       
 24325 
       
 24326 				function createButton(text, status, primary) {
       
 24327 					return {
       
 24328 						type: "button",
       
 24329 						text: text,
       
 24330 						subtype: primary ? 'primary' : '',
       
 24331 						onClick: function(e) {
       
 24332 							e.control.parents()[1].close();
       
 24333 							callback(status);
       
 24334 						}
       
 24335 					};
       
 24336 				}
       
 24337 
       
 24338 				switch (settings.buttons) {
       
 24339 					case MessageBox.OK_CANCEL:
       
 24340 						buttons = [
       
 24341 							createButton('Ok', true, true),
       
 24342 							createButton('Cancel', false)
       
 24343 						];
       
 24344 						break;
       
 24345 
       
 24346 					case MessageBox.YES_NO:
       
 24347 					case MessageBox.YES_NO_CANCEL:
       
 24348 						buttons = [
       
 24349 							createButton('Yes', 1, true),
       
 24350 							createButton('No', 0)
       
 24351 						];
       
 24352 
       
 24353 						if (settings.buttons == MessageBox.YES_NO_CANCEL) {
       
 24354 							buttons.push(createButton('Cancel', -1));
       
 24355 						}
       
 24356 						break;
       
 24357 
       
 24358 					default:
       
 24359 						buttons = [
       
 24360 							createButton('Ok', true, true)
       
 24361 						];
       
 24362 						break;
       
 24363 				}
       
 24364 
       
 24365 				return new Window({
       
 24366 					padding: 20,
       
 24367 					x: settings.x,
       
 24368 					y: settings.y,
       
 24369 					minWidth: 300,
       
 24370 					minHeight: 100,
       
 24371 					layout: "flex",
       
 24372 					pack: "center",
       
 24373 					align: "center",
       
 24374 					buttons: buttons,
       
 24375 					title: settings.title,
       
 24376 					role: 'alertdialog',
       
 24377 					items: {
       
 24378 						type: "label",
       
 24379 						multiline: true,
       
 24380 						maxWidth: 500,
       
 24381 						maxHeight: 200,
       
 24382 						text: settings.text
       
 24383 					},
       
 24384 					onPostRender: function() {
       
 24385 						this.aria('describedby', this.items()[0]._id);
       
 24386 					},
       
 24387 					onClose: settings.onClose,
       
 24388 					onCancel: function() {
       
 24389 						callback(false);
       
 24390 					}
       
 24391 				}).renderTo(document.body).reflow();
       
 24392 			},
       
 24393 
       
 24394 			/**
       
 24395 			 * Creates a new alert dialog.
       
 24396 			 *
       
 24397 			 * @method alert
       
 24398 			 * @param {Object} settings Settings for the alert dialog.
       
 24399 			 * @param {function} [callback] Callback to execute when the user makes a choice.
       
 24400 			 */
       
 24401 			alert: function(settings, callback) {
       
 24402 				if (typeof settings == "string") {
       
 24403 					settings = {text: settings};
       
 24404 				}
       
 24405 
       
 24406 				settings.callback = callback;
       
 24407 				return MessageBox.msgBox(settings);
       
 24408 			},
       
 24409 
       
 24410 			/**
       
 24411 			 * Creates a new confirm dialog.
       
 24412 			 *
       
 24413 			 * @method confirm
       
 24414 			 * @param {Object} settings Settings for the confirm dialog.
       
 24415 			 * @param {function} [callback] Callback to execute when the user makes a choice.
       
 24416 			 */
       
 24417 			confirm: function(settings, callback) {
       
 24418 				if (typeof settings == "string") {
       
 24419 					settings = {text: settings};
       
 24420 				}
       
 24421 
       
 24422 				settings.callback = callback;
       
 24423 				settings.buttons = MessageBox.OK_CANCEL;
       
 24424 
       
 24425 				return MessageBox.msgBox(settings);
       
 24426 			}
       
 24427 		}
       
 24428 	});
       
 24429 
       
 24430 	return MessageBox;
       
 24431 });
       
 24432 
       
 24433 // Included from: js/tinymce/classes/WindowManager.js
       
 24434 
       
 24435 /**
       
 24436  * WindowManager.js
       
 24437  *
       
 24438  * Copyright, Moxiecode Systems AB
       
 24439  * Released under LGPL License.
       
 24440  *
       
 24441  * License: http://www.tinymce.com/license
       
 24442  * Contributing: http://www.tinymce.com/contributing
       
 24443  */
       
 24444 
       
 24445 /**
       
 24446  * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs.
       
 24447  *
       
 24448  * @class tinymce.WindowManager
       
 24449  * @example
       
 24450  * // Opens a new dialog with the file.htm file and the size 320x240
       
 24451  * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
       
 24452  * tinymce.activeEditor.windowManager.open({
       
 24453  *    url: 'file.htm',
       
 24454  *    width: 320,
       
 24455  *    height: 240
       
 24456  * }, {
       
 24457  *    custom_param: 1
       
 24458  * });
       
 24459  *
       
 24460  * // Displays an alert box using the active editors window manager instance
       
 24461  * tinymce.activeEditor.windowManager.alert('Hello world!');
       
 24462  *
       
 24463  * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
       
 24464  * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
       
 24465  *    if (s)
       
 24466  *       tinymce.activeEditor.windowManager.alert("Ok");
       
 24467  *    else
       
 24468  *       tinymce.activeEditor.windowManager.alert("Cancel");
       
 24469  * });
       
 24470  */
       
 24471 define("tinymce/WindowManager", [
       
 24472 	"tinymce/ui/Window",
       
 24473 	"tinymce/ui/MessageBox"
       
 24474 ], function(Window, MessageBox) {
       
 24475 	return function(editor) {
       
 24476 		var self = this, windows = [];
       
 24477 
       
 24478 		function getTopMostWindow() {
       
 24479 			if (windows.length) {
       
 24480 				return windows[windows.length - 1];
       
 24481 			}
       
 24482 		}
       
 24483 
       
 24484 		self.windows = windows;
       
 24485 
       
 24486 		editor.on('remove', function() {
       
 24487 			var i = windows.length;
       
 24488 
       
 24489 			while (i--) {
       
 24490 				windows[i].close();
       
 24491 			}
       
 24492 		});
       
 24493 
       
 24494 		/**
       
 24495 		 * Opens a new window.
       
 24496 		 *
       
 24497 		 * @method open
       
 24498 		 * @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
       
 24499 		 * @option {String} title Window title.
       
 24500 		 * @option {String} file URL of the file to open in the window.
       
 24501 		 * @option {Number} width Width in pixels.
       
 24502 		 * @option {Number} height Height in pixels.
       
 24503 		 * @option {Boolean} autoScroll Specifies whether the popup window can have scrollbars if required (i.e. content
       
 24504 		 * larger than the popup size specified).
       
 24505 		 */
       
 24506 		self.open = function(args, params) {
       
 24507 			var win;
       
 24508 
       
 24509 			editor.editorManager.setActive(editor);
       
 24510 
       
 24511 			args.title = args.title || ' ';
       
 24512 
       
 24513 			// Handle URL
       
 24514 			args.url = args.url || args.file; // Legacy
       
 24515 			if (args.url) {
       
 24516 				args.width = parseInt(args.width || 320, 10);
       
 24517 				args.height = parseInt(args.height || 240, 10);
       
 24518 			}
       
 24519 
       
 24520 			// Handle body
       
 24521 			if (args.body) {
       
 24522 				args.items = {
       
 24523 					defaults: args.defaults,
       
 24524 					type: args.bodyType || 'form',
       
 24525 					items: args.body
       
 24526 				};
       
 24527 			}
       
 24528 
       
 24529 			if (!args.url && !args.buttons) {
       
 24530 				args.buttons = [
       
 24531 					{text: 'Ok', subtype: 'primary', onclick: function() {
       
 24532 						win.find('form')[0].submit();
       
 24533 					}},
       
 24534 
       
 24535 					{text: 'Cancel', onclick: function() {
       
 24536 						win.close();
       
 24537 					}}
       
 24538 				];
       
 24539 			}
       
 24540 
       
 24541 			win = new Window(args);
       
 24542 			windows.push(win);
       
 24543 
       
 24544 			win.on('close', function() {
       
 24545 				var i = windows.length;
       
 24546 
       
 24547 				while (i--) {
       
 24548 					if (windows[i] === win) {
       
 24549 						windows.splice(i, 1);
       
 24550 					}
       
 24551 				}
       
 24552 
       
 24553 				if (!windows.length) {
       
 24554 					editor.focus();
       
 24555 				}
       
 24556 			});
       
 24557 
       
 24558 			// Handle data
       
 24559 			if (args.data) {
       
 24560 				win.on('postRender', function() {
       
 24561 					this.find('*').each(function(ctrl) {
       
 24562 						var name = ctrl.name();
       
 24563 
       
 24564 						if (name in args.data) {
       
 24565 							ctrl.value(args.data[name]);
       
 24566 						}
       
 24567 					});
       
 24568 				});
       
 24569 			}
       
 24570 
       
 24571 			// store args and parameters
       
 24572 			win.features = args || {};
       
 24573 			win.params = params || {};
       
 24574 
       
 24575 			// Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
       
 24576 			if (windows.length === 1) {
       
 24577 				editor.nodeChanged();
       
 24578 			}
       
 24579 
       
 24580 			return win.renderTo().reflow();
       
 24581 		};
       
 24582 
       
 24583 		/**
       
 24584 		 * Creates a alert dialog. Please don't use the blocking behavior of this
       
 24585 		 * native version use the callback method instead then it can be extended.
       
 24586 		 *
       
 24587 		 * @method alert
       
 24588 		 * @param {String} message Text to display in the new alert dialog.
       
 24589 		 * @param {function} callback Callback function to be executed after the user has selected ok.
       
 24590 		 * @param {Object} scope Optional scope to execute the callback in.
       
 24591 		 * @example
       
 24592 		 * // Displays an alert box using the active editors window manager instance
       
 24593 		 * tinymce.activeEditor.windowManager.alert('Hello world!');
       
 24594 		 */
       
 24595 		self.alert = function(message, callback, scope) {
       
 24596 			MessageBox.alert(message, function() {
       
 24597 				if (callback) {
       
 24598 					callback.call(scope || this);
       
 24599 				} else {
       
 24600 					editor.focus();
       
 24601 				}
       
 24602 			});
       
 24603 		};
       
 24604 
       
 24605 		/**
       
 24606 		 * Creates a confirm dialog. Please don't use the blocking behavior of this
       
 24607 		 * native version use the callback method instead then it can be extended.
       
 24608 		 *
       
 24609 		 * @method confirm
       
 24610 		 * @param {String} messageText to display in the new confirm dialog.
       
 24611 		 * @param {function} callback Callback function to be executed after the user has selected ok or cancel.
       
 24612 		 * @param {Object} scope Optional scope to execute the callback in.
       
 24613 		 * @example
       
 24614 		 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
       
 24615 		 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
       
 24616 		 *    if (s)
       
 24617 		 *       tinymce.activeEditor.windowManager.alert("Ok");
       
 24618 		 *    else
       
 24619 		 *       tinymce.activeEditor.windowManager.alert("Cancel");
       
 24620 		 * });
       
 24621 		 */
       
 24622 		self.confirm = function(message, callback, scope) {
       
 24623 			MessageBox.confirm(message, function(state) {
       
 24624 				callback.call(scope || this, state);
       
 24625 			});
       
 24626 		};
       
 24627 
       
 24628 		/**
       
 24629 		 * Closes the top most window.
       
 24630 		 *
       
 24631 		 * @method close
       
 24632 		 */
       
 24633 		self.close = function() {
       
 24634 			if (getTopMostWindow()) {
       
 24635 				getTopMostWindow().close();
       
 24636 			}
       
 24637 		};
       
 24638 
       
 24639 		/**
       
 24640 		 * Returns the params of the last window open call. This can be used in iframe based
       
 24641 		 * dialog to get params passed from the tinymce plugin.
       
 24642 		 *
       
 24643 		 * @example
       
 24644 		 * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams();
       
 24645 		 *
       
 24646 		 * @method getParams
       
 24647 		 * @return {Object} Name/value object with parameters passed from windowManager.open call.
       
 24648 		 */
       
 24649 		self.getParams = function() {
       
 24650 			return getTopMostWindow() ? getTopMostWindow().params : null;
       
 24651 		};
       
 24652 
       
 24653 		/**
       
 24654 		 * Sets the params of the last opened window.
       
 24655 		 *
       
 24656 		 * @method setParams
       
 24657 		 * @param {Object} params Params object to set for the last opened window.
       
 24658 		 */
       
 24659 		self.setParams = function(params) {
       
 24660 			if (getTopMostWindow()) {
       
 24661 				getTopMostWindow().params = params;
       
 24662 			}
       
 24663 		};
       
 24664 
       
 24665 		/**
       
 24666 		 * Returns the currently opened window objects.
       
 24667 		 *
       
 24668 		 * @method getWindows
       
 24669 		 * @return {Array} Array of the currently opened windows.
       
 24670 		 */
       
 24671 		self.getWindows = function() {
       
 24672 			return windows;
       
 24673 		};
       
 24674 	};
       
 24675 });
       
 24676 
       
 24677 // Included from: js/tinymce/classes/util/Quirks.js
       
 24678 
       
 24679 /**
       
 24680  * Quirks.js
       
 24681  *
       
 24682  * Copyright, Moxiecode Systems AB
       
 24683  * Released under LGPL License.
       
 24684  *
       
 24685  * License: http://www.tinymce.com/license
       
 24686  * Contributing: http://www.tinymce.com/contributing
       
 24687  *
       
 24688  * @ignore-file
       
 24689  */
       
 24690 
       
 24691 /**
       
 24692  * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
       
 24693  *
       
 24694  * @class tinymce.util.Quirks
       
 24695  */
       
 24696 define("tinymce/util/Quirks", [
       
 24697 	"tinymce/util/VK",
       
 24698 	"tinymce/dom/RangeUtils",
       
 24699 	"tinymce/dom/TreeWalker",
       
 24700 	"tinymce/html/Node",
       
 24701 	"tinymce/html/Entities",
       
 24702 	"tinymce/Env",
       
 24703 	"tinymce/util/Tools"
       
 24704 ], function(VK, RangeUtils, TreeWalker, Node, Entities, Env, Tools) {
       
 24705 	return function(editor) {
       
 24706 		var each = Tools.each, $ = editor.$;
       
 24707 		var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
       
 24708 			settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
       
 24709 		var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
       
 24710 		var mceInternalUrlPrefix = 'data:text/mce-internal,';
       
 24711 		var mceInternalDataType = isIE ? 'Text' : 'URL';
       
 24712 
       
 24713 		/**
       
 24714 		 * Executes a command with a specific state this can be to enable/disable browser editing features.
       
 24715 		 */
       
 24716 		function setEditorCommandState(cmd, state) {
       
 24717 			try {
       
 24718 				editor.getDoc().execCommand(cmd, false, state);
       
 24719 			} catch (ex) {
       
 24720 				// Ignore
       
 24721 			}
       
 24722 		}
       
 24723 
       
 24724 		/**
       
 24725 		 * Returns current IE document mode.
       
 24726 		 */
       
 24727 		function getDocumentMode() {
       
 24728 			var documentMode = editor.getDoc().documentMode;
       
 24729 
       
 24730 			return documentMode ? documentMode : 6;
       
 24731 		}
       
 24732 
       
 24733 		/**
       
 24734 		 * Returns true/false if the event is prevented or not.
       
 24735 		 *
       
 24736 		 * @private
       
 24737 		 * @param {Event} e Event object.
       
 24738 		 * @return {Boolean} true/false if the event is prevented or not.
       
 24739 		 */
       
 24740 		function isDefaultPrevented(e) {
       
 24741 			return e.isDefaultPrevented();
       
 24742 		}
       
 24743 
       
 24744 		/**
       
 24745 		 * Sets Text/URL data on the event's dataTransfer object to a special data:text/mce-internal url.
       
 24746 		 * This is to workaround the inability to set custom contentType on IE and Safari.
       
 24747 		 * The editor's selected content is encoded into this url so drag and drop between editors will work.
       
 24748 		 *
       
 24749 		 * @private
       
 24750 		 * @param {DragEvent} e Event object
       
 24751 		 */
       
 24752 		function setMceInteralContent(e) {
       
 24753 			var selectionHtml;
       
 24754 
       
 24755 			if (e.dataTransfer) {
       
 24756 				if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') {
       
 24757 					selection.select(e.target);
       
 24758 				}
       
 24759 
       
 24760 				selectionHtml = editor.selection.getContent();
       
 24761 
       
 24762 				// Safari/IE doesn't support custom dataTransfer items so we can only use URL and Text
       
 24763 				if (selectionHtml.length > 0) {
       
 24764 					e.dataTransfer.setData(mceInternalDataType, mceInternalUrlPrefix + escape(selectionHtml));
       
 24765 				}
       
 24766 			}
       
 24767 		}
       
 24768 
       
 24769 		/**
       
 24770 		 * Gets content of special data:text/mce-internal url on the event's dataTransfer object.
       
 24771 		 * This is to workaround the inability to set custom contentType on IE and Safari.
       
 24772 		 * The editor's selected content is encoded into this url so drag and drop between editors will work.
       
 24773 		 *
       
 24774 		 * @private
       
 24775 		 * @param {DragEvent} e Event object
       
 24776 		 * @returns {String} mce-internal content
       
 24777 		 */
       
 24778 		function getMceInternalContent(e) {
       
 24779 			var internalContent, content;
       
 24780 
       
 24781 			if (e.dataTransfer) {
       
 24782 				internalContent = e.dataTransfer.getData(mceInternalDataType);
       
 24783 
       
 24784 				if (internalContent && internalContent.indexOf(mceInternalUrlPrefix) >= 0) {
       
 24785 					content = unescape(internalContent.substr(mceInternalUrlPrefix.length));
       
 24786 				}
       
 24787 			}
       
 24788 
       
 24789 			return content;
       
 24790 		}
       
 24791 
       
 24792 		/**
       
 24793 		 * Inserts contents using the paste clipboard command if it's available if it isn't it will fallback
       
 24794 		 * to the core command.
       
 24795 		 *
       
 24796 		 * @private
       
 24797 		 * @param {String} content Content to insert at selection.
       
 24798 		 */
       
 24799 		function insertClipboardContents(content) {
       
 24800 			if (editor.queryCommandSupported('mceInsertClipboardContent')) {
       
 24801 				editor.execCommand('mceInsertClipboardContent', false, {content: content});
       
 24802 			} else {
       
 24803 				editor.execCommand('mceInsertContent', false, content);
       
 24804 			}
       
 24805 		}
       
 24806 
       
 24807 		/**
       
 24808 		 * Fixes a WebKit bug when deleting contents using backspace or delete key.
       
 24809 		 * WebKit will produce a span element if you delete across two block elements.
       
 24810 		 *
       
 24811 		 * Example:
       
 24812 		 * <h1>a</h1><p>|b</p>
       
 24813 		 *
       
 24814 		 * Will produce this on backspace:
       
 24815 		 * <h1>a<span style="<all runtime styles>">b</span></p>
       
 24816 		 *
       
 24817 		 * This fixes the backspace to produce:
       
 24818 		 * <h1>a|b</p>
       
 24819 		 *
       
 24820 		 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
       
 24821 		 *
       
 24822 		 * This fixes the following delete scenarios:
       
 24823 		 *  1. Delete by pressing backspace key.
       
 24824 		 *  2. Delete by pressing delete key.
       
 24825 		 *  3. Delete by pressing backspace key with ctrl/cmd (Word delete).
       
 24826 		 *  4. Delete by pressing delete key with ctrl/cmd (Word delete).
       
 24827 		 *  5. Delete by drag/dropping contents inside the editor.
       
 24828 		 *  6. Delete by using Cut Ctrl+X/Cmd+X.
       
 24829 		 *  7. Delete by selecting contents and writing a character.
       
 24830 		 *
       
 24831 		 * This code is a ugly hack since writing full custom delete logic for just this bug
       
 24832 		 * fix seemed like a huge task. I hope we can remove this before the year 2030.
       
 24833 		 */
       
 24834 		function cleanupStylesWhenDeleting() {
       
 24835 			var doc = editor.getDoc(), dom = editor.dom, selection = editor.selection;
       
 24836 			var MutationObserver = window.MutationObserver, olderWebKit, dragStartRng;
       
 24837 
       
 24838 			// Add mini polyfill for older WebKits
       
 24839 			// TODO: Remove this when old Safari versions gets updated
       
 24840 			if (!MutationObserver) {
       
 24841 				olderWebKit = true;
       
 24842 
       
 24843 				MutationObserver = function() {
       
 24844 					var records = [], target;
       
 24845 
       
 24846 					function nodeInsert(e) {
       
 24847 						var target = e.relatedNode || e.target;
       
 24848 						records.push({target: target, addedNodes: [target]});
       
 24849 					}
       
 24850 
       
 24851 					function attrModified(e) {
       
 24852 						var target = e.relatedNode || e.target;
       
 24853 						records.push({target: target, attributeName: e.attrName});
       
 24854 					}
       
 24855 
       
 24856 					this.observe = function(node) {
       
 24857 						target = node;
       
 24858 						target.addEventListener('DOMSubtreeModified', nodeInsert, false);
       
 24859 						target.addEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
       
 24860 						target.addEventListener('DOMNodeInserted', nodeInsert, false);
       
 24861 						target.addEventListener('DOMAttrModified', attrModified, false);
       
 24862 					};
       
 24863 
       
 24864 					this.disconnect = function() {
       
 24865 						target.removeEventListener('DOMSubtreeModified', nodeInsert, false);
       
 24866 						target.removeEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
       
 24867 						target.removeEventListener('DOMNodeInserted', nodeInsert, false);
       
 24868 						target.removeEventListener('DOMAttrModified', attrModified, false);
       
 24869 					};
       
 24870 
       
 24871 					this.takeRecords = function() {
       
 24872 						return records;
       
 24873 					};
       
 24874 				};
       
 24875 			}
       
 24876 
       
 24877 			function isTrailingBr(node) {
       
 24878 				var blockElements = dom.schema.getBlockElements(), rootNode = editor.getBody();
       
 24879 
       
 24880 				if (node.nodeName != 'BR') {
       
 24881 					return false;
       
 24882 				}
       
 24883 
       
 24884 				for (node = node; node != rootNode && !blockElements[node.nodeName]; node = node.parentNode) {
       
 24885 					if (node.nextSibling) {
       
 24886 						return false;
       
 24887 					}
       
 24888 				}
       
 24889 
       
 24890 				return true;
       
 24891 			}
       
 24892 
       
 24893 			function isSiblingsIgnoreWhiteSpace(node1, node2) {
       
 24894 				var node;
       
 24895 
       
 24896 				for (node = node1.nextSibling; node && node != node2; node = node.nextSibling) {
       
 24897 					if (node.nodeType == 3 && $.trim(node.data).length === 0) {
       
 24898 						continue;
       
 24899 					}
       
 24900 
       
 24901 					if (node !== node2) {
       
 24902 						return false;
       
 24903 					}
       
 24904 				}
       
 24905 
       
 24906 				return node === node2;
       
 24907 			}
       
 24908 
       
 24909 			function findCaretNode(node, forward, startNode) {
       
 24910 				var walker, current, nonEmptyElements;
       
 24911 
       
 24912 				nonEmptyElements = dom.schema.getNonEmptyElements();
       
 24913 
       
 24914 				walker = new TreeWalker(startNode || node, node);
       
 24915 
       
 24916 				while ((current = walker[forward ? 'next' : 'prev']())) {
       
 24917 					if (nonEmptyElements[current.nodeName] && !isTrailingBr(current)) {
       
 24918 						return current;
       
 24919 					}
       
 24920 
       
 24921 					if (current.nodeType == 3 && current.data.length > 0) {
       
 24922 						return current;
       
 24923 					}
       
 24924 				}
       
 24925 			}
       
 24926 
       
 24927 			function deleteRangeBetweenTextBlocks(rng) {
       
 24928 				var startBlock, endBlock, caretNodeBefore, caretNodeAfter, textBlockElements;
       
 24929 
       
 24930 				if (rng.collapsed) {
       
 24931 					return;
       
 24932 				}
       
 24933 
       
 24934 				startBlock = dom.getParent(RangeUtils.getNode(rng.startContainer, rng.startOffset), dom.isBlock);
       
 24935 				endBlock = dom.getParent(RangeUtils.getNode(rng.endContainer, rng.endOffset), dom.isBlock);
       
 24936 				textBlockElements = editor.schema.getTextBlockElements();
       
 24937 
       
 24938 				if (startBlock == endBlock) {
       
 24939 					return;
       
 24940 				}
       
 24941 
       
 24942 				if (!textBlockElements[startBlock.nodeName] || !textBlockElements[endBlock.nodeName]) {
       
 24943 					return;
       
 24944 				}
       
 24945 
       
 24946 				if (dom.getContentEditable(startBlock) === "false" || dom.getContentEditable(endBlock) === "false") {
       
 24947 					return;
       
 24948 				}
       
 24949 
       
 24950 				rng.deleteContents();
       
 24951 
       
 24952 				caretNodeBefore = findCaretNode(startBlock, false);
       
 24953 				caretNodeAfter = findCaretNode(endBlock, true);
       
 24954 
       
 24955 				if (!dom.isEmpty(endBlock)) {
       
 24956 					$(startBlock).append(endBlock.childNodes);
       
 24957 				}
       
 24958 
       
 24959 				$(endBlock).remove();
       
 24960 
       
 24961 				if (caretNodeBefore) {
       
 24962 					if (caretNodeBefore.nodeType == 1) {
       
 24963 						if (caretNodeBefore.nodeName == "BR") {
       
 24964 							rng.setStartBefore(caretNodeBefore);
       
 24965 							rng.setEndBefore(caretNodeBefore);
       
 24966 						} else {
       
 24967 							rng.setStartAfter(caretNodeBefore);
       
 24968 							rng.setEndAfter(caretNodeBefore);
       
 24969 						}
       
 24970 					} else {
       
 24971 						rng.setStart(caretNodeBefore, caretNodeBefore.data.length);
       
 24972 						rng.setEnd(caretNodeBefore, caretNodeBefore.data.length);
       
 24973 					}
       
 24974 				} else if (caretNodeAfter) {
       
 24975 					if (caretNodeAfter.nodeType == 1) {
       
 24976 						rng.setStartBefore(caretNodeAfter);
       
 24977 						rng.setEndBefore(caretNodeAfter);
       
 24978 					} else {
       
 24979 						rng.setStart(caretNodeAfter, 0);
       
 24980 						rng.setEnd(caretNodeAfter, 0);
       
 24981 					}
       
 24982 				}
       
 24983 
       
 24984 				selection.setRng(rng);
       
 24985 
       
 24986 				return true;
       
 24987 			}
       
 24988 
       
 24989 			function expandBetweenBlocks(rng, isForward) {
       
 24990 				var caretNode, targetCaretNode, textBlock, targetTextBlock, container, offset;
       
 24991 
       
 24992 				if (!rng.collapsed) {
       
 24993 					return rng;
       
 24994 				}
       
 24995 
       
 24996 				container = rng.startContainer;
       
 24997 				offset = rng.startOffset;
       
 24998 
       
 24999 				if (container.nodeType == 3) {
       
 25000 					if (isForward) {
       
 25001 						if (offset < container.data.length) {
       
 25002 							return rng;
       
 25003 						}
       
 25004 					} else {
       
 25005 						if (offset > 0) {
       
 25006 							return rng;
       
 25007 						}
       
 25008 					}
       
 25009 				}
       
 25010 
       
 25011 				caretNode = RangeUtils.getNode(rng.startContainer, rng.startOffset);
       
 25012 				textBlock = dom.getParent(caretNode, dom.isBlock);
       
 25013 				targetCaretNode = findCaretNode(editor.getBody(), isForward, caretNode);
       
 25014 				targetTextBlock = dom.getParent(targetCaretNode, dom.isBlock);
       
 25015 
       
 25016 				if (!caretNode || !targetCaretNode) {
       
 25017 					return rng;
       
 25018 				}
       
 25019 
       
 25020 				if (targetTextBlock && textBlock != targetTextBlock) {
       
 25021 					if (!isForward) {
       
 25022 						if (!isSiblingsIgnoreWhiteSpace(targetTextBlock, textBlock)) {
       
 25023 							return rng;
       
 25024 						}
       
 25025 
       
 25026 						if (targetCaretNode.nodeType == 1) {
       
 25027 							if (targetCaretNode.nodeName == "BR") {
       
 25028 								rng.setStartBefore(targetCaretNode);
       
 25029 							} else {
       
 25030 								rng.setStartAfter(targetCaretNode);
       
 25031 							}
       
 25032 						} else {
       
 25033 							rng.setStart(targetCaretNode, targetCaretNode.data.length);
       
 25034 						}
       
 25035 
       
 25036 						if (caretNode.nodeType == 1) {
       
 25037 							rng.setEnd(caretNode, 0);
       
 25038 						} else {
       
 25039 							rng.setEndBefore(caretNode);
       
 25040 						}
       
 25041 					} else {
       
 25042 						if (!isSiblingsIgnoreWhiteSpace(textBlock, targetTextBlock)) {
       
 25043 							return rng;
       
 25044 						}
       
 25045 
       
 25046 						if (caretNode.nodeType == 1) {
       
 25047 							if (caretNode.nodeName == "BR") {
       
 25048 								rng.setStartBefore(caretNode);
       
 25049 							} else {
       
 25050 								rng.setStartAfter(caretNode);
       
 25051 							}
       
 25052 						} else {
       
 25053 							rng.setStart(caretNode, caretNode.data.length);
       
 25054 						}
       
 25055 
       
 25056 						if (targetCaretNode.nodeType == 1) {
       
 25057 							rng.setEnd(targetCaretNode, 0);
       
 25058 						} else {
       
 25059 							rng.setEndBefore(targetCaretNode);
       
 25060 						}
       
 25061 					}
       
 25062 				}
       
 25063 
       
 25064 				return rng;
       
 25065 			}
       
 25066 
       
 25067 			function handleTextBlockMergeDelete(isForward) {
       
 25068 				var rng = selection.getRng();
       
 25069 
       
 25070 				rng = expandBetweenBlocks(rng, isForward);
       
 25071 
       
 25072 				if (deleteRangeBetweenTextBlocks(rng)) {
       
 25073 					return true;
       
 25074 				}
       
 25075 			}
       
 25076 
       
 25077 			function customDelete(isForward) {
       
 25078 				var mutationObserver, rng, caretElement;
       
 25079 
       
 25080 				if (handleTextBlockMergeDelete(isForward)) {
       
 25081 					return;
       
 25082 				}
       
 25083 
       
 25084 				Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
       
 25085 					// Mark existing spans
       
 25086 					if (elm.tagName == 'SPAN') {
       
 25087 						elm.setAttribute('mce-data-marked', 1);
       
 25088 					}
       
 25089 
       
 25090 					// Make sure all elements has a data-mce-style attribute
       
 25091 					if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
       
 25092 						editor.dom.setAttrib(elm, 'style', editor.dom.getAttrib(elm, 'style'));
       
 25093 					}
       
 25094 				});
       
 25095 
       
 25096 				// Observe added nodes and style attribute changes
       
 25097 				mutationObserver = new MutationObserver(function() {});
       
 25098 				mutationObserver.observe(editor.getDoc(), {
       
 25099 					childList: true,
       
 25100 					attributes: true,
       
 25101 					subtree: true,
       
 25102 					attributeFilter: ['style']
       
 25103 				});
       
 25104 
       
 25105 				editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
       
 25106 
       
 25107 				rng = editor.selection.getRng();
       
 25108 				caretElement = rng.startContainer.parentNode;
       
 25109 
       
 25110 				Tools.each(mutationObserver.takeRecords(), function(record) {
       
 25111 					if (!dom.isChildOf(record.target, editor.getBody())) {
       
 25112 						return;
       
 25113 					}
       
 25114 
       
 25115 					// Restore style attribute to previous value
       
 25116 					if (record.attributeName == "style") {
       
 25117 						var oldValue = record.target.getAttribute('data-mce-style');
       
 25118 
       
 25119 						if (oldValue) {
       
 25120 							record.target.setAttribute("style", oldValue);
       
 25121 						} else {
       
 25122 							record.target.removeAttribute("style");
       
 25123 						}
       
 25124 					}
       
 25125 
       
 25126 					// Remove all spans that isn't maked and retain selection
       
 25127 					Tools.each(record.addedNodes, function(node) {
       
 25128 						if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
       
 25129 							var offset, container;
       
 25130 
       
 25131 							if (node == caretElement) {
       
 25132 								offset = rng.startOffset;
       
 25133 								container = node.firstChild;
       
 25134 							}
       
 25135 
       
 25136 							dom.remove(node, true);
       
 25137 
       
 25138 							if (container) {
       
 25139 								rng.setStart(container, offset);
       
 25140 								rng.setEnd(container, offset);
       
 25141 								editor.selection.setRng(rng);
       
 25142 							}
       
 25143 						}
       
 25144 					});
       
 25145 				});
       
 25146 
       
 25147 				mutationObserver.disconnect();
       
 25148 
       
 25149 				// Remove any left over marks
       
 25150 				Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
       
 25151 					span.removeAttribute('mce-data-marked');
       
 25152 				});
       
 25153 			}
       
 25154 
       
 25155 			editor.on('keydown', function(e) {
       
 25156 				var isForward = e.keyCode == DELETE, isMetaOrCtrl = e.ctrlKey || e.metaKey;
       
 25157 
       
 25158 				if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
       
 25159 					var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
       
 25160 
       
 25161 					// Ignore non meta delete in the where there is text before/after the caret
       
 25162 					if (!isMetaOrCtrl && rng.collapsed && container.nodeType == 3) {
       
 25163 						if (isForward ? offset < container.data.length : offset > 0) {
       
 25164 							return;
       
 25165 						}
       
 25166 					}
       
 25167 
       
 25168 					e.preventDefault();
       
 25169 
       
 25170 					if (isMetaOrCtrl) {
       
 25171 						editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", e.metaKey ? "lineboundary" : "word");
       
 25172 					}
       
 25173 
       
 25174 					customDelete(isForward);
       
 25175 				}
       
 25176 			});
       
 25177 
       
 25178 			// Handle case where text is deleted by typing over
       
 25179 			editor.on('keypress', function(e) {
       
 25180 				if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode && !VK.metaKeyPressed(e)) {
       
 25181 					var rng, currentFormatNodes, fragmentNode, blockParent, caretNode, charText;
       
 25182 
       
 25183 					rng = editor.selection.getRng();
       
 25184 					charText = String.fromCharCode(e.charCode);
       
 25185 					e.preventDefault();
       
 25186 
       
 25187 					// Keep track of current format nodes
       
 25188 					currentFormatNodes = $(rng.startContainer).parents().filter(function(idx, node) {
       
 25189 						return !!editor.schema.getTextInlineElements()[node.nodeName];
       
 25190 					});
       
 25191 
       
 25192 					customDelete(true);
       
 25193 
       
 25194 					// Check if the browser removed them
       
 25195 					currentFormatNodes = currentFormatNodes.filter(function(idx, node) {
       
 25196 						return !$.contains(editor.getBody(), node);
       
 25197 					});
       
 25198 
       
 25199 					// Then re-add them
       
 25200 					if (currentFormatNodes.length) {
       
 25201 						fragmentNode = dom.createFragment();
       
 25202 
       
 25203 						currentFormatNodes.each(function(idx, formatNode) {
       
 25204 							formatNode = formatNode.cloneNode(false);
       
 25205 
       
 25206 							if (fragmentNode.hasChildNodes()) {
       
 25207 								formatNode.appendChild(fragmentNode.firstChild);
       
 25208 								fragmentNode.appendChild(formatNode);
       
 25209 							} else {
       
 25210 								caretNode = formatNode;
       
 25211 								fragmentNode.appendChild(formatNode);
       
 25212 							}
       
 25213 
       
 25214 							fragmentNode.appendChild(formatNode);
       
 25215 						});
       
 25216 
       
 25217 						caretNode.appendChild(editor.getDoc().createTextNode(charText));
       
 25218 
       
 25219 						// Prevent edge case where older WebKit would add an extra BR element
       
 25220 						blockParent = dom.getParent(rng.startContainer, dom.isBlock);
       
 25221 						if (dom.isEmpty(blockParent)) {
       
 25222 							$(blockParent).empty().append(fragmentNode);
       
 25223 						} else {
       
 25224 							rng.insertNode(fragmentNode);
       
 25225 						}
       
 25226 
       
 25227 						rng.setStart(caretNode.firstChild, 1);
       
 25228 						rng.setEnd(caretNode.firstChild, 1);
       
 25229 						editor.selection.setRng(rng);
       
 25230 					} else {
       
 25231 						editor.selection.setContent(charText);
       
 25232 					}
       
 25233 				}
       
 25234 			});
       
 25235 
       
 25236 			editor.addCommand('Delete', function() {
       
 25237 				customDelete();
       
 25238 			});
       
 25239 
       
 25240 			editor.addCommand('ForwardDelete', function() {
       
 25241 				customDelete(true);
       
 25242 			});
       
 25243 
       
 25244 			// Older WebKits doesn't properly handle the clipboard so we can't add the rest
       
 25245 			if (olderWebKit) {
       
 25246 				return;
       
 25247 			}
       
 25248 
       
 25249 			editor.on('dragstart', function(e) {
       
 25250 				dragStartRng = selection.getRng();
       
 25251 				setMceInteralContent(e);
       
 25252 			});
       
 25253 
       
 25254 			editor.on('drop', function(e) {
       
 25255 				if (!isDefaultPrevented(e)) {
       
 25256 					var internalContent = getMceInternalContent(e);
       
 25257 					if (internalContent) {
       
 25258 						e.preventDefault();
       
 25259 
       
 25260 						// Safari has a weird issue where drag/dropping images sometimes
       
 25261 						// produces a green plus icon. When this happens the caretRangeFromPoint
       
 25262 						// will return "null" even though the x, y coordinate is correct.
       
 25263 						// But if we detach the insert from the drop event we will get a proper range
       
 25264 						window.setTimeout(function() {
       
 25265 							var pointRng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, doc);
       
 25266 
       
 25267 							if (dragStartRng) {
       
 25268 								selection.setRng(dragStartRng);
       
 25269 								dragStartRng = null;
       
 25270 							}
       
 25271 
       
 25272 							customDelete();
       
 25273 							selection.setRng(pointRng);
       
 25274 							insertClipboardContents(internalContent);
       
 25275 						}, 0);
       
 25276 					}
       
 25277 				}
       
 25278 			});
       
 25279 
       
 25280 			editor.on('cut', function(e) {
       
 25281 				if (!isDefaultPrevented(e) && e.clipboardData) {
       
 25282 					e.preventDefault();
       
 25283 					e.clipboardData.clearData();
       
 25284 					e.clipboardData.setData('text/html', editor.selection.getContent());
       
 25285 					e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
       
 25286 					customDelete(true);
       
 25287 				}
       
 25288 			});
       
 25289 		}
       
 25290 
       
 25291 		/**
       
 25292 		 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
       
 25293 		 *
       
 25294 		 * For example:
       
 25295 		 * <p><b>|</b></p>
       
 25296 		 *
       
 25297 		 * Or:
       
 25298 		 * <h1>|</h1>
       
 25299 		 *
       
 25300 		 * Or:
       
 25301 		 * [<h1></h1>]
       
 25302 		 */
       
 25303 		function emptyEditorWhenDeleting() {
       
 25304 			function serializeRng(rng) {
       
 25305 				var body = dom.create("body");
       
 25306 				var contents = rng.cloneContents();
       
 25307 				body.appendChild(contents);
       
 25308 				return selection.serializer.serialize(body, {format: 'html'});
       
 25309 			}
       
 25310 
       
 25311 			function allContentsSelected(rng) {
       
 25312 				if (!rng.setStart) {
       
 25313 					if (rng.item) {
       
 25314 						return false;
       
 25315 					}
       
 25316 
       
 25317 					var bodyRng = rng.duplicate();
       
 25318 					bodyRng.moveToElementText(editor.getBody());
       
 25319 					return RangeUtils.compareRanges(rng, bodyRng);
       
 25320 				}
       
 25321 
       
 25322 				var selection = serializeRng(rng);
       
 25323 
       
 25324 				var allRng = dom.createRng();
       
 25325 				allRng.selectNode(editor.getBody());
       
 25326 
       
 25327 				var allSelection = serializeRng(allRng);
       
 25328 				return selection === allSelection;
       
 25329 			}
       
 25330 
       
 25331 			editor.on('keydown', function(e) {
       
 25332 				var keyCode = e.keyCode, isCollapsed, body;
       
 25333 
       
 25334 				// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
       
 25335 				if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
       
 25336 					isCollapsed = editor.selection.isCollapsed();
       
 25337 					body = editor.getBody();
       
 25338 
       
 25339 					// Selection is collapsed but the editor isn't empty
       
 25340 					if (isCollapsed && !dom.isEmpty(body)) {
       
 25341 						return;
       
 25342 					}
       
 25343 
       
 25344 					// Selection isn't collapsed but not all the contents is selected
       
 25345 					if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
       
 25346 						return;
       
 25347 					}
       
 25348 
       
 25349 					// Manually empty the editor
       
 25350 					e.preventDefault();
       
 25351 					editor.setContent('');
       
 25352 
       
 25353 					if (body.firstChild && dom.isBlock(body.firstChild)) {
       
 25354 						editor.selection.setCursorLocation(body.firstChild, 0);
       
 25355 					} else {
       
 25356 						editor.selection.setCursorLocation(body, 0);
       
 25357 					}
       
 25358 
       
 25359 					editor.nodeChanged();
       
 25360 				}
       
 25361 			});
       
 25362 		}
       
 25363 
       
 25364 		/**
       
 25365 		 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
       
 25366 		 * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
       
 25367 		 * This selects the whole body so that backspace/delete logic will delete everything
       
 25368 		 */
       
 25369 		function selectAll() {
       
 25370 			editor.shortcuts.add('meta+a', null, 'SelectAll');
       
 25371 		}
       
 25372 
       
 25373 		/**
       
 25374 		 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
       
 25375 		 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
       
 25376 		 *
       
 25377 		 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
       
 25378 		 * you enter a character into the editor.
       
 25379 		 *
       
 25380 		 * It also happens when the first focus in made to the body.
       
 25381 		 *
       
 25382 		 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
       
 25383 		 */
       
 25384 		function inputMethodFocus() {
       
 25385 			if (!editor.settings.content_editable) {
       
 25386 				// Case 1 IME doesn't initialize if you focus the document
       
 25387 				dom.bind(editor.getDoc(), 'focusin', function() {
       
 25388 					selection.setRng(selection.getRng());
       
 25389 				});
       
 25390 
       
 25391 				// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
       
 25392 				// Needs to be both down/up due to weird rendering bug on Chrome Windows
       
 25393 				dom.bind(editor.getDoc(), 'mousedown mouseup', function(e) {
       
 25394 					if (e.target == editor.getDoc().documentElement) {
       
 25395 						editor.getBody().focus();
       
 25396 
       
 25397 						if (e.type == 'mousedown') {
       
 25398 							// Edge case for mousedown, drag select and mousedown again within selection on Chrome Windows to render caret
       
 25399 							selection.placeCaretAt(e.clientX, e.clientY);
       
 25400 						} else {
       
 25401 							selection.setRng(selection.getRng());
       
 25402 						}
       
 25403 					}
       
 25404 				});
       
 25405 			}
       
 25406 		}
       
 25407 
       
 25408 		/**
       
 25409 		 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
       
 25410 		 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
       
 25411 		 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
       
 25412 		 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
       
 25413 		 * browsers.
       
 25414 		 *
       
 25415 		 * It also fixes a bug on Firefox where it's impossible to delete HR elements.
       
 25416 		 */
       
 25417 		function removeHrOnBackspace() {
       
 25418 			editor.on('keydown', function(e) {
       
 25419 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
       
 25420 					// Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow
       
 25421 					if (!editor.getBody().getElementsByTagName('hr').length) {
       
 25422 						return;
       
 25423 					}
       
 25424 
       
 25425 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
       
 25426 						var node = selection.getNode();
       
 25427 						var previousSibling = node.previousSibling;
       
 25428 
       
 25429 						if (node.nodeName == 'HR') {
       
 25430 							dom.remove(node);
       
 25431 							e.preventDefault();
       
 25432 							return;
       
 25433 						}
       
 25434 
       
 25435 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
       
 25436 							dom.remove(previousSibling);
       
 25437 							e.preventDefault();
       
 25438 						}
       
 25439 					}
       
 25440 				}
       
 25441 			});
       
 25442 		}
       
 25443 
       
 25444 		/**
       
 25445 		 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
       
 25446 		 * side it's rectangle.
       
 25447 		 */
       
 25448 		function focusBody() {
       
 25449 			// Fix for a focus bug in FF 3.x where the body element
       
 25450 			// wouldn't get proper focus if the user clicked on the HTML element
       
 25451 			if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
       
 25452 				editor.on('mousedown', function(e) {
       
 25453 					if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
       
 25454 						var body = editor.getBody();
       
 25455 
       
 25456 						// Blur the body it's focused but not correctly focused
       
 25457 						body.blur();
       
 25458 
       
 25459 						// Refocus the body after a little while
       
 25460 						setTimeout(function() {
       
 25461 							body.focus();
       
 25462 						}, 0);
       
 25463 					}
       
 25464 				});
       
 25465 			}
       
 25466 		}
       
 25467 
       
 25468 		/**
       
 25469 		 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
       
 25470 		 * by clicking on them so we need to fake that.
       
 25471 		 */
       
 25472 		function selectControlElements() {
       
 25473 			editor.on('click', function(e) {
       
 25474 				var target = e.target;
       
 25475 
       
 25476 				// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
       
 25477 				// WebKit can't even do simple things like selecting an image
       
 25478 				// Needs to be the setBaseAndExtend or it will fail to select floated images
       
 25479 				if (/^(IMG|HR)$/.test(target.nodeName)) {
       
 25480 					e.preventDefault();
       
 25481 					selection.getSel().setBaseAndExtent(target, 0, target, 1);
       
 25482 					editor.nodeChanged();
       
 25483 				}
       
 25484 
       
 25485 				if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) {
       
 25486 					e.preventDefault();
       
 25487 					selection.select(target);
       
 25488 				}
       
 25489 			});
       
 25490 		}
       
 25491 
       
 25492 		/**
       
 25493 		 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
       
 25494 		 *
       
 25495 		 * Fixes do backspace/delete on this:
       
 25496 		 * <p>bla[ck</p><p style="color:red">r]ed</p>
       
 25497 		 *
       
 25498 		 * Would become:
       
 25499 		 * <p>bla|ed</p>
       
 25500 		 *
       
 25501 		 * Instead of:
       
 25502 		 * <p style="color:red">bla|ed</p>
       
 25503 		 */
       
 25504 		function removeStylesWhenDeletingAcrossBlockElements() {
       
 25505 			function getAttributeApplyFunction() {
       
 25506 				var template = dom.getAttribs(selection.getStart().cloneNode(false));
       
 25507 
       
 25508 				return function() {
       
 25509 					var target = selection.getStart();
       
 25510 
       
 25511 					if (target !== editor.getBody()) {
       
 25512 						dom.setAttrib(target, "style", null);
       
 25513 
       
 25514 						each(template, function(attr) {
       
 25515 							target.setAttributeNode(attr.cloneNode(true));
       
 25516 						});
       
 25517 					}
       
 25518 				};
       
 25519 			}
       
 25520 
       
 25521 			function isSelectionAcrossElements() {
       
 25522 				return !selection.isCollapsed() &&
       
 25523 					dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
       
 25524 			}
       
 25525 
       
 25526 			editor.on('keypress', function(e) {
       
 25527 				var applyAttributes;
       
 25528 
       
 25529 				if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
       
 25530 					applyAttributes = getAttributeApplyFunction();
       
 25531 					editor.getDoc().execCommand('delete', false, null);
       
 25532 					applyAttributes();
       
 25533 					e.preventDefault();
       
 25534 					return false;
       
 25535 				}
       
 25536 			});
       
 25537 
       
 25538 			dom.bind(editor.getDoc(), 'cut', function(e) {
       
 25539 				var applyAttributes;
       
 25540 
       
 25541 				if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
       
 25542 					applyAttributes = getAttributeApplyFunction();
       
 25543 
       
 25544 					setTimeout(function() {
       
 25545 						applyAttributes();
       
 25546 					}, 0);
       
 25547 				}
       
 25548 			});
       
 25549 		}
       
 25550 
       
 25551 		/**
       
 25552 		 * Screen readers on IE needs to have the role application set on the body.
       
 25553 		 */
       
 25554 		function ensureBodyHasRoleApplication() {
       
 25555 			document.body.setAttribute("role", "application");
       
 25556 		}
       
 25557 
       
 25558 		/**
       
 25559 		 * Backspacing into a table behaves differently depending upon browser type.
       
 25560 		 * Therefore, disable Backspace when cursor immediately follows a table.
       
 25561 		 */
       
 25562 		function disableBackspaceIntoATable() {
       
 25563 			editor.on('keydown', function(e) {
       
 25564 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
       
 25565 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
       
 25566 						var previousSibling = selection.getNode().previousSibling;
       
 25567 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
       
 25568 							e.preventDefault();
       
 25569 							return false;
       
 25570 						}
       
 25571 					}
       
 25572 				}
       
 25573 			});
       
 25574 		}
       
 25575 
       
 25576 		/**
       
 25577 		 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
       
 25578 		 * logic adds a \n before the BR so that it will get rendered.
       
 25579 		 */
       
 25580 		function addNewLinesBeforeBrInPre() {
       
 25581 			// IE8+ rendering mode does the right thing with BR in PRE
       
 25582 			if (getDocumentMode() > 7) {
       
 25583 				return;
       
 25584 			}
       
 25585 
       
 25586 			// Enable display: none in area and add a specific class that hides all BR elements in PRE to
       
 25587 			// avoid the caret from getting stuck at the BR elements while pressing the right arrow key
       
 25588 			setEditorCommandState('RespectVisibilityInDesign', true);
       
 25589 			editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
       
 25590 			dom.addClass(editor.getBody(), 'mceHideBrInPre');
       
 25591 
       
 25592 			// Adds a \n before all BR elements in PRE to get them visual
       
 25593 			parser.addNodeFilter('pre', function(nodes) {
       
 25594 				var i = nodes.length, brNodes, j, brElm, sibling;
       
 25595 
       
 25596 				while (i--) {
       
 25597 					brNodes = nodes[i].getAll('br');
       
 25598 					j = brNodes.length;
       
 25599 					while (j--) {
       
 25600 						brElm = brNodes[j];
       
 25601 
       
 25602 						// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
       
 25603 						sibling = brElm.prev;
       
 25604 						if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
       
 25605 							sibling.value += '\n';
       
 25606 						} else {
       
 25607 							brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
       
 25608 						}
       
 25609 					}
       
 25610 				}
       
 25611 			});
       
 25612 
       
 25613 			// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
       
 25614 			serializer.addNodeFilter('pre', function(nodes) {
       
 25615 				var i = nodes.length, brNodes, j, brElm, sibling;
       
 25616 
       
 25617 				while (i--) {
       
 25618 					brNodes = nodes[i].getAll('br');
       
 25619 					j = brNodes.length;
       
 25620 					while (j--) {
       
 25621 						brElm = brNodes[j];
       
 25622 						sibling = brElm.prev;
       
 25623 						if (sibling && sibling.type == 3) {
       
 25624 							sibling.value = sibling.value.replace(/\r?\n$/, '');
       
 25625 						}
       
 25626 					}
       
 25627 				}
       
 25628 			});
       
 25629 		}
       
 25630 
       
 25631 		/**
       
 25632 		 * Moves style width/height to attribute width/height when the user resizes an image on IE.
       
 25633 		 */
       
 25634 		function removePreSerializedStylesWhenSelectingControls() {
       
 25635 			dom.bind(editor.getBody(), 'mouseup', function() {
       
 25636 				var value, node = selection.getNode();
       
 25637 
       
 25638 				// Moved styles to attributes on IMG eements
       
 25639 				if (node.nodeName == 'IMG') {
       
 25640 					// Convert style width to width attribute
       
 25641 					if ((value = dom.getStyle(node, 'width'))) {
       
 25642 						dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
       
 25643 						dom.setStyle(node, 'width', '');
       
 25644 					}
       
 25645 
       
 25646 					// Convert style height to height attribute
       
 25647 					if ((value = dom.getStyle(node, 'height'))) {
       
 25648 						dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
       
 25649 						dom.setStyle(node, 'height', '');
       
 25650 					}
       
 25651 				}
       
 25652 			});
       
 25653 		}
       
 25654 
       
 25655 		/**
       
 25656 		 * Removes a blockquote when backspace is pressed at the beginning of it.
       
 25657 		 *
       
 25658 		 * For example:
       
 25659 		 * <blockquote><p>|x</p></blockquote>
       
 25660 		 *
       
 25661 		 * Becomes:
       
 25662 		 * <p>|x</p>
       
 25663 		 */
       
 25664 		function removeBlockQuoteOnBackSpace() {
       
 25665 			// Add block quote deletion handler
       
 25666 			editor.on('keydown', function(e) {
       
 25667 				var rng, container, offset, root, parent;
       
 25668 
       
 25669 				if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
       
 25670 					return;
       
 25671 				}
       
 25672 
       
 25673 				rng = selection.getRng();
       
 25674 				container = rng.startContainer;
       
 25675 				offset = rng.startOffset;
       
 25676 				root = dom.getRoot();
       
 25677 				parent = container;
       
 25678 
       
 25679 				if (!rng.collapsed || offset !== 0) {
       
 25680 					return;
       
 25681 				}
       
 25682 
       
 25683 				while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
       
 25684 					parent = parent.parentNode;
       
 25685 				}
       
 25686 
       
 25687 				// Is the cursor at the beginning of a blockquote?
       
 25688 				if (parent.tagName === 'BLOCKQUOTE') {
       
 25689 					// Remove the blockquote
       
 25690 					editor.formatter.toggle('blockquote', null, parent);
       
 25691 
       
 25692 					// Move the caret to the beginning of container
       
 25693 					rng = dom.createRng();
       
 25694 					rng.setStart(container, 0);
       
 25695 					rng.setEnd(container, 0);
       
 25696 					selection.setRng(rng);
       
 25697 				}
       
 25698 			});
       
 25699 		}
       
 25700 
       
 25701 		/**
       
 25702 		 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
       
 25703 		 */
       
 25704 		function setGeckoEditingOptions() {
       
 25705 			function setOpts() {
       
 25706 				editor._refreshContentEditable();
       
 25707 
       
 25708 				setEditorCommandState("StyleWithCSS", false);
       
 25709 				setEditorCommandState("enableInlineTableEditing", false);
       
 25710 
       
 25711 				if (!settings.object_resizing) {
       
 25712 					setEditorCommandState("enableObjectResizing", false);
       
 25713 				}
       
 25714 			}
       
 25715 
       
 25716 			if (!settings.readonly) {
       
 25717 				editor.on('BeforeExecCommand MouseDown', setOpts);
       
 25718 			}
       
 25719 		}
       
 25720 
       
 25721 		/**
       
 25722 		 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
       
 25723 		 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
       
 25724 		 *
       
 25725 		 * For example this:
       
 25726 		 * <p><b><a href="#">x</a></b></p>
       
 25727 		 *
       
 25728 		 * Becomes this:
       
 25729 		 * <p><b><a href="#">x</a></b><br></p>
       
 25730 		 */
       
 25731 		function addBrAfterLastLinks() {
       
 25732 			function fixLinks() {
       
 25733 				each(dom.select('a'), function(node) {
       
 25734 					var parentNode = node.parentNode, root = dom.getRoot();
       
 25735 
       
 25736 					if (parentNode.lastChild === node) {
       
 25737 						while (parentNode && !dom.isBlock(parentNode)) {
       
 25738 							if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
       
 25739 								return;
       
 25740 							}
       
 25741 
       
 25742 							parentNode = parentNode.parentNode;
       
 25743 						}
       
 25744 
       
 25745 						dom.add(parentNode, 'br', {'data-mce-bogus': 1});
       
 25746 					}
       
 25747 				});
       
 25748 			}
       
 25749 
       
 25750 			editor.on('SetContent ExecCommand', function(e) {
       
 25751 				if (e.type == "setcontent" || e.command === 'mceInsertLink') {
       
 25752 					fixLinks();
       
 25753 				}
       
 25754 			});
       
 25755 		}
       
 25756 
       
 25757 		/**
       
 25758 		 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
       
 25759 		 * default we want to change that behavior.
       
 25760 		 */
       
 25761 		function setDefaultBlockType() {
       
 25762 			if (settings.forced_root_block) {
       
 25763 				editor.on('init', function() {
       
 25764 					setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
       
 25765 				});
       
 25766 			}
       
 25767 		}
       
 25768 
       
 25769 		/**
       
 25770 		 * Removes ghost selections from images/tables on Gecko.
       
 25771 		 */
       
 25772 		function removeGhostSelection() {
       
 25773 			editor.on('Undo Redo SetContent', function(e) {
       
 25774 				if (!e.initial) {
       
 25775 					editor.execCommand('mceRepaint');
       
 25776 				}
       
 25777 			});
       
 25778 		}
       
 25779 
       
 25780 		/**
       
 25781 		 * Deletes the selected image on IE instead of navigating to previous page.
       
 25782 		 */
       
 25783 		function deleteControlItemOnBackSpace() {
       
 25784 			editor.on('keydown', function(e) {
       
 25785 				var rng;
       
 25786 
       
 25787 				if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
       
 25788 					rng = editor.getDoc().selection.createRange();
       
 25789 					if (rng && rng.item) {
       
 25790 						e.preventDefault();
       
 25791 						editor.undoManager.beforeChange();
       
 25792 						dom.remove(rng.item(0));
       
 25793 						editor.undoManager.add();
       
 25794 					}
       
 25795 				}
       
 25796 			});
       
 25797 		}
       
 25798 
       
 25799 		/**
       
 25800 		 * IE10 doesn't properly render block elements with the right height until you add contents to them.
       
 25801 		 * This fixes that by adding a padding-right to all empty text block elements.
       
 25802 		 * See: https://connect.microsoft.com/IE/feedback/details/743881
       
 25803 		 */
       
 25804 		function renderEmptyBlocksFix() {
       
 25805 			var emptyBlocksCSS;
       
 25806 
       
 25807 			// IE10+
       
 25808 			if (getDocumentMode() >= 10) {
       
 25809 				emptyBlocksCSS = '';
       
 25810 				each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
       
 25811 					emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
       
 25812 				});
       
 25813 
       
 25814 				editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
       
 25815 			}
       
 25816 		}
       
 25817 
       
 25818 		/**
       
 25819 		 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
       
 25820 		 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
       
 25821 		 */
       
 25822 		function keepNoScriptContents() {
       
 25823 			if (getDocumentMode() < 9) {
       
 25824 				parser.addNodeFilter('noscript', function(nodes) {
       
 25825 					var i = nodes.length, node, textNode;
       
 25826 
       
 25827 					while (i--) {
       
 25828 						node = nodes[i];
       
 25829 						textNode = node.firstChild;
       
 25830 
       
 25831 						if (textNode) {
       
 25832 							node.attr('data-mce-innertext', textNode.value);
       
 25833 						}
       
 25834 					}
       
 25835 				});
       
 25836 
       
 25837 				serializer.addNodeFilter('noscript', function(nodes) {
       
 25838 					var i = nodes.length, node, textNode, value;
       
 25839 
       
 25840 					while (i--) {
       
 25841 						node = nodes[i];
       
 25842 						textNode = nodes[i].firstChild;
       
 25843 
       
 25844 						if (textNode) {
       
 25845 							textNode.value = Entities.decode(textNode.value);
       
 25846 						} else {
       
 25847 							// Old IE can't retain noscript value so an attribute is used to store it
       
 25848 							value = node.attributes.map['data-mce-innertext'];
       
 25849 							if (value) {
       
 25850 								node.attr('data-mce-innertext', null);
       
 25851 								textNode = new Node('#text', 3);
       
 25852 								textNode.value = value;
       
 25853 								textNode.raw = true;
       
 25854 								node.append(textNode);
       
 25855 							}
       
 25856 						}
       
 25857 					}
       
 25858 				});
       
 25859 			}
       
 25860 		}
       
 25861 
       
 25862 		/**
       
 25863 		 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
       
 25864 		 */
       
 25865 		function fixCaretSelectionOfDocumentElementOnIe() {
       
 25866 			var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
       
 25867 
       
 25868 			// Return range from point or null if it failed
       
 25869 			function rngFromPoint(x, y) {
       
 25870 				var rng = body.createTextRange();
       
 25871 
       
 25872 				try {
       
 25873 					rng.moveToPoint(x, y);
       
 25874 				} catch (ex) {
       
 25875 					// IE sometimes throws and exception, so lets just ignore it
       
 25876 					rng = null;
       
 25877 				}
       
 25878 
       
 25879 				return rng;
       
 25880 			}
       
 25881 
       
 25882 			// Fires while the selection is changing
       
 25883 			function selectionChange(e) {
       
 25884 				var pointRng;
       
 25885 
       
 25886 				// Check if the button is down or not
       
 25887 				if (e.button) {
       
 25888 					// Create range from mouse position
       
 25889 					pointRng = rngFromPoint(e.x, e.y);
       
 25890 
       
 25891 					if (pointRng) {
       
 25892 						// Check if pointRange is before/after selection then change the endPoint
       
 25893 						if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
       
 25894 							pointRng.setEndPoint('StartToStart', startRng);
       
 25895 						} else {
       
 25896 							pointRng.setEndPoint('EndToEnd', startRng);
       
 25897 						}
       
 25898 
       
 25899 						pointRng.select();
       
 25900 					}
       
 25901 				} else {
       
 25902 					endSelection();
       
 25903 				}
       
 25904 			}
       
 25905 
       
 25906 			// Removes listeners
       
 25907 			function endSelection() {
       
 25908 				var rng = doc.selection.createRange();
       
 25909 
       
 25910 				// If the range is collapsed then use the last start range
       
 25911 				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
       
 25912 					startRng.select();
       
 25913 				}
       
 25914 
       
 25915 				dom.unbind(doc, 'mouseup', endSelection);
       
 25916 				dom.unbind(doc, 'mousemove', selectionChange);
       
 25917 				startRng = started = 0;
       
 25918 			}
       
 25919 
       
 25920 			// Make HTML element unselectable since we are going to handle selection by hand
       
 25921 			doc.documentElement.unselectable = true;
       
 25922 
       
 25923 			// Detect when user selects outside BODY
       
 25924 			dom.bind(doc, 'mousedown contextmenu', function(e) {
       
 25925 				if (e.target.nodeName === 'HTML') {
       
 25926 					if (started) {
       
 25927 						endSelection();
       
 25928 					}
       
 25929 
       
 25930 					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
       
 25931 					htmlElm = doc.documentElement;
       
 25932 					if (htmlElm.scrollHeight > htmlElm.clientHeight) {
       
 25933 						return;
       
 25934 					}
       
 25935 
       
 25936 					started = 1;
       
 25937 					// Setup start position
       
 25938 					startRng = rngFromPoint(e.x, e.y);
       
 25939 					if (startRng) {
       
 25940 						// Listen for selection change events
       
 25941 						dom.bind(doc, 'mouseup', endSelection);
       
 25942 						dom.bind(doc, 'mousemove', selectionChange);
       
 25943 
       
 25944 						dom.getRoot().focus();
       
 25945 						startRng.select();
       
 25946 					}
       
 25947 				}
       
 25948 			});
       
 25949 		}
       
 25950 
       
 25951 		/**
       
 25952 		 * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
       
 25953 		 * this fix will lean the caret right into the closest inline element.
       
 25954 		 */
       
 25955 		function normalizeSelection() {
       
 25956 			// 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
       
 25957 			editor.on('keyup focusin mouseup', function(e) {
       
 25958 				if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
       
 25959 					selection.normalize();
       
 25960 				}
       
 25961 			}, true);
       
 25962 		}
       
 25963 
       
 25964 		/**
       
 25965 		 * Forces Gecko to render a broken image icon if it fails to load an image.
       
 25966 		 */
       
 25967 		function showBrokenImageIcon() {
       
 25968 			editor.contentStyles.push(
       
 25969 				'img:-moz-broken {' +
       
 25970 					'-moz-force-broken-image-icon:1;' +
       
 25971 					'min-width:24px;' +
       
 25972 					'min-height:24px' +
       
 25973 				'}'
       
 25974 			);
       
 25975 		}
       
 25976 
       
 25977 		/**
       
 25978 		 * iOS has a bug where it's impossible to type if the document has a touchstart event
       
 25979 		 * bound and the user touches the document while having the on screen keyboard visible.
       
 25980 		 *
       
 25981 		 * The touch event moves the focus to the parent document while having the caret inside the iframe
       
 25982 		 * this fix moves the focus back into the iframe document.
       
 25983 		 */
       
 25984 		function restoreFocusOnKeyDown() {
       
 25985 			if (!editor.inline) {
       
 25986 				editor.on('keydown', function() {
       
 25987 					if (document.activeElement == document.body) {
       
 25988 						editor.getWin().focus();
       
 25989 					}
       
 25990 				});
       
 25991 			}
       
 25992 		}
       
 25993 
       
 25994 		/**
       
 25995 		 * IE 11 has an annoying issue where you can't move focus into the editor
       
 25996 		 * by clicking on the white area HTML element. We used to be able to to fix this with
       
 25997 		 * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
       
 25998 		 * object it's not possible anymore. So we need to hack in a ungly CSS to force the
       
 25999 		 * body to be at least 150px. If the user clicks the HTML element out side this 150px region
       
 26000 		 * we simply move the focus into the first paragraph. Not ideal since you loose the
       
 26001 		 * positioning of the caret but goot enough for most cases.
       
 26002 		 */
       
 26003 		function bodyHeight() {
       
 26004 			if (!editor.inline) {
       
 26005 				editor.contentStyles.push('body {min-height: 150px}');
       
 26006 				editor.on('click', function(e) {
       
 26007 					if (e.target.nodeName == 'HTML') {
       
 26008 						var rng;
       
 26009 
       
 26010 						// Need to store away non collapsed ranges since the focus call will mess that up see #7382
       
 26011 						rng = editor.selection.getRng();
       
 26012 						editor.getBody().focus();
       
 26013 						editor.selection.setRng(rng);
       
 26014 						editor.selection.normalize();
       
 26015 						editor.nodeChanged();
       
 26016 					}
       
 26017 				});
       
 26018 			}
       
 26019 		}
       
 26020 
       
 26021 		/**
       
 26022 		 * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
       
 26023 		 * You might then loose all your work so we need to block that behavior and replace it with our own.
       
 26024 		 */
       
 26025 		function blockCmdArrowNavigation() {
       
 26026 			if (Env.mac) {
       
 26027 				editor.on('keydown', function(e) {
       
 26028 					if (VK.metaKeyPressed(e) && (e.keyCode == 37 || e.keyCode == 39)) {
       
 26029 						e.preventDefault();
       
 26030 						editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'lineboundary');
       
 26031 					}
       
 26032 				});
       
 26033 			}
       
 26034 		}
       
 26035 
       
 26036 		/**
       
 26037 		 * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin.
       
 26038 		 */
       
 26039 		function disableAutoUrlDetect() {
       
 26040 			setEditorCommandState("AutoUrlDetect", false);
       
 26041 		}
       
 26042 
       
 26043 		/**
       
 26044 		 * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
       
 26045 		 * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
       
 26046 		 * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
       
 26047 		 * but not as the lastChild of the body. However is we add a BR element to the body then remove it
       
 26048 		 * it doesn't seem to add these BR elements makes sence right?!
       
 26049 		 *
       
 26050 		 * Example of what happens: <body>text</body> becomes <body>text<br><br></body>
       
 26051 		 */
       
 26052 		function doubleTrailingBrElements() {
       
 26053 			if (!editor.inline) {
       
 26054 				editor.on('focus blur beforegetcontent', function() {
       
 26055 					var br = editor.dom.create('br');
       
 26056 					editor.getBody().appendChild(br);
       
 26057 					br.parentNode.removeChild(br);
       
 26058 				}, true);
       
 26059 			}
       
 26060 		}
       
 26061 
       
 26062 		/**
       
 26063 		 * iOS 7.1 introduced two new bugs:
       
 26064 		 * 1) It's possible to open links within a contentEditable area by clicking on them.
       
 26065 		 * 2) If you hold down the finger it will display the link/image touch callout menu.
       
 26066 		 */
       
 26067 		function tapLinksAndImages() {
       
 26068 			editor.on('click', function(e) {
       
 26069 				var elm = e.target;
       
 26070 
       
 26071 				do {
       
 26072 					if (elm.tagName === 'A') {
       
 26073 						e.preventDefault();
       
 26074 						return;
       
 26075 					}
       
 26076 				} while ((elm = elm.parentNode));
       
 26077 			});
       
 26078 
       
 26079 			editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
       
 26080 		}
       
 26081 
       
 26082 		/**
       
 26083 		 * iOS Safari and possible other browsers have a bug where it won't fire
       
 26084 		 * a click event when a contentEditable is focused. This function fakes click events
       
 26085 		 * by using touchstart/touchend and measuring the time and distance travelled.
       
 26086 		 */
       
 26087 		function touchClickEvent() {
       
 26088 			editor.on('touchstart', function(e) {
       
 26089 				var elm, time, startTouch, changedTouches;
       
 26090 
       
 26091 				elm = e.target;
       
 26092 				time = new Date().getTime();
       
 26093 				changedTouches = e.changedTouches;
       
 26094 
       
 26095 				if (!changedTouches || changedTouches.length > 1) {
       
 26096 					return;
       
 26097 				}
       
 26098 
       
 26099 				startTouch = changedTouches[0];
       
 26100 
       
 26101 				editor.once('touchend', function(e) {
       
 26102 					var endTouch = e.changedTouches[0], args;
       
 26103 
       
 26104 					if (new Date().getTime() - time > 500) {
       
 26105 						return;
       
 26106 					}
       
 26107 
       
 26108 					if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) {
       
 26109 						return;
       
 26110 					}
       
 26111 
       
 26112 					if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) {
       
 26113 						return;
       
 26114 					}
       
 26115 
       
 26116 					args = {
       
 26117 						target: elm
       
 26118 					};
       
 26119 
       
 26120 					each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) {
       
 26121 						args[key] = endTouch[key];
       
 26122 					});
       
 26123 
       
 26124 					args = editor.fire('click', args);
       
 26125 
       
 26126 					if (!args.isDefaultPrevented()) {
       
 26127 						// iOS WebKit can't place the caret properly once
       
 26128 						// you bind touch events so we need to do this manually
       
 26129 						// TODO: Expand to the closest word? Touble tap still works.
       
 26130 						editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY);
       
 26131 						editor.nodeChanged();
       
 26132 					}
       
 26133 				});
       
 26134 			});
       
 26135 		}
       
 26136 
       
 26137 		/**
       
 26138 		 * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
       
 26139 		 * For example this: <form><button></form>
       
 26140 		 */
       
 26141 		function blockFormSubmitInsideEditor() {
       
 26142 			editor.on('init', function() {
       
 26143 				editor.dom.bind(editor.getBody(), 'submit', function(e) {
       
 26144 					e.preventDefault();
       
 26145 				});
       
 26146 			});
       
 26147 		}
       
 26148 
       
 26149 		/**
       
 26150 		 * Sometimes WebKit/Blink generates BR elements with the Apple-interchange-newline class.
       
 26151 		 *
       
 26152 		 * Scenario:
       
 26153 		 *  1) Create a table 2x2.
       
 26154 		 *  2) Select and copy cells A2-B2.
       
 26155 		 *  3) Paste and it will add BR element to table cell.
       
 26156 		 */
       
 26157 		function removeAppleInterchangeBrs() {
       
 26158 			parser.addNodeFilter('br', function(nodes) {
       
 26159 				var i = nodes.length;
       
 26160 
       
 26161 				while (i--) {
       
 26162 					if (nodes[i].attr('class') == 'Apple-interchange-newline') {
       
 26163 						nodes[i].remove();
       
 26164 					}
       
 26165 				}
       
 26166 			});
       
 26167 		}
       
 26168 
       
 26169 		/**
       
 26170 		 * IE cannot set custom contentType's on drag events, and also does not properly drag/drop between
       
 26171 		 * editors. This uses a special data:text/mce-internal URL to pass data when drag/drop between editors.
       
 26172 		 */
       
 26173 		function ieInternalDragAndDrop() {
       
 26174 			editor.on('dragstart', function(e) {
       
 26175 				setMceInteralContent(e);
       
 26176 			});
       
 26177 
       
 26178 			editor.on('drop', function(e) {
       
 26179 				if (!isDefaultPrevented(e)) {
       
 26180 					var internalContent = getMceInternalContent(e);
       
 26181 					if (internalContent) {
       
 26182 						e.preventDefault();
       
 26183 
       
 26184 						var rng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, editor.getDoc());
       
 26185 						selection.setRng(rng);
       
 26186 						insertClipboardContents(internalContent);
       
 26187 					}
       
 26188 				}
       
 26189 			});
       
 26190 		}
       
 26191 
       
 26192 		// All browsers
       
 26193 		removeBlockQuoteOnBackSpace();
       
 26194 		emptyEditorWhenDeleting();
       
 26195 		normalizeSelection();
       
 26196 
       
 26197 		// WebKit
       
 26198 		if (isWebKit) {
       
 26199 			cleanupStylesWhenDeleting();
       
 26200 			inputMethodFocus();
       
 26201 			selectControlElements();
       
 26202 			setDefaultBlockType();
       
 26203 			blockFormSubmitInsideEditor();
       
 26204 			disableBackspaceIntoATable();
       
 26205 			removeAppleInterchangeBrs();
       
 26206 			touchClickEvent();
       
 26207 
       
 26208 			// iOS
       
 26209 			if (Env.iOS) {
       
 26210 				restoreFocusOnKeyDown();
       
 26211 				bodyHeight();
       
 26212 				tapLinksAndImages();
       
 26213 			} else {
       
 26214 				selectAll();
       
 26215 			}
       
 26216 		}
       
 26217 
       
 26218 		// IE
       
 26219 		if (isIE && Env.ie < 11) {
       
 26220 			removeHrOnBackspace();
       
 26221 			ensureBodyHasRoleApplication();
       
 26222 			addNewLinesBeforeBrInPre();
       
 26223 			removePreSerializedStylesWhenSelectingControls();
       
 26224 			deleteControlItemOnBackSpace();
       
 26225 			renderEmptyBlocksFix();
       
 26226 			keepNoScriptContents();
       
 26227 			fixCaretSelectionOfDocumentElementOnIe();
       
 26228 		}
       
 26229 
       
 26230 		if (Env.ie >= 11) {
       
 26231 			bodyHeight();
       
 26232 			doubleTrailingBrElements();
       
 26233 			disableBackspaceIntoATable();
       
 26234 		}
       
 26235 
       
 26236 		if (Env.ie) {
       
 26237 			selectAll();
       
 26238 			disableAutoUrlDetect();
       
 26239 			ieInternalDragAndDrop();
       
 26240 		}
       
 26241 
       
 26242 		// Gecko
       
 26243 		if (isGecko) {
       
 26244 			removeHrOnBackspace();
       
 26245 			focusBody();
       
 26246 			removeStylesWhenDeletingAcrossBlockElements();
       
 26247 			setGeckoEditingOptions();
       
 26248 			addBrAfterLastLinks();
       
 26249 			removeGhostSelection();
       
 26250 			showBrokenImageIcon();
       
 26251 			blockCmdArrowNavigation();
       
 26252 			disableBackspaceIntoATable();
       
 26253 		}
       
 26254 	};
       
 26255 });
       
 26256 
       
 26257 // Included from: js/tinymce/classes/util/Observable.js
       
 26258 
       
 26259 /**
       
 26260  * Observable.js
       
 26261  *
       
 26262  * Copyright, Moxiecode Systems AB
       
 26263  * Released under LGPL License.
       
 26264  *
       
 26265  * License: http://www.tinymce.com/license
       
 26266  * Contributing: http://www.tinymce.com/contributing
       
 26267  */
       
 26268 
       
 26269 /**
       
 26270  * This mixin will add event binding logic to classes.
       
 26271  *
       
 26272  * @mixin tinymce.util.Observable
       
 26273  */
       
 26274 define("tinymce/util/Observable", [
       
 26275 	"tinymce/util/EventDispatcher"
       
 26276 ], function(EventDispatcher) {
       
 26277 	function getEventDispatcher(obj) {
       
 26278 		if (!obj._eventDispatcher) {
       
 26279 			obj._eventDispatcher = new EventDispatcher({
       
 26280 				scope: obj,
       
 26281 				toggleEvent: function(name, state) {
       
 26282 					if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {
       
 26283 						obj.toggleNativeEvent(name, state);
       
 26284 					}
       
 26285 				}
       
 26286 			});
       
 26287 		}
       
 26288 
       
 26289 		return obj._eventDispatcher;
       
 26290 	}
       
 26291 
       
 26292 	return {
       
 26293 		/**
       
 26294 		 * Fires the specified event by name.
       
 26295 		 *
       
 26296 		 * @method fire
       
 26297 		 * @param {String} name Name of the event to fire.
       
 26298 		 * @param {Object?} args Event arguments.
       
 26299 		 * @param {Boolean?} bubble True/false if the event is to be bubbled.
       
 26300 		 * @return {Object} Event args instance passed in.
       
 26301 		 * @example
       
 26302 		 * instance.fire('event', {...});
       
 26303 		 */
       
 26304 		fire: function(name, args, bubble) {
       
 26305 			var self = this;
       
 26306 
       
 26307 			// Prevent all events except the remove event after the instance has been removed
       
 26308 			if (self.removed && name !== "remove") {
       
 26309 				return args;
       
 26310 			}
       
 26311 
       
 26312 			args = getEventDispatcher(self).fire(name, args, bubble);
       
 26313 
       
 26314 			// Bubble event up to parents
       
 26315 			if (bubble !== false && self.parent) {
       
 26316 				var parent = self.parent();
       
 26317 				while (parent && !args.isPropagationStopped()) {
       
 26318 					parent.fire(name, args, false);
       
 26319 					parent = parent.parent();
       
 26320 				}
       
 26321 			}
       
 26322 
       
 26323 			return args;
       
 26324 		},
       
 26325 
       
 26326 		/**
       
 26327 		 * Binds an event listener to a specific event by name.
       
 26328 		 *
       
 26329 		 * @method on
       
 26330 		 * @param {String} name Event name or space separated list of events to bind.
       
 26331 		 * @param {callback} callback Callback to be executed when the event occurs.
       
 26332 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
       
 26333 		 * @return {Object} Current class instance.
       
 26334 		 * @example
       
 26335 		 * instance.on('event', function(e) {
       
 26336 		 *     // Callback logic
       
 26337 		 * });
       
 26338 		 */
       
 26339 		on: function(name, callback, prepend) {
       
 26340 			return getEventDispatcher(this).on(name, callback, prepend);
       
 26341 		},
       
 26342 
       
 26343 		/**
       
 26344 		 * Unbinds an event listener to a specific event by name.
       
 26345 		 *
       
 26346 		 * @method off
       
 26347 		 * @param {String?} name Name of the event to unbind.
       
 26348 		 * @param {callback?} callback Callback to unbind.
       
 26349 		 * @return {Object} Current class instance.
       
 26350 		 * @example
       
 26351 		 * // Unbind specific callback
       
 26352 		 * instance.off('event', handler);
       
 26353 		 *
       
 26354 		 * // Unbind all listeners by name
       
 26355 		 * instance.off('event');
       
 26356 		 *
       
 26357 		 * // Unbind all events
       
 26358 		 * instance.off();
       
 26359 		 */
       
 26360 		off: function(name, callback) {
       
 26361 			return getEventDispatcher(this).off(name, callback);
       
 26362 		},
       
 26363 
       
 26364 		/**
       
 26365 		 * Bind the event callback and once it fires the callback is removed.
       
 26366 		 *
       
 26367 		 * @method once
       
 26368 		 * @param {String} name Name of the event to bind.
       
 26369 		 * @param {callback} callback Callback to bind only once.
       
 26370 		 * @return {Object} Current class instance.
       
 26371 		 */
       
 26372 		once: function(name, callback) {
       
 26373 			return getEventDispatcher(this).once(name, callback);
       
 26374 		},
       
 26375 
       
 26376 		/**
       
 26377 		 * Returns true/false if the object has a event of the specified name.
       
 26378 		 *
       
 26379 		 * @method hasEventListeners
       
 26380 		 * @param {String} name Name of the event to check for.
       
 26381 		 * @return {Boolean} true/false if the event exists or not.
       
 26382 		 */
       
 26383 		hasEventListeners: function(name) {
       
 26384 			return getEventDispatcher(this).has(name);
       
 26385 		}
       
 26386 	};
       
 26387 });
       
 26388 
       
 26389 // Included from: js/tinymce/classes/EditorObservable.js
       
 26390 
       
 26391 /**
       
 26392  * EditorObservable.js
       
 26393  *
       
 26394  * Copyright, Moxiecode Systems AB
       
 26395  * Released under LGPL License.
       
 26396  *
       
 26397  * License: http://www.tinymce.com/license
       
 26398  * Contributing: http://www.tinymce.com/contributing
       
 26399  */
       
 26400 
       
 26401 /**
       
 26402  * This mixin contains the event logic for the tinymce.Editor class.
       
 26403  *
       
 26404  * @mixin tinymce.EditorObservable
       
 26405  * @extends tinymce.util.Observable
       
 26406  */
       
 26407 define("tinymce/EditorObservable", [
       
 26408 	"tinymce/util/Observable",
       
 26409 	"tinymce/dom/DOMUtils",
       
 26410 	"tinymce/util/Tools"
       
 26411 ], function(Observable, DOMUtils, Tools) {
       
 26412 	var DOM = DOMUtils.DOM, customEventRootDelegates;
       
 26413 
       
 26414 	/**
       
 26415 	 * Returns the event target so for the specified event. Some events fire
       
 26416 	 * only on document, some fire on documentElement etc. This also handles the
       
 26417 	 * custom event root setting where it returns that element instead of the body.
       
 26418 	 *
       
 26419 	 * @private
       
 26420 	 * @param {tinymce.Editor} editor Editor instance to get event target from.
       
 26421 	 * @param {String} eventName Name of the event for example "click".
       
 26422 	 * @return {Element/Document} HTML Element or document target to bind on.
       
 26423 	 */
       
 26424 	function getEventTarget(editor, eventName) {
       
 26425 		if (eventName == 'selectionchange') {
       
 26426 			return editor.getDoc();
       
 26427 		}
       
 26428 
       
 26429 		// Need to bind mousedown/mouseup etc to document not body in iframe mode
       
 26430 		// Since the user might click on the HTML element not the BODY
       
 26431 		if (!editor.inline && /^mouse|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
       
 26432 			return editor.getDoc().documentElement;
       
 26433 		}
       
 26434 
       
 26435 		// Bind to event root instead of body if it's defined
       
 26436 		if (editor.settings.event_root) {
       
 26437 			if (!editor.eventRoot) {
       
 26438 				editor.eventRoot = DOM.select(editor.settings.event_root)[0];
       
 26439 			}
       
 26440 
       
 26441 			return editor.eventRoot;
       
 26442 		}
       
 26443 
       
 26444 		return editor.getBody();
       
 26445 	}
       
 26446 
       
 26447 	/**
       
 26448 	 * Binds a event delegate for the specified name this delegate will fire
       
 26449 	 * the event to the editor dispatcher.
       
 26450 	 *
       
 26451 	 * @private
       
 26452 	 * @param {tinymce.Editor} editor Editor instance to get event target from.
       
 26453 	 * @param {String} eventName Name of the event for example "click".
       
 26454 	 */
       
 26455 	function bindEventDelegate(editor, eventName) {
       
 26456 		var eventRootElm = getEventTarget(editor, eventName), delegate;
       
 26457 
       
 26458 		if (!editor.delegates) {
       
 26459 			editor.delegates = {};
       
 26460 		}
       
 26461 
       
 26462 		if (editor.delegates[eventName]) {
       
 26463 			return;
       
 26464 		}
       
 26465 
       
 26466 		if (editor.settings.event_root) {
       
 26467 			if (!customEventRootDelegates) {
       
 26468 				customEventRootDelegates = {};
       
 26469 				editor.editorManager.on('removeEditor', function() {
       
 26470 					var name;
       
 26471 
       
 26472 					if (!editor.editorManager.activeEditor) {
       
 26473 						if (customEventRootDelegates) {
       
 26474 							for (name in customEventRootDelegates) {
       
 26475 								editor.dom.unbind(getEventTarget(editor, name));
       
 26476 							}
       
 26477 
       
 26478 							customEventRootDelegates = null;
       
 26479 						}
       
 26480 					}
       
 26481 				});
       
 26482 			}
       
 26483 
       
 26484 			if (customEventRootDelegates[eventName]) {
       
 26485 				return;
       
 26486 			}
       
 26487 
       
 26488 			delegate = function(e) {
       
 26489 				var target = e.target, editors = editor.editorManager.editors, i = editors.length;
       
 26490 
       
 26491 				while (i--) {
       
 26492 					var body = editors[i].getBody();
       
 26493 
       
 26494 					if (body === target || DOM.isChildOf(target, body)) {
       
 26495 						if (!editors[i].hidden) {
       
 26496 							editors[i].fire(eventName, e);
       
 26497 						}
       
 26498 					}
       
 26499 				}
       
 26500 			};
       
 26501 
       
 26502 			customEventRootDelegates[eventName] = delegate;
       
 26503 			DOM.bind(eventRootElm, eventName, delegate);
       
 26504 		} else {
       
 26505 			delegate = function(e) {
       
 26506 				if (!editor.hidden) {
       
 26507 					editor.fire(eventName, e);
       
 26508 				}
       
 26509 			};
       
 26510 
       
 26511 			DOM.bind(eventRootElm, eventName, delegate);
       
 26512 			editor.delegates[eventName] = delegate;
       
 26513 		}
       
 26514 	}
       
 26515 
       
 26516 	var EditorObservable = {
       
 26517 		/**
       
 26518 		 * Bind any pending event delegates. This gets executed after the target body/document is created.
       
 26519 		 *
       
 26520 		 * @private
       
 26521 		 */
       
 26522 		bindPendingEventDelegates: function() {
       
 26523 			var self = this;
       
 26524 
       
 26525 			Tools.each(self._pendingNativeEvents, function(name) {
       
 26526 				bindEventDelegate(self, name);
       
 26527 			});
       
 26528 		},
       
 26529 
       
 26530 		/**
       
 26531 		 * Toggles a native event on/off this is called by the EventDispatcher when
       
 26532 		 * the first native event handler is added and when the last native event handler is removed.
       
 26533 		 *
       
 26534 		 * @private
       
 26535 		 */
       
 26536 		toggleNativeEvent: function(name, state) {
       
 26537 			var self = this;
       
 26538 
       
 26539 			if (self.settings.readonly) {
       
 26540 				return;
       
 26541 			}
       
 26542 
       
 26543 			// Never bind focus/blur since the FocusManager fakes those
       
 26544 			if (name == "focus" || name == "blur") {
       
 26545 				return;
       
 26546 			}
       
 26547 
       
 26548 			if (state) {
       
 26549 				if (self.initialized) {
       
 26550 					bindEventDelegate(self, name);
       
 26551 				} else {
       
 26552 					if (!self._pendingNativeEvents) {
       
 26553 						self._pendingNativeEvents = [name];
       
 26554 					} else {
       
 26555 						self._pendingNativeEvents.push(name);
       
 26556 					}
       
 26557 				}
       
 26558 			} else if (self.initialized) {
       
 26559 				self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
       
 26560 				delete self.delegates[name];
       
 26561 			}
       
 26562 		},
       
 26563 
       
 26564 		/**
       
 26565 		 * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
       
 26566 		 *
       
 26567 		 * @private
       
 26568 		 */
       
 26569 		unbindAllNativeEvents: function() {
       
 26570 			var self = this, name;
       
 26571 
       
 26572 			if (self.delegates) {
       
 26573 				for (name in self.delegates) {
       
 26574 					self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
       
 26575 				}
       
 26576 
       
 26577 				delete self.delegates;
       
 26578 			}
       
 26579 
       
 26580 			if (!self.inline) {
       
 26581 				self.getBody().onload = null;
       
 26582 				self.dom.unbind(self.getWin());
       
 26583 				self.dom.unbind(self.getDoc());
       
 26584 			}
       
 26585 
       
 26586 			self.dom.unbind(self.getBody());
       
 26587 			self.dom.unbind(self.getContainer());
       
 26588 		}
       
 26589 	};
       
 26590 
       
 26591 	EditorObservable = Tools.extend({}, Observable, EditorObservable);
       
 26592 
       
 26593 	return EditorObservable;
       
 26594 });
       
 26595 
       
 26596 // Included from: js/tinymce/classes/Shortcuts.js
       
 26597 
       
 26598 /**
       
 26599  * Shortcuts.js
       
 26600  *
       
 26601  * Copyright, Moxiecode Systems AB
       
 26602  * Released under LGPL License.
       
 26603  *
       
 26604  * License: http://www.tinymce.com/license
       
 26605  * Contributing: http://www.tinymce.com/contributing
       
 26606  */
       
 26607 
       
 26608 /**
       
 26609  * Contains all logic for handling of keyboard shortcuts.
       
 26610  *
       
 26611  * @example
       
 26612  * editor.shortcuts.add('ctrl+a', function() {});
       
 26613  * editor.shortcuts.add('meta+a', function() {}); // "meta" maps to Command on Mac and Ctrl on PC
       
 26614  * editor.shortcuts.add('ctrl+alt+a', function() {});
       
 26615  * editor.shortcuts.add('access+a', function() {}); // "access" maps to ctrl+alt on Mac and shift+alt on PC
       
 26616  */
       
 26617 define("tinymce/Shortcuts", [
       
 26618 	"tinymce/util/Tools",
       
 26619 	"tinymce/Env"
       
 26620 ], function(Tools, Env) {
       
 26621 	var each = Tools.each, explode = Tools.explode;
       
 26622 
       
 26623 	var keyCodeLookup = {
       
 26624 		"f9": 120,
       
 26625 		"f10": 121,
       
 26626 		"f11": 122
       
 26627 	};
       
 26628 
       
 26629 	var modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access');
       
 26630 
       
 26631 	return function(editor) {
       
 26632 		var self = this, shortcuts = {};
       
 26633 
       
 26634 		function createShortcut(pattern, desc, cmdFunc, scope) {
       
 26635 			var id, key, shortcut;
       
 26636 
       
 26637 			shortcut = {
       
 26638 				func: cmdFunc,
       
 26639 				scope: scope || editor,
       
 26640 				desc: editor.translate(desc)
       
 26641 			};
       
 26642 
       
 26643 			// Parse modifiers and keys ctrl+alt+b for example
       
 26644 			each(explode(pattern, '+'), function(value) {
       
 26645 				if (value in modifierNames) {
       
 26646 					shortcut[value] = true;
       
 26647 				} else {
       
 26648 					// Allow numeric keycodes like ctrl+219 for ctrl+[
       
 26649 					if (/^[0-9]{2,}$/.test(value)) {
       
 26650 						shortcut.keyCode = parseInt(value, 10);
       
 26651 					} else {
       
 26652 						shortcut.charCode = value.charCodeAt(0);
       
 26653 						shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
       
 26654 					}
       
 26655 				}
       
 26656 			});
       
 26657 
       
 26658 			// Generate unique id for modifier combination and set default state for unused modifiers
       
 26659 			id = [shortcut.keyCode];
       
 26660 			for (key in modifierNames) {
       
 26661 				if (shortcut[key]) {
       
 26662 					id.push(key);
       
 26663 				} else {
       
 26664 					shortcut[key] = false;
       
 26665 				}
       
 26666 			}
       
 26667 			shortcut.id = id.join(',');
       
 26668 
       
 26669 			// Handle special access modifier differently depending on Mac/Win
       
 26670 			if (shortcut.access) {
       
 26671 				shortcut.alt = true;
       
 26672 
       
 26673 				if (Env.mac) {
       
 26674 					shortcut.ctrl = true;
       
 26675 				} else {
       
 26676 					shortcut.shift = true;
       
 26677 				}
       
 26678 			}
       
 26679 
       
 26680 			// Handle special meta modifier differently depending on Mac/Win
       
 26681 			if (shortcut.meta) {
       
 26682 				if (Env.mac) {
       
 26683 					shortcut.meta = true;
       
 26684 				} else {
       
 26685 					shortcut.ctrl = true;
       
 26686 					shortcut.meta = false;
       
 26687 				}
       
 26688 			}
       
 26689 
       
 26690 			return shortcut;
       
 26691 		}
       
 26692 
       
 26693 		editor.on('keyup keypress keydown', function(e) {
       
 26694 			if ((e.altKey || e.ctrlKey || e.metaKey) && !e.isDefaultPrevented()) {
       
 26695 				each(shortcuts, function(shortcut) {
       
 26696 					if (shortcut.ctrl != e.ctrlKey || shortcut.meta != e.metaKey) {
       
 26697 						return;
       
 26698 					}
       
 26699 
       
 26700 					if (shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
       
 26701 						return;
       
 26702 					}
       
 26703 
       
 26704 					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
       
 26705 						e.preventDefault();
       
 26706 
       
 26707 						if (e.type == "keydown") {
       
 26708 							shortcut.func.call(shortcut.scope);
       
 26709 						}
       
 26710 
       
 26711 						return true;
       
 26712 					}
       
 26713 				});
       
 26714 			}
       
 26715 		});
       
 26716 
       
 26717 		/**
       
 26718 		 * Adds a keyboard shortcut for some command or function.
       
 26719 		 *
       
 26720 		 * @method addShortcut
       
 26721 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
 26722 		 * @param {String} desc Text description for the command.
       
 26723 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
       
 26724 		 * @param {Object} sc Optional scope to execute the function in.
       
 26725 		 * @return {Boolean} true/false state if the shortcut was added or not.
       
 26726 		 */
       
 26727 		self.add = function(pattern, desc, cmdFunc, scope) {
       
 26728 			var cmd;
       
 26729 
       
 26730 			cmd = cmdFunc;
       
 26731 
       
 26732 			if (typeof cmdFunc === 'string') {
       
 26733 				cmdFunc = function() {
       
 26734 					editor.execCommand(cmd, false, null);
       
 26735 				};
       
 26736 			} else if (Tools.isArray(cmd)) {
       
 26737 				cmdFunc = function() {
       
 26738 					editor.execCommand(cmd[0], cmd[1], cmd[2]);
       
 26739 				};
       
 26740 			}
       
 26741 
       
 26742 			each(explode(pattern.toLowerCase()), function(pattern) {
       
 26743 				var shortcut = createShortcut(pattern, desc, cmdFunc, scope);
       
 26744 				shortcuts[shortcut.id] = shortcut;
       
 26745 			});
       
 26746 
       
 26747 			return true;
       
 26748 		};
       
 26749 
       
 26750 		/**
       
 26751 		 * Remove a keyboard shortcut by pattern.
       
 26752 		 *
       
 26753 		 * @method remove
       
 26754 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
 26755 		 * @return {Boolean} true/false state if the shortcut was removed or not.
       
 26756 		 */
       
 26757 		self.remove = function(pattern) {
       
 26758 			var shortcut = createShortcut(pattern);
       
 26759 
       
 26760 			if (shortcuts[shortcut.id]) {
       
 26761 				delete shortcuts[shortcut.id];
       
 26762 				return true;
       
 26763 			}
       
 26764 
       
 26765 			return false;
       
 26766 		};
       
 26767 	};
       
 26768 });
       
 26769 
       
 26770 // Included from: js/tinymce/classes/Editor.js
       
 26771 
       
 26772 /**
       
 26773  * Editor.js
       
 26774  *
       
 26775  * Copyright, Moxiecode Systems AB
       
 26776  * Released under LGPL License.
       
 26777  *
       
 26778  * License: http://www.tinymce.com/license
       
 26779  * Contributing: http://www.tinymce.com/contributing
       
 26780  */
       
 26781 
       
 26782 /*jshint scripturl:true */
       
 26783 
       
 26784 /**
       
 26785  * Include the base event class documentation.
       
 26786  *
       
 26787  * @include ../../../tools/docs/tinymce.Event.js
       
 26788  */
       
 26789 
       
 26790 /**
       
 26791  * This class contains the core logic for a TinyMCE editor.
       
 26792  *
       
 26793  * @class tinymce.Editor
       
 26794  * @mixes tinymce.util.Observable
       
 26795  * @example
       
 26796  * // Add a class to all paragraphs in the editor.
       
 26797  * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
 26798  *
       
 26799  * // Gets the current editors selection as text
       
 26800  * tinymce.activeEditor.selection.getContent({format: 'text'});
       
 26801  *
       
 26802  * // Creates a new editor instance
       
 26803  * var ed = new tinymce.Editor('textareaid', {
       
 26804  *     some_setting: 1
       
 26805  * }, tinymce.EditorManager);
       
 26806  *
       
 26807  * // Select each item the user clicks on
       
 26808  * ed.on('click', function(e) {
       
 26809  *     ed.selection.select(e.target);
       
 26810  * });
       
 26811  *
       
 26812  * ed.render();
       
 26813  */
       
 26814 define("tinymce/Editor", [
       
 26815 	"tinymce/dom/DOMUtils",
       
 26816 	"tinymce/dom/DomQuery",
       
 26817 	"tinymce/AddOnManager",
       
 26818 	"tinymce/NodeChange",
       
 26819 	"tinymce/html/Node",
       
 26820 	"tinymce/dom/Serializer",
       
 26821 	"tinymce/html/Serializer",
       
 26822 	"tinymce/dom/Selection",
       
 26823 	"tinymce/Formatter",
       
 26824 	"tinymce/UndoManager",
       
 26825 	"tinymce/EnterKey",
       
 26826 	"tinymce/ForceBlocks",
       
 26827 	"tinymce/EditorCommands",
       
 26828 	"tinymce/util/URI",
       
 26829 	"tinymce/dom/ScriptLoader",
       
 26830 	"tinymce/dom/EventUtils",
       
 26831 	"tinymce/WindowManager",
       
 26832 	"tinymce/html/Schema",
       
 26833 	"tinymce/html/DomParser",
       
 26834 	"tinymce/util/Quirks",
       
 26835 	"tinymce/Env",
       
 26836 	"tinymce/util/Tools",
       
 26837 	"tinymce/EditorObservable",
       
 26838 	"tinymce/Shortcuts"
       
 26839 ], function(
       
 26840 	DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
       
 26841 	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
       
 26842 	URI, ScriptLoader, EventUtils, WindowManager,
       
 26843 	Schema, DomParser, Quirks, Env, Tools, EditorObservable, Shortcuts
       
 26844 ) {
       
 26845 	// Shorten these names
       
 26846 	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
       
 26847 	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
       
 26848 	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
       
 26849 	var Event = EventUtils.Event;
       
 26850 	var isGecko = Env.gecko, ie = Env.ie;
       
 26851 
       
 26852 	/**
       
 26853 	 * Include documentation for all the events.
       
 26854 	 *
       
 26855 	 * @include ../../../tools/docs/tinymce.Editor.js
       
 26856 	 */
       
 26857 
       
 26858 	/**
       
 26859 	 * Constructs a editor instance by id.
       
 26860 	 *
       
 26861 	 * @constructor
       
 26862 	 * @method Editor
       
 26863 	 * @param {String} id Unique id for the editor.
       
 26864 	 * @param {Object} settings Settings for the editor.
       
 26865 	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
       
 26866 	 * @author Moxiecode
       
 26867 	 */
       
 26868 	function Editor(id, settings, editorManager) {
       
 26869 		var self = this, documentBaseUrl, baseUri;
       
 26870 
       
 26871 		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
       
 26872 		baseUri = editorManager.baseURI;
       
 26873 
       
 26874 		/**
       
 26875 		 * Name/value collection with editor settings.
       
 26876 		 *
       
 26877 		 * @property settings
       
 26878 		 * @type Object
       
 26879 		 * @example
       
 26880 		 * // Get the value of the theme setting
       
 26881 		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
       
 26882 		 */
       
 26883 		self.settings = settings = extend({
       
 26884 			id: id,
       
 26885 			theme: 'modern',
       
 26886 			delta_width: 0,
       
 26887 			delta_height: 0,
       
 26888 			popup_css: '',
       
 26889 			plugins: '',
       
 26890 			document_base_url: documentBaseUrl,
       
 26891 			add_form_submit_trigger: true,
       
 26892 			submit_patch: true,
       
 26893 			add_unload_trigger: true,
       
 26894 			convert_urls: true,
       
 26895 			relative_urls: true,
       
 26896 			remove_script_host: true,
       
 26897 			object_resizing: true,
       
 26898 			doctype: '<!DOCTYPE html>',
       
 26899 			visual: true,
       
 26900 			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
       
 26901 
       
 26902 			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
       
 26903 			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
       
 26904 			forced_root_block: 'p',
       
 26905 			hidden_input: true,
       
 26906 			padd_empty_editor: true,
       
 26907 			render_ui: true,
       
 26908 			indentation: '30px',
       
 26909 			inline_styles: true,
       
 26910 			convert_fonts_to_spans: true,
       
 26911 			indent: 'simple',
       
 26912 			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,' +
       
 26913 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
       
 26914 			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,' +
       
 26915 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
       
 26916 			validate: true,
       
 26917 			entity_encoding: 'named',
       
 26918 			url_converter: self.convertURL,
       
 26919 			url_converter_scope: self,
       
 26920 			ie7_compat: true
       
 26921 		}, settings);
       
 26922 
       
 26923 		AddOnManager.language = settings.language || 'en';
       
 26924 		AddOnManager.languageLoad = settings.language_load;
       
 26925 
       
 26926 		AddOnManager.baseURL = editorManager.baseURL;
       
 26927 
       
 26928 		/**
       
 26929 		 * Editor instance id, normally the same as the div/textarea that was replaced.
       
 26930 		 *
       
 26931 		 * @property id
       
 26932 		 * @type String
       
 26933 		 */
       
 26934 		self.id = settings.id = id;
       
 26935 
       
 26936 		/**
       
 26937 		 * State to force the editor to return false on a isDirty call.
       
 26938 		 *
       
 26939 		 * @property isNotDirty
       
 26940 		 * @type Boolean
       
 26941 		 * @example
       
 26942 		 * function ajaxSave() {
       
 26943 		 *     var ed = tinymce.get('elm1');
       
 26944 		 *
       
 26945 		 *     // Save contents using some XHR call
       
 26946 		 *     alert(ed.getContent());
       
 26947 		 *
       
 26948 		 *     ed.isNotDirty = true; // Force not dirty state
       
 26949 		 * }
       
 26950 		 */
       
 26951 		self.isNotDirty = true;
       
 26952 
       
 26953 		/**
       
 26954 		 * Name/Value object containting plugin instances.
       
 26955 		 *
       
 26956 		 * @property plugins
       
 26957 		 * @type Object
       
 26958 		 * @example
       
 26959 		 * // Execute a method inside a plugin directly
       
 26960 		 * tinymce.activeEditor.plugins.someplugin.someMethod();
       
 26961 		 */
       
 26962 		self.plugins = {};
       
 26963 
       
 26964 		/**
       
 26965 		 * URI object to document configured for the TinyMCE instance.
       
 26966 		 *
       
 26967 		 * @property documentBaseURI
       
 26968 		 * @type tinymce.util.URI
       
 26969 		 * @example
       
 26970 		 * // Get relative URL from the location of document_base_url
       
 26971 		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
       
 26972 		 *
       
 26973 		 * // Get absolute URL from the location of document_base_url
       
 26974 		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
       
 26975 		 */
       
 26976 		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
       
 26977 			base_uri: baseUri
       
 26978 		});
       
 26979 
       
 26980 		/**
       
 26981 		 * URI object to current document that holds the TinyMCE editor instance.
       
 26982 		 *
       
 26983 		 * @property baseURI
       
 26984 		 * @type tinymce.util.URI
       
 26985 		 * @example
       
 26986 		 * // Get relative URL from the location of the API
       
 26987 		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
       
 26988 		 *
       
 26989 		 * // Get absolute URL from the location of the API
       
 26990 		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
       
 26991 		 */
       
 26992 		self.baseURI = baseUri;
       
 26993 
       
 26994 		/**
       
 26995 		 * Array with CSS files to load into the iframe.
       
 26996 		 *
       
 26997 		 * @property contentCSS
       
 26998 		 * @type Array
       
 26999 		 */
       
 27000 		self.contentCSS = [];
       
 27001 
       
 27002 		/**
       
 27003 		 * Array of CSS styles to add to head of document when the editor loads.
       
 27004 		 *
       
 27005 		 * @property contentStyles
       
 27006 		 * @type Array
       
 27007 		 */
       
 27008 		self.contentStyles = [];
       
 27009 
       
 27010 		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
       
 27011 		self.shortcuts = new Shortcuts(self);
       
 27012 		self.loadedCSS = {};
       
 27013 		self.editorCommands = new EditorCommands(self);
       
 27014 
       
 27015 		if (settings.target) {
       
 27016 			self.targetElm = settings.target;
       
 27017 		}
       
 27018 
       
 27019 		self.suffix = editorManager.suffix;
       
 27020 		self.editorManager = editorManager;
       
 27021 		self.inline = settings.inline;
       
 27022 
       
 27023 		if (settings.cache_suffix) {
       
 27024 			Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, '');
       
 27025 		}
       
 27026 
       
 27027 		// Call setup
       
 27028 		editorManager.fire('SetupEditor', self);
       
 27029 		self.execCallback('setup', self);
       
 27030 
       
 27031 		/**
       
 27032 		 * Dom query instance with default scope to the editor document and default element is the body of the editor.
       
 27033 		 *
       
 27034 		 * @property $
       
 27035 		 * @type tinymce.dom.DomQuery
       
 27036 		 * @example
       
 27037 		 * tinymce.activeEditor.$('p').css('color', 'red');
       
 27038 		 * tinymce.activeEditor.$().append('<p>new</p>');
       
 27039 		 */
       
 27040 		self.$ = DomQuery.overrideDefaults(function() {
       
 27041 			return {
       
 27042 				context: self.inline ? self.getBody() : self.getDoc(),
       
 27043 				element: self.getBody()
       
 27044 			};
       
 27045 		});
       
 27046 	}
       
 27047 
       
 27048 	Editor.prototype = {
       
 27049 		/**
       
 27050 		 * Renderes the editor/adds it to the page.
       
 27051 		 *
       
 27052 		 * @method render
       
 27053 		 */
       
 27054 		render: function() {
       
 27055 			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
       
 27056 
       
 27057 			function readyHandler() {
       
 27058 				DOM.unbind(window, 'ready', readyHandler);
       
 27059 				self.render();
       
 27060 			}
       
 27061 
       
 27062 			// Page is not loaded yet, wait for it
       
 27063 			if (!Event.domLoaded) {
       
 27064 				DOM.bind(window, 'ready', readyHandler);
       
 27065 				return;
       
 27066 			}
       
 27067 
       
 27068 			// Element not found, then skip initialization
       
 27069 			if (!self.getElement()) {
       
 27070 				return;
       
 27071 			}
       
 27072 
       
 27073 			// No editable support old iOS versions etc
       
 27074 			if (!Env.contentEditable) {
       
 27075 				return;
       
 27076 			}
       
 27077 
       
 27078 			// Hide target element early to prevent content flashing
       
 27079 			if (!settings.inline) {
       
 27080 				self.orgVisibility = self.getElement().style.visibility;
       
 27081 				self.getElement().style.visibility = 'hidden';
       
 27082 			} else {
       
 27083 				self.inline = true;
       
 27084 			}
       
 27085 
       
 27086 			var form = self.getElement().form || DOM.getParent(id, 'form');
       
 27087 			if (form) {
       
 27088 				self.formElement = form;
       
 27089 
       
 27090 				// Add hidden input for non input elements inside form elements
       
 27091 				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
       
 27092 					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
       
 27093 					self.hasHiddenInput = true;
       
 27094 				}
       
 27095 
       
 27096 				// Pass submit/reset from form to editor instance
       
 27097 				self.formEventDelegate = function(e) {
       
 27098 					self.fire(e.type, e);
       
 27099 				};
       
 27100 
       
 27101 				DOM.bind(form, 'submit reset', self.formEventDelegate);
       
 27102 
       
 27103 				// Reset contents in editor when the form is reset
       
 27104 				self.on('reset', function() {
       
 27105 					self.setContent(self.startContent, {format: 'raw'});
       
 27106 				});
       
 27107 
       
 27108 				// Check page uses id="submit" or name="submit" for it's submit button
       
 27109 				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
       
 27110 					form._mceOldSubmit = form.submit;
       
 27111 					form.submit = function() {
       
 27112 						self.editorManager.triggerSave();
       
 27113 						self.isNotDirty = true;
       
 27114 
       
 27115 						return form._mceOldSubmit(form);
       
 27116 					};
       
 27117 				}
       
 27118 			}
       
 27119 
       
 27120 			/**
       
 27121 			 * Window manager reference, use this to open new windows and dialogs.
       
 27122 			 *
       
 27123 			 * @property windowManager
       
 27124 			 * @type tinymce.WindowManager
       
 27125 			 * @example
       
 27126 			 * // Shows an alert message
       
 27127 			 * tinymce.activeEditor.windowManager.alert('Hello world!');
       
 27128 			 *
       
 27129 			 * // Opens a new dialog with the file.htm file and the size 320x240
       
 27130 			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
       
 27131 			 * tinymce.activeEditor.windowManager.open({
       
 27132 			 *    url: 'file.htm',
       
 27133 			 *    width: 320,
       
 27134 			 *    height: 240
       
 27135 			 * }, {
       
 27136 			 *    custom_param: 1
       
 27137 			 * });
       
 27138 			 */
       
 27139 			self.windowManager = new WindowManager(self);
       
 27140 
       
 27141 			if (settings.encoding == 'xml') {
       
 27142 				self.on('GetContent', function(e) {
       
 27143 					if (e.save) {
       
 27144 						e.content = DOM.encode(e.content);
       
 27145 					}
       
 27146 				});
       
 27147 			}
       
 27148 
       
 27149 			if (settings.add_form_submit_trigger) {
       
 27150 				self.on('submit', function() {
       
 27151 					if (self.initialized) {
       
 27152 						self.save();
       
 27153 					}
       
 27154 				});
       
 27155 			}
       
 27156 
       
 27157 			if (settings.add_unload_trigger) {
       
 27158 				self._beforeUnload = function() {
       
 27159 					if (self.initialized && !self.destroyed && !self.isHidden()) {
       
 27160 						self.save({format: 'raw', no_events: true, set_dirty: false});
       
 27161 					}
       
 27162 				};
       
 27163 
       
 27164 				self.editorManager.on('BeforeUnload', self._beforeUnload);
       
 27165 			}
       
 27166 
       
 27167 			// Load scripts
       
 27168 			function loadScripts() {
       
 27169 				var scriptLoader = ScriptLoader.ScriptLoader;
       
 27170 
       
 27171 				if (settings.language && settings.language != 'en' && !settings.language_url) {
       
 27172 					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
       
 27173 				}
       
 27174 
       
 27175 				if (settings.language_url) {
       
 27176 					scriptLoader.add(settings.language_url);
       
 27177 				}
       
 27178 
       
 27179 				if (settings.theme && typeof settings.theme != "function" &&
       
 27180 					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
       
 27181 					var themeUrl = settings.theme_url;
       
 27182 
       
 27183 					if (themeUrl) {
       
 27184 						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
       
 27185 					} else {
       
 27186 						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
       
 27187 					}
       
 27188 
       
 27189 					ThemeManager.load(settings.theme, themeUrl);
       
 27190 				}
       
 27191 
       
 27192 				if (Tools.isArray(settings.plugins)) {
       
 27193 					settings.plugins = settings.plugins.join(' ');
       
 27194 				}
       
 27195 
       
 27196 				each(settings.external_plugins, function(url, name) {
       
 27197 					PluginManager.load(name, url);
       
 27198 					settings.plugins += ' ' + name;
       
 27199 				});
       
 27200 
       
 27201 				each(settings.plugins.split(/[ ,]/), function(plugin) {
       
 27202 					plugin = trim(plugin);
       
 27203 
       
 27204 					if (plugin && !PluginManager.urls[plugin]) {
       
 27205 						if (plugin.charAt(0) == '-') {
       
 27206 							plugin = plugin.substr(1, plugin.length);
       
 27207 
       
 27208 							var dependencies = PluginManager.dependencies(plugin);
       
 27209 
       
 27210 							each(dependencies, function(dep) {
       
 27211 								var defaultSettings = {
       
 27212 									prefix: 'plugins/',
       
 27213 									resource: dep,
       
 27214 									suffix: '/plugin' + suffix + '.js'
       
 27215 								};
       
 27216 
       
 27217 								dep = PluginManager.createUrl(defaultSettings, dep);
       
 27218 								PluginManager.load(dep.resource, dep);
       
 27219 							});
       
 27220 						} else {
       
 27221 							PluginManager.load(plugin, {
       
 27222 								prefix: 'plugins/',
       
 27223 								resource: plugin,
       
 27224 								suffix: '/plugin' + suffix + '.js'
       
 27225 							});
       
 27226 						}
       
 27227 					}
       
 27228 				});
       
 27229 
       
 27230 				scriptLoader.loadQueue(function() {
       
 27231 					if (!self.removed) {
       
 27232 						self.init();
       
 27233 					}
       
 27234 				});
       
 27235 			}
       
 27236 
       
 27237 			loadScripts();
       
 27238 		},
       
 27239 
       
 27240 		/**
       
 27241 		 * Initializes the editor this will be called automatically when
       
 27242 		 * all plugins/themes and language packs are loaded by the rendered method.
       
 27243 		 * This method will setup the iframe and create the theme and plugin instances.
       
 27244 		 *
       
 27245 		 * @method init
       
 27246 		 */
       
 27247 		init: function() {
       
 27248 			var self = this, settings = self.settings, elm = self.getElement();
       
 27249 			var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
       
 27250 
       
 27251 			this.editorManager.i18n.setCode(settings.language);
       
 27252 			self.rtl = this.editorManager.i18n.rtl;
       
 27253 			self.editorManager.add(self);
       
 27254 
       
 27255 			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
       
 27256 
       
 27257 			/**
       
 27258 			 * Reference to the theme instance that was used to generate the UI.
       
 27259 			 *
       
 27260 			 * @property theme
       
 27261 			 * @type tinymce.Theme
       
 27262 			 * @example
       
 27263 			 * // Executes a method on the theme directly
       
 27264 			 * tinymce.activeEditor.theme.someMethod();
       
 27265 			 */
       
 27266 			if (settings.theme) {
       
 27267 				if (typeof settings.theme != "function") {
       
 27268 					settings.theme = settings.theme.replace(/-/, '');
       
 27269 					Theme = ThemeManager.get(settings.theme);
       
 27270 					self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
       
 27271 
       
 27272 					if (self.theme.init) {
       
 27273 						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''), self.$);
       
 27274 					}
       
 27275 				} else {
       
 27276 					self.theme = settings.theme;
       
 27277 				}
       
 27278 			}
       
 27279 
       
 27280 			function initPlugin(plugin) {
       
 27281 				var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
       
 27282 
       
 27283 				pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
       
 27284 				plugin = trim(plugin);
       
 27285 				if (Plugin && inArray(initializedPlugins, plugin) === -1) {
       
 27286 					each(PluginManager.dependencies(plugin), function(dep) {
       
 27287 						initPlugin(dep);
       
 27288 					});
       
 27289 
       
 27290 					pluginInstance = new Plugin(self, pluginUrl, self.$);
       
 27291 
       
 27292 					self.plugins[plugin] = pluginInstance;
       
 27293 
       
 27294 					if (pluginInstance.init) {
       
 27295 						pluginInstance.init(self, pluginUrl);
       
 27296 						initializedPlugins.push(plugin);
       
 27297 					}
       
 27298 				}
       
 27299 			}
       
 27300 
       
 27301 			// Create all plugins
       
 27302 			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
       
 27303 
       
 27304 			// Measure box
       
 27305 			if (settings.render_ui && self.theme) {
       
 27306 				self.orgDisplay = elm.style.display;
       
 27307 
       
 27308 				if (typeof settings.theme != "function") {
       
 27309 					w = settings.width || elm.style.width || elm.offsetWidth;
       
 27310 					h = settings.height || elm.style.height || elm.offsetHeight;
       
 27311 					minHeight = settings.min_height || 100;
       
 27312 					re = /^[0-9\.]+(|px)$/i;
       
 27313 
       
 27314 					if (re.test('' + w)) {
       
 27315 						w = Math.max(parseInt(w, 10), 100);
       
 27316 					}
       
 27317 
       
 27318 					if (re.test('' + h)) {
       
 27319 						h = Math.max(parseInt(h, 10), minHeight);
       
 27320 					}
       
 27321 
       
 27322 					// Render UI
       
 27323 					o = self.theme.renderUI({
       
 27324 						targetNode: elm,
       
 27325 						width: w,
       
 27326 						height: h,
       
 27327 						deltaWidth: settings.delta_width,
       
 27328 						deltaHeight: settings.delta_height
       
 27329 					});
       
 27330 
       
 27331 					// Resize editor
       
 27332 					if (!settings.content_editable) {
       
 27333 						h = (o.iframeHeight || h) + (typeof h == 'number' ? (o.deltaHeight || 0) : '');
       
 27334 						if (h < minHeight) {
       
 27335 							h = minHeight;
       
 27336 						}
       
 27337 					}
       
 27338 				} else {
       
 27339 					o = settings.theme(self, elm);
       
 27340 
       
 27341 					// Convert element type to id:s
       
 27342 					if (o.editorContainer.nodeType) {
       
 27343 						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
       
 27344 					}
       
 27345 
       
 27346 					// Convert element type to id:s
       
 27347 					if (o.iframeContainer.nodeType) {
       
 27348 						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
       
 27349 					}
       
 27350 
       
 27351 					// Use specified iframe height or the targets offsetHeight
       
 27352 					h = o.iframeHeight || elm.offsetHeight;
       
 27353 				}
       
 27354 
       
 27355 				self.editorContainer = o.editorContainer;
       
 27356 			}
       
 27357 
       
 27358 			// Load specified content CSS last
       
 27359 			if (settings.content_css) {
       
 27360 				each(explode(settings.content_css), function(u) {
       
 27361 					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
       
 27362 				});
       
 27363 			}
       
 27364 
       
 27365 			// Load specified content CSS last
       
 27366 			if (settings.content_style) {
       
 27367 				self.contentStyles.push(settings.content_style);
       
 27368 			}
       
 27369 
       
 27370 			// Content editable mode ends here
       
 27371 			if (settings.content_editable) {
       
 27372 				elm = n = o = null; // Fix IE leak
       
 27373 				return self.initContentBody();
       
 27374 			}
       
 27375 
       
 27376 			self.iframeHTML = settings.doctype + '<html><head>';
       
 27377 
       
 27378 			// We only need to override paths if we have to
       
 27379 			// IE has a bug where it remove site absolute urls to relative ones if this is specified
       
 27380 			if (settings.document_base_url != self.documentBaseUrl) {
       
 27381 				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
       
 27382 			}
       
 27383 
       
 27384 			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
       
 27385 			if (!Env.caretAfter && settings.ie7_compat) {
       
 27386 				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
       
 27387 			}
       
 27388 
       
 27389 			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
       
 27390 
       
 27391 			// Load the CSS by injecting them into the HTML this will reduce "flicker"
       
 27392 			for (i = 0; i < self.contentCSS.length; i++) {
       
 27393 				var cssUrl = self.contentCSS[i];
       
 27394 				self.iframeHTML += (
       
 27395 					'<link type="text/css" ' +
       
 27396 						'rel="stylesheet" ' +
       
 27397 						'href="' + Tools._addCacheSuffix(cssUrl) + '" />'
       
 27398 				);
       
 27399 				self.loadedCSS[cssUrl] = true;
       
 27400 			}
       
 27401 
       
 27402 			bodyId = settings.body_id || 'tinymce';
       
 27403 			if (bodyId.indexOf('=') != -1) {
       
 27404 				bodyId = self.getParam('body_id', '', 'hash');
       
 27405 				bodyId = bodyId[self.id] || bodyId;
       
 27406 			}
       
 27407 
       
 27408 			bodyClass = settings.body_class || '';
       
 27409 			if (bodyClass.indexOf('=') != -1) {
       
 27410 				bodyClass = self.getParam('body_class', '', 'hash');
       
 27411 				bodyClass = bodyClass[self.id] || '';
       
 27412 			}
       
 27413 
       
 27414 			if (settings.content_security_policy) {
       
 27415 				self.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />';
       
 27416 			}
       
 27417 
       
 27418 			self.iframeHTML += '</head><body id="' + bodyId +
       
 27419 				'" class="mce-content-body ' + bodyClass +
       
 27420 				'" data-id="' + self.id + '"><br></body></html>';
       
 27421 
       
 27422 			/*eslint no-script-url:0 */
       
 27423 			var domainRelaxUrl = 'javascript:(function(){' +
       
 27424 				'document.open();document.domain="' + document.domain + '";' +
       
 27425 				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
       
 27426 				'document.close();ed.initContentBody(true);})()';
       
 27427 
       
 27428 			// Domain relaxing is required since the user has messed around with document.domain
       
 27429 			if (document.domain != location.hostname) {
       
 27430 				url = domainRelaxUrl;
       
 27431 			}
       
 27432 
       
 27433 			// Create iframe
       
 27434 			// TODO: ACC add the appropriate description on this.
       
 27435 			var ifr = DOM.create('iframe', {
       
 27436 				id: self.id + "_ifr",
       
 27437 				//src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
       
 27438 				frameBorder: '0',
       
 27439 				allowTransparency: "true",
       
 27440 				title: self.editorManager.translate(
       
 27441 						"Rich Text Area. Press ALT-F9 for menu. " +
       
 27442 						"Press ALT-F10 for toolbar. Press ALT-0 for help"
       
 27443 				),
       
 27444 				style: {
       
 27445 					width: '100%',
       
 27446 					height: h,
       
 27447 					display: 'block' // Important for Gecko to render the iframe correctly
       
 27448 				}
       
 27449 			});
       
 27450 
       
 27451 			ifr.onload = function() {
       
 27452 				ifr.onload = null;
       
 27453 				self.fire("load");
       
 27454 			};
       
 27455 
       
 27456 			DOM.setAttrib(ifr, "src", url || 'javascript:""');
       
 27457 
       
 27458 			self.contentAreaContainer = o.iframeContainer;
       
 27459 			self.iframeElement = ifr;
       
 27460 
       
 27461 			n = DOM.add(o.iframeContainer, ifr);
       
 27462 
       
 27463 			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
       
 27464 			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
       
 27465 			if (ie) {
       
 27466 				try {
       
 27467 					self.getDoc();
       
 27468 				} catch (e) {
       
 27469 					n.src = url = domainRelaxUrl;
       
 27470 				}
       
 27471 			}
       
 27472 
       
 27473 			if (o.editorContainer) {
       
 27474 				DOM.get(o.editorContainer).style.display = self.orgDisplay;
       
 27475 				self.hidden = DOM.isHidden(o.editorContainer);
       
 27476 			}
       
 27477 
       
 27478 			self.getElement().style.display = 'none';
       
 27479 			DOM.setAttrib(self.id, 'aria-hidden', true);
       
 27480 
       
 27481 			if (!url) {
       
 27482 				self.initContentBody();
       
 27483 			}
       
 27484 
       
 27485 			elm = n = o = null; // Cleanup
       
 27486 		},
       
 27487 
       
 27488 		/**
       
 27489 		 * This method get called by the init method ones the iframe is loaded.
       
 27490 		 * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
       
 27491 		 *
       
 27492 		 * @method initContentBody
       
 27493 		 * @private
       
 27494 		 */
       
 27495 		initContentBody: function(skipWrite) {
       
 27496 			var self = this, settings = self.settings, targetElm = self.getElement(), doc = self.getDoc(), body, contentCssText;
       
 27497 
       
 27498 			// Restore visibility on target element
       
 27499 			if (!settings.inline) {
       
 27500 				self.getElement().style.visibility = self.orgVisibility;
       
 27501 			}
       
 27502 
       
 27503 			// Setup iframe body
       
 27504 			if (!skipWrite && !settings.content_editable) {
       
 27505 				doc.open();
       
 27506 				doc.write(self.iframeHTML);
       
 27507 				doc.close();
       
 27508 			}
       
 27509 
       
 27510 			if (settings.content_editable) {
       
 27511 				self.on('remove', function() {
       
 27512 					var bodyEl = this.getBody();
       
 27513 
       
 27514 					DOM.removeClass(bodyEl, 'mce-content-body');
       
 27515 					DOM.removeClass(bodyEl, 'mce-edit-focus');
       
 27516 					DOM.setAttrib(bodyEl, 'contentEditable', null);
       
 27517 				});
       
 27518 
       
 27519 				DOM.addClass(targetElm, 'mce-content-body');
       
 27520 				self.contentDocument = doc = settings.content_document || document;
       
 27521 				self.contentWindow = settings.content_window || window;
       
 27522 				self.bodyElement = targetElm;
       
 27523 
       
 27524 				// Prevent leak in IE
       
 27525 				settings.content_document = settings.content_window = null;
       
 27526 
       
 27527 				// TODO: Fix this
       
 27528 				settings.root_name = targetElm.nodeName.toLowerCase();
       
 27529 			}
       
 27530 
       
 27531 			// It will not steal focus while setting contentEditable
       
 27532 			body = self.getBody();
       
 27533 			body.disabled = true;
       
 27534 
       
 27535 			if (!settings.readonly) {
       
 27536 				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
       
 27537 					body.style.position = 'relative';
       
 27538 				}
       
 27539 
       
 27540 				body.contentEditable = self.getParam('content_editable_state', true);
       
 27541 			}
       
 27542 
       
 27543 			body.disabled = false;
       
 27544 
       
 27545 			/**
       
 27546 			 * Schema instance, enables you to validate elements and it's children.
       
 27547 			 *
       
 27548 			 * @property schema
       
 27549 			 * @type tinymce.html.Schema
       
 27550 			 */
       
 27551 			self.schema = new Schema(settings);
       
 27552 
       
 27553 			/**
       
 27554 			 * DOM instance for the editor.
       
 27555 			 *
       
 27556 			 * @property dom
       
 27557 			 * @type tinymce.dom.DOMUtils
       
 27558 			 * @example
       
 27559 			 * // Adds a class to all paragraphs within the editor
       
 27560 			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
       
 27561 			 */
       
 27562 			self.dom = new DOMUtils(doc, {
       
 27563 				keep_values: true,
       
 27564 				url_converter: self.convertURL,
       
 27565 				url_converter_scope: self,
       
 27566 				hex_colors: settings.force_hex_style_colors,
       
 27567 				class_filter: settings.class_filter,
       
 27568 				update_styles: true,
       
 27569 				root_element: self.inline ? self.getBody() : null,
       
 27570 				collect: settings.content_editable,
       
 27571 				schema: self.schema,
       
 27572 				onSetAttrib: function(e) {
       
 27573 					self.fire('SetAttrib', e);
       
 27574 				}
       
 27575 			});
       
 27576 
       
 27577 			/**
       
 27578 			 * HTML parser will be used when contents is inserted into the editor.
       
 27579 			 *
       
 27580 			 * @property parser
       
 27581 			 * @type tinymce.html.DomParser
       
 27582 			 */
       
 27583 			self.parser = new DomParser(settings, self.schema);
       
 27584 
       
 27585 			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
       
 27586 			self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
       
 27587 				var i = nodes.length, node, dom = self.dom, value, internalName;
       
 27588 
       
 27589 				while (i--) {
       
 27590 					node = nodes[i];
       
 27591 					value = node.attr(name);
       
 27592 					internalName = 'data-mce-' + name;
       
 27593 
       
 27594 					// Add internal attribute if we need to we don't on a refresh of the document
       
 27595 					if (!node.attributes.map[internalName]) {
       
 27596 						if (name === "style") {
       
 27597 							value = dom.serializeStyle(dom.parseStyle(value), node.name);
       
 27598 
       
 27599 							if (!value.length) {
       
 27600 								value = null;
       
 27601 							}
       
 27602 
       
 27603 							node.attr(internalName, value);
       
 27604 							node.attr(name, value);
       
 27605 						} else if (name === "tabindex") {
       
 27606 							node.attr(internalName, value);
       
 27607 							node.attr(name, null);
       
 27608 						} else {
       
 27609 							node.attr(internalName, self.convertURL(value, name, node.name));
       
 27610 						}
       
 27611 					}
       
 27612 				}
       
 27613 			});
       
 27614 
       
 27615 			// Keep scripts from executing
       
 27616 			self.parser.addNodeFilter('script', function(nodes) {
       
 27617 				var i = nodes.length, node;
       
 27618 
       
 27619 				while (i--) {
       
 27620 					node = nodes[i];
       
 27621 					node.attr('type', 'mce-' + (node.attr('type') || 'no/type'));
       
 27622 				}
       
 27623 			});
       
 27624 
       
 27625 			self.parser.addNodeFilter('#cdata', function(nodes) {
       
 27626 				var i = nodes.length, node;
       
 27627 
       
 27628 				while (i--) {
       
 27629 					node = nodes[i];
       
 27630 					node.type = 8;
       
 27631 					node.name = '#comment';
       
 27632 					node.value = '[CDATA[' + node.value + ']]';
       
 27633 				}
       
 27634 			});
       
 27635 
       
 27636 			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
       
 27637 				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
       
 27638 
       
 27639 				while (i--) {
       
 27640 					node = nodes[i];
       
 27641 
       
 27642 					if (node.isEmpty(nonEmptyElements)) {
       
 27643 						node.append(new Node('br', 1)).shortEnded = true;
       
 27644 					}
       
 27645 				}
       
 27646 			});
       
 27647 
       
 27648 			/**
       
 27649 			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
       
 27650 			 *
       
 27651 			 * @property serializer
       
 27652 			 * @type tinymce.dom.Serializer
       
 27653 			 * @example
       
 27654 			 * // Serializes the first paragraph in the editor into a string
       
 27655 			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
       
 27656 			 */
       
 27657 			self.serializer = new DomSerializer(settings, self);
       
 27658 
       
 27659 			/**
       
 27660 			 * Selection instance for the editor.
       
 27661 			 *
       
 27662 			 * @property selection
       
 27663 			 * @type tinymce.dom.Selection
       
 27664 			 * @example
       
 27665 			 * // Sets some contents to the current selection in the editor
       
 27666 			 * tinymce.activeEditor.selection.setContent('Some contents');
       
 27667 			 *
       
 27668 			 * // Gets the current selection
       
 27669 			 * alert(tinymce.activeEditor.selection.getContent());
       
 27670 			 *
       
 27671 			 * // Selects the first paragraph found
       
 27672 			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
       
 27673 			 */
       
 27674 			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
       
 27675 
       
 27676 			/**
       
 27677 			 * Formatter instance.
       
 27678 			 *
       
 27679 			 * @property formatter
       
 27680 			 * @type tinymce.Formatter
       
 27681 			 */
       
 27682 			self.formatter = new Formatter(self);
       
 27683 
       
 27684 			/**
       
 27685 			 * Undo manager instance, responsible for handling undo levels.
       
 27686 			 *
       
 27687 			 * @property undoManager
       
 27688 			 * @type tinymce.UndoManager
       
 27689 			 * @example
       
 27690 			 * // Undoes the last modification to the editor
       
 27691 			 * tinymce.activeEditor.undoManager.undo();
       
 27692 			 */
       
 27693 			self.undoManager = new UndoManager(self);
       
 27694 
       
 27695 			self.forceBlocks = new ForceBlocks(self);
       
 27696 			self.enterKey = new EnterKey(self);
       
 27697 			self._nodeChangeDispatcher = new NodeChange(self);
       
 27698 
       
 27699 			self.fire('PreInit');
       
 27700 
       
 27701 			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
       
 27702 				doc.body.spellcheck = false; // Gecko
       
 27703 				DOM.setAttrib(body, "spellcheck", "false");
       
 27704 			}
       
 27705 
       
 27706 			self.fire('PostRender');
       
 27707 
       
 27708 			self.quirks = new Quirks(self);
       
 27709 
       
 27710 			if (settings.directionality) {
       
 27711 				body.dir = settings.directionality;
       
 27712 			}
       
 27713 
       
 27714 			if (settings.nowrap) {
       
 27715 				body.style.whiteSpace = "nowrap";
       
 27716 			}
       
 27717 
       
 27718 			if (settings.protect) {
       
 27719 				self.on('BeforeSetContent', function(e) {
       
 27720 					each(settings.protect, function(pattern) {
       
 27721 						e.content = e.content.replace(pattern, function(str) {
       
 27722 							return '<!--mce:protected ' + escape(str) + '-->';
       
 27723 						});
       
 27724 					});
       
 27725 				});
       
 27726 			}
       
 27727 
       
 27728 			self.on('SetContent', function() {
       
 27729 				self.addVisual(self.getBody());
       
 27730 			});
       
 27731 
       
 27732 			// Remove empty contents
       
 27733 			if (settings.padd_empty_editor) {
       
 27734 				self.on('PostProcess', function(e) {
       
 27735 					e.content = e.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
       
 27736 				});
       
 27737 			}
       
 27738 
       
 27739 			self.load({initial: true, format: 'html'});
       
 27740 			self.startContent = self.getContent({format: 'raw'});
       
 27741 
       
 27742 			/**
       
 27743 			 * Is set to true after the editor instance has been initialized
       
 27744 			 *
       
 27745 			 * @property initialized
       
 27746 			 * @type Boolean
       
 27747 			 * @example
       
 27748 			 * function isEditorInitialized(editor) {
       
 27749 			 *     return editor && editor.initialized;
       
 27750 			 * }
       
 27751 			 */
       
 27752 			self.initialized = true;
       
 27753 			self.bindPendingEventDelegates();
       
 27754 
       
 27755 			self.fire('init');
       
 27756 			self.focus(true);
       
 27757 			self.nodeChanged({initial: true});
       
 27758 			self.execCallback('init_instance_callback', self);
       
 27759 
       
 27760 			// Add editor specific CSS styles
       
 27761 			if (self.contentStyles.length > 0) {
       
 27762 				contentCssText = '';
       
 27763 
       
 27764 				each(self.contentStyles, function(style) {
       
 27765 					contentCssText += style + "\r\n";
       
 27766 				});
       
 27767 
       
 27768 				self.dom.addStyle(contentCssText);
       
 27769 			}
       
 27770 
       
 27771 			// Load specified content CSS last
       
 27772 			each(self.contentCSS, function(cssUrl) {
       
 27773 				if (!self.loadedCSS[cssUrl]) {
       
 27774 					self.dom.loadCSS(cssUrl);
       
 27775 					self.loadedCSS[cssUrl] = true;
       
 27776 				}
       
 27777 			});
       
 27778 
       
 27779 			// Handle auto focus
       
 27780 			if (settings.auto_focus) {
       
 27781 				setTimeout(function() {
       
 27782 					var editor;
       
 27783 
       
 27784 					if (settings.auto_focus === true) {
       
 27785 						editor = self;
       
 27786 					} else {
       
 27787 						editor = self.editorManager.get(settings.auto_focus);
       
 27788 					}
       
 27789 
       
 27790 					if (!editor.destroyed) {
       
 27791 						editor.focus();
       
 27792 					}
       
 27793 				}, 100);
       
 27794 			}
       
 27795 
       
 27796 			// Clean up references for IE
       
 27797 			targetElm = doc = body = null;
       
 27798 		},
       
 27799 
       
 27800 		/**
       
 27801 		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
       
 27802 		 * it will also place DOM focus inside the editor.
       
 27803 		 *
       
 27804 		 * @method focus
       
 27805 		 * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor.
       
 27806 		 */
       
 27807 		focus: function(skipFocus) {
       
 27808 			var self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
       
 27809 			var controlElm, doc = self.getDoc(), body;
       
 27810 
       
 27811 			if (!skipFocus) {
       
 27812 				// Get selected control element
       
 27813 				rng = selection.getRng();
       
 27814 				if (rng.item) {
       
 27815 					controlElm = rng.item(0);
       
 27816 				}
       
 27817 
       
 27818 				self._refreshContentEditable();
       
 27819 
       
 27820 				// Focus the window iframe
       
 27821 				if (!contentEditable) {
       
 27822 					// WebKit needs this call to fire focusin event properly see #5948
       
 27823 					// But Opera pre Blink engine will produce an empty selection so skip Opera
       
 27824 					if (!Env.opera) {
       
 27825 						self.getBody().focus();
       
 27826 					}
       
 27827 
       
 27828 					self.getWin().focus();
       
 27829 				}
       
 27830 
       
 27831 				// Focus the body as well since it's contentEditable
       
 27832 				if (isGecko || contentEditable) {
       
 27833 					body = self.getBody();
       
 27834 
       
 27835 					// Check for setActive since it doesn't scroll to the element
       
 27836 					if (body.setActive) {
       
 27837 						// IE 11 sometimes throws "Invalid function" then fallback to focus
       
 27838 						try {
       
 27839 							body.setActive();
       
 27840 						} catch (ex) {
       
 27841 							body.focus();
       
 27842 						}
       
 27843 					} else {
       
 27844 						body.focus();
       
 27845 					}
       
 27846 
       
 27847 					if (contentEditable) {
       
 27848 						selection.normalize();
       
 27849 					}
       
 27850 				}
       
 27851 
       
 27852 				// Restore selected control element
       
 27853 				// This is needed when for example an image is selected within a
       
 27854 				// layer a call to focus will then remove the control selection
       
 27855 				if (controlElm && controlElm.ownerDocument == doc) {
       
 27856 					rng = doc.body.createControlRange();
       
 27857 					rng.addElement(controlElm);
       
 27858 					rng.select();
       
 27859 				}
       
 27860 			}
       
 27861 
       
 27862 			self.editorManager.setActive(self);
       
 27863 		},
       
 27864 
       
 27865 		/**
       
 27866 		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
       
 27867 		 * There new event model is a better way to add callback so this method might be removed in the future.
       
 27868 		 *
       
 27869 		 * @method execCallback
       
 27870 		 * @param {String} name Name of the callback to execute.
       
 27871 		 * @return {Object} Return value passed from callback function.
       
 27872 		 */
       
 27873 		execCallback: function(name) {
       
 27874 			var self = this, callback = self.settings[name], scope;
       
 27875 
       
 27876 			if (!callback) {
       
 27877 				return;
       
 27878 			}
       
 27879 
       
 27880 			// Look through lookup
       
 27881 			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
       
 27882 				callback = scope.func;
       
 27883 				scope = scope.scope;
       
 27884 			}
       
 27885 
       
 27886 			if (typeof callback === 'string') {
       
 27887 				scope = callback.replace(/\.\w+$/, '');
       
 27888 				scope = scope ? resolve(scope) : 0;
       
 27889 				callback = resolve(callback);
       
 27890 				self.callbackLookup = self.callbackLookup || {};
       
 27891 				self.callbackLookup[name] = {func: callback, scope: scope};
       
 27892 			}
       
 27893 
       
 27894 			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
       
 27895 		},
       
 27896 
       
 27897 		/**
       
 27898 		 * Translates the specified string by replacing variables with language pack items it will also check if there is
       
 27899 		 * a key mathcin the input.
       
 27900 		 *
       
 27901 		 * @method translate
       
 27902 		 * @param {String} text String to translate by the language pack data.
       
 27903 		 * @return {String} Translated string.
       
 27904 		 */
       
 27905 		translate: function(text) {
       
 27906 			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
       
 27907 
       
 27908 			if (!text) {
       
 27909 				return '';
       
 27910 			}
       
 27911 
       
 27912 			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
       
 27913 				return i18n.data[lang + '.' + b] || '{#' + b + '}';
       
 27914 			});
       
 27915 		},
       
 27916 
       
 27917 		/**
       
 27918 		 * Returns a language pack item by name/key.
       
 27919 		 *
       
 27920 		 * @method getLang
       
 27921 		 * @param {String} name Name/key to get from the language pack.
       
 27922 		 * @param {String} defaultVal Optional default value to retrive.
       
 27923 		 */
       
 27924 		getLang: function(name, defaultVal) {
       
 27925 			return (
       
 27926 				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
       
 27927 				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
       
 27928 			);
       
 27929 		},
       
 27930 
       
 27931 		/**
       
 27932 		 * Returns a configuration parameter by name.
       
 27933 		 *
       
 27934 		 * @method getParam
       
 27935 		 * @param {String} name Configruation parameter to retrive.
       
 27936 		 * @param {String} defaultVal Optional default value to return.
       
 27937 		 * @param {String} type Optional type parameter.
       
 27938 		 * @return {String} Configuration parameter value or default value.
       
 27939 		 * @example
       
 27940 		 * // Returns a specific config value from the currently active editor
       
 27941 		 * var someval = tinymce.activeEditor.getParam('myvalue');
       
 27942 		 *
       
 27943 		 * // Returns a specific config value from a specific editor instance by id
       
 27944 		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
       
 27945 		 */
       
 27946 		getParam: function(name, defaultVal, type) {
       
 27947 			var value = name in this.settings ? this.settings[name] : defaultVal, output;
       
 27948 
       
 27949 			if (type === 'hash') {
       
 27950 				output = {};
       
 27951 
       
 27952 				if (typeof value === 'string') {
       
 27953 					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
       
 27954 						value = value.split('=');
       
 27955 
       
 27956 						if (value.length > 1) {
       
 27957 							output[trim(value[0])] = trim(value[1]);
       
 27958 						} else {
       
 27959 							output[trim(value[0])] = trim(value);
       
 27960 						}
       
 27961 					});
       
 27962 				} else {
       
 27963 					output = value;
       
 27964 				}
       
 27965 
       
 27966 				return output;
       
 27967 			}
       
 27968 
       
 27969 			return value;
       
 27970 		},
       
 27971 
       
 27972 		/**
       
 27973 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
       
 27974 		 * need to update the UI states or element path etc.
       
 27975 		 *
       
 27976 		 * @method nodeChanged
       
 27977 		 * @param {Object} args Optional args to pass to NodeChange event handlers.
       
 27978 		 */
       
 27979 		nodeChanged: function(args) {
       
 27980 			this._nodeChangeDispatcher.nodeChanged(args);
       
 27981 		},
       
 27982 
       
 27983 		/**
       
 27984 		 * Adds a button that later gets created by the theme in the editors toolbars.
       
 27985 		 *
       
 27986 		 * @method addButton
       
 27987 		 * @param {String} name Button name to add.
       
 27988 		 * @param {Object} settings Settings object with title, cmd etc.
       
 27989 		 * @example
       
 27990 		 * // Adds a custom button to the editor that inserts contents when clicked
       
 27991 		 * tinymce.init({
       
 27992 		 *    ...
       
 27993 		 *
       
 27994 		 *    toolbar: 'example'
       
 27995 		 *
       
 27996 		 *    setup: function(ed) {
       
 27997 		 *       ed.addButton('example', {
       
 27998 		 *          title: 'My title',
       
 27999 		 *          image: '../js/tinymce/plugins/example/img/example.gif',
       
 28000 		 *          onclick: function() {
       
 28001 		 *             ed.insertContent('Hello world!!');
       
 28002 		 *          }
       
 28003 		 *       });
       
 28004 		 *    }
       
 28005 		 * });
       
 28006 		 */
       
 28007 		addButton: function(name, settings) {
       
 28008 			var self = this;
       
 28009 
       
 28010 			if (settings.cmd) {
       
 28011 				settings.onclick = function() {
       
 28012 					self.execCommand(settings.cmd);
       
 28013 				};
       
 28014 			}
       
 28015 
       
 28016 			if (!settings.text && !settings.icon) {
       
 28017 				settings.icon = name;
       
 28018 			}
       
 28019 
       
 28020 			self.buttons = self.buttons || {};
       
 28021 			settings.tooltip = settings.tooltip || settings.title;
       
 28022 			self.buttons[name] = settings;
       
 28023 		},
       
 28024 
       
 28025 		/**
       
 28026 		 * Adds a menu item to be used in the menus of the theme. There might be multiple instances
       
 28027 		 * of this menu item for example it might be used in the main menus of the theme but also in
       
 28028 		 * the context menu so make sure that it's self contained and supports multiple instances.
       
 28029 		 *
       
 28030 		 * @method addMenuItem
       
 28031 		 * @param {String} name Menu item name to add.
       
 28032 		 * @param {Object} settings Settings object with title, cmd etc.
       
 28033 		 * @example
       
 28034 		 * // Adds a custom menu item to the editor that inserts contents when clicked
       
 28035 		 * // The context option allows you to add the menu item to an existing default menu
       
 28036 		 * tinymce.init({
       
 28037 		 *    ...
       
 28038 		 *
       
 28039 		 *    setup: function(ed) {
       
 28040 		 *       ed.addMenuItem('example', {
       
 28041 		 *          text: 'My menu item',
       
 28042 		 *          context: 'tools',
       
 28043 		 *          onclick: function() {
       
 28044 		 *             ed.insertContent('Hello world!!');
       
 28045 		 *          }
       
 28046 		 *       });
       
 28047 		 *    }
       
 28048 		 * });
       
 28049 		 */
       
 28050 		addMenuItem: function(name, settings) {
       
 28051 			var self = this;
       
 28052 
       
 28053 			if (settings.cmd) {
       
 28054 				settings.onclick = function() {
       
 28055 					self.execCommand(settings.cmd);
       
 28056 				};
       
 28057 			}
       
 28058 
       
 28059 			self.menuItems = self.menuItems || {};
       
 28060 			self.menuItems[name] = settings;
       
 28061 		},
       
 28062 
       
 28063 		/**
       
 28064 		 * Adds a custom command to the editor, you can also override existing commands with this method.
       
 28065 		 * The command that you add can be executed with execCommand.
       
 28066 		 *
       
 28067 		 * @method addCommand
       
 28068 		 * @param {String} name Command name to add/override.
       
 28069 		 * @param {addCommandCallback} callback Function to execute when the command occurs.
       
 28070 		 * @param {Object} scope Optional scope to execute the function in.
       
 28071 		 * @example
       
 28072 		 * // Adds a custom command that later can be executed using execCommand
       
 28073 		 * tinymce.init({
       
 28074 		 *    ...
       
 28075 		 *
       
 28076 		 *    setup: function(ed) {
       
 28077 		 *       // Register example command
       
 28078 		 *       ed.addCommand('mycommand', function(ui, v) {
       
 28079 		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
       
 28080 		 *       });
       
 28081 		 *    }
       
 28082 		 * });
       
 28083 		 */
       
 28084 		addCommand: function(name, callback, scope) {
       
 28085 			/**
       
 28086 			 * Callback function that gets called when a command is executed.
       
 28087 			 *
       
 28088 			 * @callback addCommandCallback
       
 28089 			 * @param {Boolean} ui Display UI state true/false.
       
 28090 			 * @param {Object} value Optional value for command.
       
 28091 			 * @return {Boolean} True/false state if the command was handled or not.
       
 28092 			 */
       
 28093 			this.editorCommands.addCommand(name, callback, scope);
       
 28094 		},
       
 28095 
       
 28096 		/**
       
 28097 		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
       
 28098 		 * The command that you add can be executed with queryCommandState function.
       
 28099 		 *
       
 28100 		 * @method addQueryStateHandler
       
 28101 		 * @param {String} name Command name to add/override.
       
 28102 		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
       
 28103 		 * @param {Object} scope Optional scope to execute the function in.
       
 28104 		 */
       
 28105 		addQueryStateHandler: function(name, callback, scope) {
       
 28106 			/**
       
 28107 			 * Callback function that gets called when a queryCommandState is executed.
       
 28108 			 *
       
 28109 			 * @callback addQueryStateHandlerCallback
       
 28110 			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
       
 28111 			 */
       
 28112 			this.editorCommands.addQueryStateHandler(name, callback, scope);
       
 28113 		},
       
 28114 
       
 28115 		/**
       
 28116 		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
       
 28117 		 * The command that you add can be executed with queryCommandValue function.
       
 28118 		 *
       
 28119 		 * @method addQueryValueHandler
       
 28120 		 * @param {String} name Command name to add/override.
       
 28121 		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
       
 28122 		 * @param {Object} scope Optional scope to execute the function in.
       
 28123 		 */
       
 28124 		addQueryValueHandler: function(name, callback, scope) {
       
 28125 			/**
       
 28126 			 * Callback function that gets called when a queryCommandValue is executed.
       
 28127 			 *
       
 28128 			 * @callback addQueryValueHandlerCallback
       
 28129 			 * @return {Object} Value of the command or undefined.
       
 28130 			 */
       
 28131 			this.editorCommands.addQueryValueHandler(name, callback, scope);
       
 28132 		},
       
 28133 
       
 28134 		/**
       
 28135 		 * Adds a keyboard shortcut for some command or function.
       
 28136 		 *
       
 28137 		 * @method addShortcut
       
 28138 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
       
 28139 		 * @param {String} desc Text description for the command.
       
 28140 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
       
 28141 		 * @param {Object} sc Optional scope to execute the function in.
       
 28142 		 * @return {Boolean} true/false state if the shortcut was added or not.
       
 28143 		 */
       
 28144 		addShortcut: function(pattern, desc, cmdFunc, scope) {
       
 28145 			this.shortcuts.add(pattern, desc, cmdFunc, scope);
       
 28146 		},
       
 28147 
       
 28148 		/**
       
 28149 		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
       
 28150 		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
       
 28151 		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
       
 28152 		 * return true it will handle the command as a internal browser command.
       
 28153 		 *
       
 28154 		 * @method execCommand
       
 28155 		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
       
 28156 		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
       
 28157 		 * @param {mixed} value Optional command value, this can be anything.
       
 28158 		 * @param {Object} args Optional arguments object.
       
 28159 		 */
       
 28160 		execCommand: function(cmd, ui, value, args) {
       
 28161 			return this.editorCommands.execCommand(cmd, ui, value, args);
       
 28162 		},
       
 28163 
       
 28164 		/**
       
 28165 		 * Returns a command specific state, for example if bold is enabled or not.
       
 28166 		 *
       
 28167 		 * @method queryCommandState
       
 28168 		 * @param {string} cmd Command to query state from.
       
 28169 		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
       
 28170 		 */
       
 28171 		queryCommandState: function(cmd) {
       
 28172 			return this.editorCommands.queryCommandState(cmd);
       
 28173 		},
       
 28174 
       
 28175 		/**
       
 28176 		 * Returns a command specific value, for example the current font size.
       
 28177 		 *
       
 28178 		 * @method queryCommandValue
       
 28179 		 * @param {string} cmd Command to query value from.
       
 28180 		 * @return {Object} Command specific value, for example the current font size.
       
 28181 		 */
       
 28182 		queryCommandValue: function(cmd) {
       
 28183 			return this.editorCommands.queryCommandValue(cmd);
       
 28184 		},
       
 28185 
       
 28186 		/**
       
 28187 		 * Returns true/false if the command is supported or not.
       
 28188 		 *
       
 28189 		 * @method queryCommandSupported
       
 28190 		 * @param {String} cmd Command that we check support for.
       
 28191 		 * @return {Boolean} true/false if the command is supported or not.
       
 28192 		 */
       
 28193 		queryCommandSupported: function(cmd) {
       
 28194 			return this.editorCommands.queryCommandSupported(cmd);
       
 28195 		},
       
 28196 
       
 28197 		/**
       
 28198 		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
       
 28199 		 *
       
 28200 		 * @method show
       
 28201 		 */
       
 28202 		show: function() {
       
 28203 			var self = this;
       
 28204 
       
 28205 			if (self.hidden) {
       
 28206 				self.hidden = false;
       
 28207 
       
 28208 				if (self.inline) {
       
 28209 					self.getBody().contentEditable = true;
       
 28210 				} else {
       
 28211 					DOM.show(self.getContainer());
       
 28212 					DOM.hide(self.id);
       
 28213 				}
       
 28214 
       
 28215 				self.load();
       
 28216 				self.fire('show');
       
 28217 			}
       
 28218 		},
       
 28219 
       
 28220 		/**
       
 28221 		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
       
 28222 		 *
       
 28223 		 * @method hide
       
 28224 		 */
       
 28225 		hide: function() {
       
 28226 			var self = this, doc = self.getDoc();
       
 28227 
       
 28228 			if (!self.hidden) {
       
 28229 				// Fixed bug where IE has a blinking cursor left from the editor
       
 28230 				if (ie && doc && !self.inline) {
       
 28231 					doc.execCommand('SelectAll');
       
 28232 				}
       
 28233 
       
 28234 				// We must save before we hide so Safari doesn't crash
       
 28235 				self.save();
       
 28236 
       
 28237 				if (self.inline) {
       
 28238 					self.getBody().contentEditable = false;
       
 28239 
       
 28240 					// Make sure the editor gets blurred
       
 28241 					if (self == self.editorManager.focusedEditor) {
       
 28242 						self.editorManager.focusedEditor = null;
       
 28243 					}
       
 28244 				} else {
       
 28245 					DOM.hide(self.getContainer());
       
 28246 					DOM.setStyle(self.id, 'display', self.orgDisplay);
       
 28247 				}
       
 28248 
       
 28249 				self.hidden = true;
       
 28250 				self.fire('hide');
       
 28251 			}
       
 28252 		},
       
 28253 
       
 28254 		/**
       
 28255 		 * Returns true/false if the editor is hidden or not.
       
 28256 		 *
       
 28257 		 * @method isHidden
       
 28258 		 * @return {Boolean} True/false if the editor is hidden or not.
       
 28259 		 */
       
 28260 		isHidden: function() {
       
 28261 			return !!this.hidden;
       
 28262 		},
       
 28263 
       
 28264 		/**
       
 28265 		 * Sets the progress state, this will display a throbber/progess for the editor.
       
 28266 		 * This is ideal for asycronous operations like an AJAX save call.
       
 28267 		 *
       
 28268 		 * @method setProgressState
       
 28269 		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
       
 28270 		 * @param {Number} time Optional time to wait before the progress gets shown.
       
 28271 		 * @return {Boolean} Same as the input state.
       
 28272 		 * @example
       
 28273 		 * // Show progress for the active editor
       
 28274 		 * tinymce.activeEditor.setProgressState(true);
       
 28275 		 *
       
 28276 		 * // Hide progress for the active editor
       
 28277 		 * tinymce.activeEditor.setProgressState(false);
       
 28278 		 *
       
 28279 		 * // Show progress after 3 seconds
       
 28280 		 * tinymce.activeEditor.setProgressState(true, 3000);
       
 28281 		 */
       
 28282 		setProgressState: function(state, time) {
       
 28283 			this.fire('ProgressState', {state: state, time: time});
       
 28284 		},
       
 28285 
       
 28286 		/**
       
 28287 		 * Loads contents from the textarea or div element that got converted into an editor instance.
       
 28288 		 * This method will move the contents from that textarea or div into the editor by using setContent
       
 28289 		 * so all events etc that method has will get dispatched as well.
       
 28290 		 *
       
 28291 		 * @method load
       
 28292 		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
       
 28293 		 * @return {String} HTML string that got set into the editor.
       
 28294 		 */
       
 28295 		load: function(args) {
       
 28296 			var self = this, elm = self.getElement(), html;
       
 28297 
       
 28298 			if (elm) {
       
 28299 				args = args || {};
       
 28300 				args.load = true;
       
 28301 
       
 28302 				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
       
 28303 				args.element = elm;
       
 28304 
       
 28305 				if (!args.no_events) {
       
 28306 					self.fire('LoadContent', args);
       
 28307 				}
       
 28308 
       
 28309 				args.element = elm = null;
       
 28310 
       
 28311 				return html;
       
 28312 			}
       
 28313 		},
       
 28314 
       
 28315 		/**
       
 28316 		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
       
 28317 		 * This method will move the HTML contents from the editor into that textarea or div by getContent
       
 28318 		 * so all events etc that method has will get dispatched as well.
       
 28319 		 *
       
 28320 		 * @method save
       
 28321 		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
       
 28322 		 * @return {String} HTML string that got set into the textarea/div.
       
 28323 		 */
       
 28324 		save: function(args) {
       
 28325 			var self = this, elm = self.getElement(), html, form;
       
 28326 
       
 28327 			if (!elm || !self.initialized) {
       
 28328 				return;
       
 28329 			}
       
 28330 
       
 28331 			args = args || {};
       
 28332 			args.save = true;
       
 28333 
       
 28334 			args.element = elm;
       
 28335 			html = args.content = self.getContent(args);
       
 28336 
       
 28337 			if (!args.no_events) {
       
 28338 				self.fire('SaveContent', args);
       
 28339 			}
       
 28340 
       
 28341 			html = args.content;
       
 28342 
       
 28343 			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
       
 28344 				// Update DIV element when not in inline mode
       
 28345 				if (!self.inline) {
       
 28346 					elm.innerHTML = html;
       
 28347 				}
       
 28348 
       
 28349 				// Update hidden form element
       
 28350 				if ((form = DOM.getParent(self.id, 'form'))) {
       
 28351 					each(form.elements, function(elm) {
       
 28352 						if (elm.name == self.id) {
       
 28353 							elm.value = html;
       
 28354 							return false;
       
 28355 						}
       
 28356 					});
       
 28357 				}
       
 28358 			} else {
       
 28359 				elm.value = html;
       
 28360 			}
       
 28361 
       
 28362 			args.element = elm = null;
       
 28363 
       
 28364 			if (args.set_dirty !== false) {
       
 28365 				self.isNotDirty = true;
       
 28366 			}
       
 28367 
       
 28368 			return html;
       
 28369 		},
       
 28370 
       
 28371 		/**
       
 28372 		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
       
 28373 		 * the different cleanup rules options.
       
 28374 		 *
       
 28375 		 * @method setContent
       
 28376 		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
       
 28377 		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
       
 28378 		 * @return {String} HTML string that got set into the editor.
       
 28379 		 * @example
       
 28380 		 * // Sets the HTML contents of the activeEditor editor
       
 28381 		 * tinymce.activeEditor.setContent('<span>some</span> html');
       
 28382 		 *
       
 28383 		 * // Sets the raw contents of the activeEditor editor
       
 28384 		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
       
 28385 		 *
       
 28386 		 * // Sets the content of a specific editor (my_editor in this example)
       
 28387 		 * tinymce.get('my_editor').setContent(data);
       
 28388 		 *
       
 28389 		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
       
 28390 		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
       
 28391 		 */
       
 28392 		setContent: function(content, args) {
       
 28393 			var self = this, body = self.getBody(), forcedRootBlockName;
       
 28394 
       
 28395 			// Setup args object
       
 28396 			args = args || {};
       
 28397 			args.format = args.format || 'html';
       
 28398 			args.set = true;
       
 28399 			args.content = content;
       
 28400 
       
 28401 			// Do preprocessing
       
 28402 			if (!args.no_events) {
       
 28403 				self.fire('BeforeSetContent', args);
       
 28404 			}
       
 28405 
       
 28406 			content = args.content;
       
 28407 
       
 28408 			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
       
 28409 			// It will also be impossible to place the caret in the editor unless there is a BR element present
       
 28410 			if (content.length === 0 || /^\s+$/.test(content)) {
       
 28411 				forcedRootBlockName = self.settings.forced_root_block;
       
 28412 
       
 28413 				// Check if forcedRootBlock is configured and that the block is a valid child of the body
       
 28414 				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
       
 28415 					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
       
 28416 					content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
       
 28417 					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
       
 28418 				} else if (!ie) {
       
 28419 					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
       
 28420 					content = '<br data-mce-bogus="1">';
       
 28421 				}
       
 28422 
       
 28423 				self.dom.setHTML(body, content);
       
 28424 
       
 28425 				self.fire('SetContent', args);
       
 28426 			} else {
       
 28427 				// Parse and serialize the html
       
 28428 				if (args.format !== 'raw') {
       
 28429 					content = new Serializer({}, self.schema).serialize(
       
 28430 						self.parser.parse(content, {isRootContent: true})
       
 28431 					);
       
 28432 				}
       
 28433 
       
 28434 				// Set the new cleaned contents to the editor
       
 28435 				args.content = trim(content);
       
 28436 				self.dom.setHTML(body, args.content);
       
 28437 
       
 28438 				// Do post processing
       
 28439 				if (!args.no_events) {
       
 28440 					self.fire('SetContent', args);
       
 28441 				}
       
 28442 
       
 28443 				// Don't normalize selection if the focused element isn't the body in
       
 28444 				// content editable mode since it will steal focus otherwise
       
 28445 				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
       
 28446 					self.selection.normalize();
       
 28447 				}*/
       
 28448 			}
       
 28449 
       
 28450 			return args.content;
       
 28451 		},
       
 28452 
       
 28453 		/**
       
 28454 		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
       
 28455 		 * the different cleanup rules options.
       
 28456 		 *
       
 28457 		 * @method getContent
       
 28458 		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
       
 28459 		 * @return {String} Cleaned content string, normally HTML contents.
       
 28460 		 * @example
       
 28461 		 * // Get the HTML contents of the currently active editor
       
 28462 		 * console.debug(tinymce.activeEditor.getContent());
       
 28463 		 *
       
 28464 		 * // Get the raw contents of the currently active editor
       
 28465 		 * tinymce.activeEditor.getContent({format: 'raw'});
       
 28466 		 *
       
 28467 		 * // Get content of a specific editor:
       
 28468 		 * tinymce.get('content id').getContent()
       
 28469 		 */
       
 28470 		getContent: function(args) {
       
 28471 			var self = this, content, body = self.getBody();
       
 28472 
       
 28473 			// Setup args object
       
 28474 			args = args || {};
       
 28475 			args.format = args.format || 'html';
       
 28476 			args.get = true;
       
 28477 			args.getInner = true;
       
 28478 
       
 28479 			// Do preprocessing
       
 28480 			if (!args.no_events) {
       
 28481 				self.fire('BeforeGetContent', args);
       
 28482 			}
       
 28483 
       
 28484 			// Get raw contents or by default the cleaned contents
       
 28485 			if (args.format == 'raw') {
       
 28486 				content = body.innerHTML;
       
 28487 			} else if (args.format == 'text') {
       
 28488 				content = body.innerText || body.textContent;
       
 28489 			} else {
       
 28490 				content = self.serializer.serialize(body, args);
       
 28491 			}
       
 28492 
       
 28493 			// Trim whitespace in beginning/end of HTML
       
 28494 			if (args.format != 'text') {
       
 28495 				args.content = trim(content);
       
 28496 			} else {
       
 28497 				args.content = content;
       
 28498 			}
       
 28499 
       
 28500 			// Do post processing
       
 28501 			if (!args.no_events) {
       
 28502 				self.fire('GetContent', args);
       
 28503 			}
       
 28504 
       
 28505 			return args.content;
       
 28506 		},
       
 28507 
       
 28508 		/**
       
 28509 		 * Inserts content at caret position.
       
 28510 		 *
       
 28511 		 * @method insertContent
       
 28512 		 * @param {String} content Content to insert.
       
 28513 		 * @param {Object} args Optional args to pass to insert call.
       
 28514 		 */
       
 28515 		insertContent: function(content, args) {
       
 28516 			if (args) {
       
 28517 				content = extend({content: content}, args);
       
 28518 			}
       
 28519 
       
 28520 			this.execCommand('mceInsertContent', false, content);
       
 28521 		},
       
 28522 
       
 28523 		/**
       
 28524 		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
       
 28525 		 *
       
 28526 		 * @method isDirty
       
 28527 		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
       
 28528 		 * @example
       
 28529 		 * if (tinymce.activeEditor.isDirty())
       
 28530 		 *     alert("You must save your contents.");
       
 28531 		 */
       
 28532 		isDirty: function() {
       
 28533 			return !this.isNotDirty;
       
 28534 		},
       
 28535 
       
 28536 		/**
       
 28537 		 * Returns the editors container element. The container element wrappes in
       
 28538 		 * all the elements added to the page for the editor. Such as UI, iframe etc.
       
 28539 		 *
       
 28540 		 * @method getContainer
       
 28541 		 * @return {Element} HTML DOM element for the editor container.
       
 28542 		 */
       
 28543 		getContainer: function() {
       
 28544 			var self = this;
       
 28545 
       
 28546 			if (!self.container) {
       
 28547 				self.container = DOM.get(self.editorContainer || self.id + '_parent');
       
 28548 			}
       
 28549 
       
 28550 			return self.container;
       
 28551 		},
       
 28552 
       
 28553 		/**
       
 28554 		 * Returns the editors content area container element. The this element is the one who
       
 28555 		 * holds the iframe or the editable element.
       
 28556 		 *
       
 28557 		 * @method getContentAreaContainer
       
 28558 		 * @return {Element} HTML DOM element for the editor area container.
       
 28559 		 */
       
 28560 		getContentAreaContainer: function() {
       
 28561 			return this.contentAreaContainer;
       
 28562 		},
       
 28563 
       
 28564 		/**
       
 28565 		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
       
 28566 		 *
       
 28567 		 * @method getElement
       
 28568 		 * @return {Element} HTML DOM element for the replaced element.
       
 28569 		 */
       
 28570 		getElement: function() {
       
 28571 			if (!this.targetElm) {
       
 28572 				this.targetElm = DOM.get(this.id);
       
 28573 			}
       
 28574 
       
 28575 			return this.targetElm;
       
 28576 		},
       
 28577 
       
 28578 		/**
       
 28579 		 * Returns the iframes window object.
       
 28580 		 *
       
 28581 		 * @method getWin
       
 28582 		 * @return {Window} Iframe DOM window object.
       
 28583 		 */
       
 28584 		getWin: function() {
       
 28585 			var self = this, elm;
       
 28586 
       
 28587 			if (!self.contentWindow) {
       
 28588 				elm = self.iframeElement;
       
 28589 
       
 28590 				if (elm) {
       
 28591 					self.contentWindow = elm.contentWindow;
       
 28592 				}
       
 28593 			}
       
 28594 
       
 28595 			return self.contentWindow;
       
 28596 		},
       
 28597 
       
 28598 		/**
       
 28599 		 * Returns the iframes document object.
       
 28600 		 *
       
 28601 		 * @method getDoc
       
 28602 		 * @return {Document} Iframe DOM document object.
       
 28603 		 */
       
 28604 		getDoc: function() {
       
 28605 			var self = this, win;
       
 28606 
       
 28607 			if (!self.contentDocument) {
       
 28608 				win = self.getWin();
       
 28609 
       
 28610 				if (win) {
       
 28611 					self.contentDocument = win.document;
       
 28612 				}
       
 28613 			}
       
 28614 
       
 28615 			return self.contentDocument;
       
 28616 		},
       
 28617 
       
 28618 		/**
       
 28619 		 * Returns the root element of the editable area.
       
 28620 		 * For a non-inline iframe-based editor, returns the iframe's body element.
       
 28621 		 *
       
 28622 		 * @method getBody
       
 28623 		 * @return {Element} The root element of the editable area.
       
 28624 		 */
       
 28625 		getBody: function() {
       
 28626 			return this.bodyElement || this.getDoc().body;
       
 28627 		},
       
 28628 
       
 28629 		/**
       
 28630 		 * URL converter function this gets executed each time a user adds an img, a or
       
 28631 		 * any other element that has a URL in it. This will be called both by the DOM and HTML
       
 28632 		 * manipulation functions.
       
 28633 		 *
       
 28634 		 * @method convertURL
       
 28635 		 * @param {string} url URL to convert.
       
 28636 		 * @param {string} name Attribute name src, href etc.
       
 28637 		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
       
 28638 		 * @return {string} Converted URL string.
       
 28639 		 */
       
 28640 		convertURL: function(url, name, elm) {
       
 28641 			var self = this, settings = self.settings;
       
 28642 
       
 28643 			// Use callback instead
       
 28644 			if (settings.urlconverter_callback) {
       
 28645 				return self.execCallback('urlconverter_callback', url, elm, true, name);
       
 28646 			}
       
 28647 
       
 28648 			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
       
 28649 			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
       
 28650 				return url;
       
 28651 			}
       
 28652 
       
 28653 			// Convert to relative
       
 28654 			if (settings.relative_urls) {
       
 28655 				return self.documentBaseURI.toRelative(url);
       
 28656 			}
       
 28657 
       
 28658 			// Convert to absolute
       
 28659 			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
       
 28660 
       
 28661 			return url;
       
 28662 		},
       
 28663 
       
 28664 		/**
       
 28665 		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
       
 28666 		 *
       
 28667 		 * @method addVisual
       
 28668 		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
       
 28669 		 */
       
 28670 		addVisual: function(elm) {
       
 28671 			var self = this, settings = self.settings, dom = self.dom, cls;
       
 28672 
       
 28673 			elm = elm || self.getBody();
       
 28674 
       
 28675 			if (self.hasVisual === undefined) {
       
 28676 				self.hasVisual = settings.visual;
       
 28677 			}
       
 28678 
       
 28679 			each(dom.select('table,a', elm), function(elm) {
       
 28680 				var value;
       
 28681 
       
 28682 				switch (elm.nodeName) {
       
 28683 					case 'TABLE':
       
 28684 						cls = settings.visual_table_class || 'mce-item-table';
       
 28685 						value = dom.getAttrib(elm, 'border');
       
 28686 
       
 28687 						if ((!value || value == '0') && self.hasVisual) {
       
 28688 							dom.addClass(elm, cls);
       
 28689 						} else {
       
 28690 							dom.removeClass(elm, cls);
       
 28691 						}
       
 28692 
       
 28693 						return;
       
 28694 
       
 28695 					case 'A':
       
 28696 						if (!dom.getAttrib(elm, 'href', false)) {
       
 28697 							value = dom.getAttrib(elm, 'name') || elm.id;
       
 28698 							cls = settings.visual_anchor_class || 'mce-item-anchor';
       
 28699 
       
 28700 							if (value && self.hasVisual) {
       
 28701 								dom.addClass(elm, cls);
       
 28702 							} else {
       
 28703 								dom.removeClass(elm, cls);
       
 28704 							}
       
 28705 						}
       
 28706 
       
 28707 						return;
       
 28708 				}
       
 28709 			});
       
 28710 
       
 28711 			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
       
 28712 		},
       
 28713 
       
 28714 		/**
       
 28715 		 * Removes the editor from the dom and tinymce collection.
       
 28716 		 *
       
 28717 		 * @method remove
       
 28718 		 */
       
 28719 		remove: function() {
       
 28720 			var self = this;
       
 28721 
       
 28722 			if (!self.removed) {
       
 28723 				self.save();
       
 28724 				self.removed = 1;
       
 28725 				self.unbindAllNativeEvents();
       
 28726 
       
 28727 				// Remove any hidden input
       
 28728 				if (self.hasHiddenInput) {
       
 28729 					DOM.remove(self.getElement().nextSibling);
       
 28730 				}
       
 28731 
       
 28732 				if (!self.inline) {
       
 28733 					// IE 9 has a bug where the selection stops working if you place the
       
 28734 					// caret inside the editor then remove the iframe
       
 28735 					if (ie && ie < 10) {
       
 28736 						self.getDoc().execCommand('SelectAll', false, null);
       
 28737 					}
       
 28738 
       
 28739 					DOM.setStyle(self.id, 'display', self.orgDisplay);
       
 28740 					self.getBody().onload = null; // Prevent #6816
       
 28741 				}
       
 28742 
       
 28743 				self.fire('remove');
       
 28744 
       
 28745 				self.editorManager.remove(self);
       
 28746 				DOM.remove(self.getContainer());
       
 28747 				self.destroy();
       
 28748 			}
       
 28749 		},
       
 28750 
       
 28751 		/**
       
 28752 		 * Destroys the editor instance by removing all events, element references or other resources
       
 28753 		 * that could leak memory. This method will be called automatically when the page is unloaded
       
 28754 		 * but you can also call it directly if you know what you are doing.
       
 28755 		 *
       
 28756 		 * @method destroy
       
 28757 		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
       
 28758 		 */
       
 28759 		destroy: function(automatic) {
       
 28760 			var self = this, form;
       
 28761 
       
 28762 			// One time is enough
       
 28763 			if (self.destroyed) {
       
 28764 				return;
       
 28765 			}
       
 28766 
       
 28767 			// If user manually calls destroy and not remove
       
 28768 			// Users seems to have logic that calls destroy instead of remove
       
 28769 			if (!automatic && !self.removed) {
       
 28770 				self.remove();
       
 28771 				return;
       
 28772 			}
       
 28773 
       
 28774 			if (!automatic) {
       
 28775 				self.editorManager.off('beforeunload', self._beforeUnload);
       
 28776 
       
 28777 				// Manual destroy
       
 28778 				if (self.theme && self.theme.destroy) {
       
 28779 					self.theme.destroy();
       
 28780 				}
       
 28781 
       
 28782 				// Destroy controls, selection and dom
       
 28783 				self.selection.destroy();
       
 28784 				self.dom.destroy();
       
 28785 			}
       
 28786 
       
 28787 			form = self.formElement;
       
 28788 			if (form) {
       
 28789 				if (form._mceOldSubmit) {
       
 28790 					form.submit = form._mceOldSubmit;
       
 28791 					form._mceOldSubmit = null;
       
 28792 				}
       
 28793 
       
 28794 				DOM.unbind(form, 'submit reset', self.formEventDelegate);
       
 28795 			}
       
 28796 
       
 28797 			self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null;
       
 28798 			self.bodyElement = self.contentDocument = self.contentWindow = null;
       
 28799 			self.iframeElement = self.targetElm = null;
       
 28800 
       
 28801 			if (self.selection) {
       
 28802 				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
       
 28803 			}
       
 28804 
       
 28805 			self.destroyed = 1;
       
 28806 		},
       
 28807 
       
 28808 		// Internal functions
       
 28809 
       
 28810 		_refreshContentEditable: function() {
       
 28811 			var self = this, body, parent;
       
 28812 
       
 28813 			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
       
 28814 			if (self._isHidden()) {
       
 28815 				body = self.getBody();
       
 28816 				parent = body.parentNode;
       
 28817 
       
 28818 				parent.removeChild(body);
       
 28819 				parent.appendChild(body);
       
 28820 
       
 28821 				body.focus();
       
 28822 			}
       
 28823 		},
       
 28824 
       
 28825 		_isHidden: function() {
       
 28826 			var sel;
       
 28827 
       
 28828 			if (!isGecko) {
       
 28829 				return 0;
       
 28830 			}
       
 28831 
       
 28832 			// Weird, wheres that cursor selection?
       
 28833 			sel = this.selection.getSel();
       
 28834 			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
       
 28835 		}
       
 28836 	};
       
 28837 
       
 28838 	extend(Editor.prototype, EditorObservable);
       
 28839 
       
 28840 	return Editor;
       
 28841 });
       
 28842 
       
 28843 // Included from: js/tinymce/classes/util/I18n.js
       
 28844 
       
 28845 /**
       
 28846  * I18n.js
       
 28847  *
       
 28848  * Copyright, Moxiecode Systems AB
       
 28849  * Released under LGPL License.
       
 28850  *
       
 28851  * License: http://www.tinymce.com/license
       
 28852  * Contributing: http://www.tinymce.com/contributing
       
 28853  */
       
 28854 
       
 28855 /**
       
 28856  * I18n class that handles translation of TinyMCE UI.
       
 28857  * Uses po style with csharp style parameters.
       
 28858  *
       
 28859  * @class tinymce.util.I18n
       
 28860  */
       
 28861 define("tinymce/util/I18n", [], function() {
       
 28862 	"use strict";
       
 28863 
       
 28864 	var data = {}, code = "en";
       
 28865 
       
 28866 	return {
       
 28867 		/**
       
 28868 		 * Sets the current language code.
       
 28869 		 *
       
 28870 		 * @method setCode
       
 28871 		 * @param {String} newCode Current language code.
       
 28872 		 */
       
 28873 		setCode: function(newCode) {
       
 28874 			if (newCode) {
       
 28875 				code = newCode;
       
 28876 				this.rtl = this.data[newCode] ? this.data[newCode]._dir === 'rtl' : false;
       
 28877 			}
       
 28878 		},
       
 28879 
       
 28880 		/**
       
 28881 		 * Returns the current language code.
       
 28882 		 *
       
 28883 		 * @return {String} Current language code.
       
 28884 		 */
       
 28885 		getCode: function() {
       
 28886 			return code;
       
 28887 		},
       
 28888 
       
 28889 		/**
       
 28890 		 * Property gets set to true if a RTL language pack was loaded.
       
 28891 		 *
       
 28892 		 * @property rtl
       
 28893 		 * @type Boolean
       
 28894 		 */
       
 28895 		rtl: false,
       
 28896 
       
 28897 		/**
       
 28898 		 * Adds translations for a specific language code.
       
 28899 		 *
       
 28900 		 * @method add
       
 28901 		 * @param {String} code Language code like sv_SE.
       
 28902 		 * @param {Array} items Name/value array with English en_US to sv_SE.
       
 28903 		 */
       
 28904 		add: function(code, items) {
       
 28905 			var langData = data[code];
       
 28906 
       
 28907 			if (!langData) {
       
 28908 				data[code] = langData = {};
       
 28909 			}
       
 28910 
       
 28911 			for (var name in items) {
       
 28912 				langData[name] = items[name];
       
 28913 			}
       
 28914 
       
 28915 			this.setCode(code);
       
 28916 		},
       
 28917 
       
 28918 		/**
       
 28919 		 * Translates the specified text.
       
 28920 		 *
       
 28921 		 * It has a few formats:
       
 28922 		 * I18n.translate("Text");
       
 28923 		 * I18n.translate(["Text {0}/{1}", 0, 1]);
       
 28924 		 * I18n.translate({raw: "Raw string"});
       
 28925 		 *
       
 28926 		 * @method translate
       
 28927 		 * @param {String/Object/Array} text Text to translate.
       
 28928 		 * @return {String} String that got translated.
       
 28929 		 */
       
 28930 		translate: function(text) {
       
 28931 			var langData;
       
 28932 
       
 28933 			langData = data[code];
       
 28934 			if (!langData) {
       
 28935 				langData = {};
       
 28936 			}
       
 28937 
       
 28938 			if (typeof text == "undefined") {
       
 28939 				return text;
       
 28940 			}
       
 28941 
       
 28942 			if (typeof text != "string" && text.raw) {
       
 28943 				return text.raw;
       
 28944 			}
       
 28945 
       
 28946 			if (text.push) {
       
 28947 				var values = text.slice(1);
       
 28948 
       
 28949 				text = (langData[text[0]] || text[0]).replace(/\{([0-9]+)\}/g, function(match1, match2) {
       
 28950 					return values[match2];
       
 28951 				});
       
 28952 			}
       
 28953 
       
 28954 			return (langData[text] || text).replace(/{context:\w+}$/, '');
       
 28955 		},
       
 28956 
       
 28957 		data: data
       
 28958 	};
       
 28959 });
       
 28960 
       
 28961 // Included from: js/tinymce/classes/FocusManager.js
       
 28962 
       
 28963 /**
       
 28964  * FocusManager.js
       
 28965  *
       
 28966  * Copyright, Moxiecode Systems AB
       
 28967  * Released under LGPL License.
       
 28968  *
       
 28969  * License: http://www.tinymce.com/license
       
 28970  * Contributing: http://www.tinymce.com/contributing
       
 28971  */
       
 28972 
       
 28973 /**
       
 28974  * This class manages the focus/blur state of the editor. This class is needed since some
       
 28975  * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
       
 28976  *
       
 28977  * This class will fire two events focus and blur on the editor instances that got affected.
       
 28978  * It will also handle the restore of selection when the focus is lost and returned.
       
 28979  *
       
 28980  * @class tinymce.FocusManager
       
 28981  */
       
 28982 define("tinymce/FocusManager", [
       
 28983 	"tinymce/dom/DOMUtils",
       
 28984 	"tinymce/Env"
       
 28985 ], function(DOMUtils, Env) {
       
 28986 	var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM;
       
 28987 
       
 28988 	/**
       
 28989 	 * Constructs a new focus manager instance.
       
 28990 	 *
       
 28991 	 * @constructor FocusManager
       
 28992 	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
       
 28993 	 */
       
 28994 	function FocusManager(editorManager) {
       
 28995 		function getActiveElement() {
       
 28996 			try {
       
 28997 				return document.activeElement;
       
 28998 			} catch (ex) {
       
 28999 				// IE sometimes fails to get the activeElement when resizing table
       
 29000 				// TODO: Investigate this
       
 29001 				return document.body;
       
 29002 			}
       
 29003 		}
       
 29004 
       
 29005 		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
       
 29006 		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
       
 29007 		function createBookmark(dom, rng) {
       
 29008 			if (rng && rng.startContainer) {
       
 29009 				// Verify that the range is within the root of the editor
       
 29010 				if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) {
       
 29011 					return;
       
 29012 				}
       
 29013 
       
 29014 				return {
       
 29015 					startContainer: rng.startContainer,
       
 29016 					startOffset: rng.startOffset,
       
 29017 					endContainer: rng.endContainer,
       
 29018 					endOffset: rng.endOffset
       
 29019 				};
       
 29020 			}
       
 29021 
       
 29022 			return rng;
       
 29023 		}
       
 29024 
       
 29025 		function bookmarkToRng(editor, bookmark) {
       
 29026 			var rng;
       
 29027 
       
 29028 			if (bookmark.startContainer) {
       
 29029 				rng = editor.getDoc().createRange();
       
 29030 				rng.setStart(bookmark.startContainer, bookmark.startOffset);
       
 29031 				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
       
 29032 			} else {
       
 29033 				rng = bookmark;
       
 29034 			}
       
 29035 
       
 29036 			return rng;
       
 29037 		}
       
 29038 
       
 29039 		function isUIElement(elm) {
       
 29040 			return !!DOM.getParent(elm, FocusManager.isEditorUIElement);
       
 29041 		}
       
 29042 
       
 29043 		function registerEvents(e) {
       
 29044 			var editor = e.editor;
       
 29045 
       
 29046 			editor.on('init', function() {
       
 29047 				// Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab
       
 29048 				if (editor.inline || Env.ie) {
       
 29049 					// Use the onbeforedeactivate event when available since it works better see #7023
       
 29050 					if ("onbeforedeactivate" in document && Env.ie < 9) {
       
 29051 						editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) {
       
 29052 							if (e.target != editor.getBody()) {
       
 29053 								return;
       
 29054 							}
       
 29055 
       
 29056 							try {
       
 29057 								editor.lastRng = editor.selection.getRng();
       
 29058 							} catch (ex) {
       
 29059 								// IE throws "Unexcpected call to method or property access" some times so lets ignore it
       
 29060 							}
       
 29061 						});
       
 29062 					} else {
       
 29063 						// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
       
 29064 						editor.on('nodechange mouseup keyup', function(e) {
       
 29065 							var node = getActiveElement();
       
 29066 
       
 29067 							// Only act on manual nodechanges
       
 29068 							if (e.type == 'nodechange' && e.selectionChange) {
       
 29069 								return;
       
 29070 							}
       
 29071 
       
 29072 							// IE 11 reports active element as iframe not body of iframe
       
 29073 							if (node && node.id == editor.id + '_ifr') {
       
 29074 								node = editor.getBody();
       
 29075 							}
       
 29076 
       
 29077 							if (editor.dom.isChildOf(node, editor.getBody())) {
       
 29078 								editor.lastRng = editor.selection.getRng();
       
 29079 							}
       
 29080 						});
       
 29081 					}
       
 29082 
       
 29083 					// Handles the issue with WebKit not retaining selection within inline document
       
 29084 					// If the user releases the mouse out side the body since a mouse up event wont occur on the body
       
 29085 					if (Env.webkit && !selectionChangeHandler) {
       
 29086 						selectionChangeHandler = function() {
       
 29087 							var activeEditor = editorManager.activeEditor;
       
 29088 
       
 29089 							if (activeEditor && activeEditor.selection) {
       
 29090 								var rng = activeEditor.selection.getRng();
       
 29091 
       
 29092 								// Store when it's non collapsed
       
 29093 								if (rng && !rng.collapsed) {
       
 29094 									editor.lastRng = rng;
       
 29095 								}
       
 29096 							}
       
 29097 						};
       
 29098 
       
 29099 						DOM.bind(document, 'selectionchange', selectionChangeHandler);
       
 29100 					}
       
 29101 				}
       
 29102 			});
       
 29103 
       
 29104 			editor.on('setcontent', function() {
       
 29105 				editor.lastRng = null;
       
 29106 			});
       
 29107 
       
 29108 			// Remove last selection bookmark on mousedown see #6305
       
 29109 			editor.on('mousedown', function() {
       
 29110 				editor.selection.lastFocusBookmark = null;
       
 29111 			});
       
 29112 
       
 29113 			editor.on('focusin', function() {
       
 29114 				var focusedEditor = editorManager.focusedEditor;
       
 29115 
       
 29116 				if (editor.selection.lastFocusBookmark) {
       
 29117 					editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
       
 29118 					editor.selection.lastFocusBookmark = null;
       
 29119 				}
       
 29120 
       
 29121 				if (focusedEditor != editor) {
       
 29122 					if (focusedEditor) {
       
 29123 						focusedEditor.fire('blur', {focusedEditor: editor});
       
 29124 					}
       
 29125 
       
 29126 					editorManager.setActive(editor);
       
 29127 					editorManager.focusedEditor = editor;
       
 29128 					editor.fire('focus', {blurredEditor: focusedEditor});
       
 29129 					editor.focus(true);
       
 29130 				}
       
 29131 
       
 29132 				editor.lastRng = null;
       
 29133 			});
       
 29134 
       
 29135 			editor.on('focusout', function() {
       
 29136 				window.setTimeout(function() {
       
 29137 					var focusedEditor = editorManager.focusedEditor;
       
 29138 
       
 29139 					// Still the same editor the the blur was outside any editor UI
       
 29140 					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
       
 29141 						editor.fire('blur', {focusedEditor: null});
       
 29142 						editorManager.focusedEditor = null;
       
 29143 
       
 29144 						// Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs
       
 29145 						if (editor.selection) {
       
 29146 							editor.selection.lastFocusBookmark = null;
       
 29147 						}
       
 29148 					}
       
 29149 				}, 0);
       
 29150 			});
       
 29151 
       
 29152 			// Check if focus is moved to an element outside the active editor by checking if the target node
       
 29153 			// isn't within the body of the activeEditor nor a UI element such as a dialog child control
       
 29154 			if (!documentFocusInHandler) {
       
 29155 				documentFocusInHandler = function(e) {
       
 29156 					var activeEditor = editorManager.activeEditor;
       
 29157 
       
 29158 					if (activeEditor && e.target.ownerDocument == document) {
       
 29159 						// Check to make sure we have a valid selection don't update the bookmark if it's
       
 29160 						// a focusin to the body of the editor see #7025
       
 29161 						if (activeEditor.selection && e.target != activeEditor.getBody()) {
       
 29162 							activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng);
       
 29163 						}
       
 29164 
       
 29165 						// Fire a blur event if the element isn't a UI element
       
 29166 						if (e.target != document.body && !isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
       
 29167 							activeEditor.fire('blur', {focusedEditor: null});
       
 29168 							editorManager.focusedEditor = null;
       
 29169 						}
       
 29170 					}
       
 29171 				};
       
 29172 
       
 29173 				DOM.bind(document, 'focusin', documentFocusInHandler);
       
 29174 			}
       
 29175 
       
 29176 			// Handle edge case when user starts the selection inside the editor and releases
       
 29177 			// the mouse outside the editor producing a new selection. This weird workaround is needed since
       
 29178 			// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
       
 29179 			if (editor.inline && !documentMouseUpHandler) {
       
 29180 				documentMouseUpHandler = function(e) {
       
 29181 					var activeEditor = editorManager.activeEditor;
       
 29182 
       
 29183 					if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
       
 29184 						var rng = activeEditor.selection.getRng();
       
 29185 
       
 29186 						if (!rng.collapsed) {
       
 29187 							activeEditor.lastRng = rng;
       
 29188 						}
       
 29189 					}
       
 29190 				};
       
 29191 
       
 29192 				DOM.bind(document, 'mouseup', documentMouseUpHandler);
       
 29193 			}
       
 29194 		}
       
 29195 
       
 29196 		function unregisterDocumentEvents(e) {
       
 29197 			if (editorManager.focusedEditor == e.editor) {
       
 29198 				editorManager.focusedEditor = null;
       
 29199 			}
       
 29200 
       
 29201 			if (!editorManager.activeEditor) {
       
 29202 				DOM.unbind(document, 'selectionchange', selectionChangeHandler);
       
 29203 				DOM.unbind(document, 'focusin', documentFocusInHandler);
       
 29204 				DOM.unbind(document, 'mouseup', documentMouseUpHandler);
       
 29205 				selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null;
       
 29206 			}
       
 29207 		}
       
 29208 
       
 29209 		editorManager.on('AddEditor', registerEvents);
       
 29210 		editorManager.on('RemoveEditor', unregisterDocumentEvents);
       
 29211 	}
       
 29212 
       
 29213 	/**
       
 29214 	 * Returns true if the specified element is part of the UI for example an button or text input.
       
 29215 	 *
       
 29216 	 * @method isEditorUIElement
       
 29217 	 * @param  {Element} elm Element to check if it's part of the UI or not.
       
 29218 	 * @return {Boolean} True/false state if the element is part of the UI or not.
       
 29219 	 */
       
 29220 	FocusManager.isEditorUIElement = function(elm) {
       
 29221 		// Needs to be converted to string since svg can have focus: #6776
       
 29222 		return elm.className.toString().indexOf('mce-') !== -1;
       
 29223 	};
       
 29224 
       
 29225 	return FocusManager;
       
 29226 });
       
 29227 
       
 29228 // Included from: js/tinymce/classes/EditorManager.js
       
 29229 
       
 29230 /**
       
 29231  * EditorManager.js
       
 29232  *
       
 29233  * Copyright, Moxiecode Systems AB
       
 29234  * Released under LGPL License.
       
 29235  *
       
 29236  * License: http://www.tinymce.com/license
       
 29237  * Contributing: http://www.tinymce.com/contributing
       
 29238  */
       
 29239 
       
 29240 /**
       
 29241  * This class used as a factory for manager for tinymce.Editor instances.
       
 29242  *
       
 29243  * @example
       
 29244  * tinymce.EditorManager.init({});
       
 29245  *
       
 29246  * @class tinymce.EditorManager
       
 29247  * @mixes tinymce.util.Observable
       
 29248  * @static
       
 29249  */
       
 29250 define("tinymce/EditorManager", [
       
 29251 	"tinymce/Editor",
       
 29252 	"tinymce/dom/DomQuery",
       
 29253 	"tinymce/dom/DOMUtils",
       
 29254 	"tinymce/util/URI",
       
 29255 	"tinymce/Env",
       
 29256 	"tinymce/util/Tools",
       
 29257 	"tinymce/util/Observable",
       
 29258 	"tinymce/util/I18n",
       
 29259 	"tinymce/FocusManager"
       
 29260 ], function(Editor, DomQuery, DOMUtils, URI, Env, Tools, Observable, I18n, FocusManager) {
       
 29261 	var DOM = DOMUtils.DOM;
       
 29262 	var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
       
 29263 	var instanceCounter = 0, beforeUnloadDelegate, EditorManager;
       
 29264 
       
 29265 	function removeEditorFromList(editor) {
       
 29266 		var editors = EditorManager.editors, removedFromList;
       
 29267 
       
 29268 		delete editors[editor.id];
       
 29269 
       
 29270 		for (var i = 0; i < editors.length; i++) {
       
 29271 			if (editors[i] == editor) {
       
 29272 				editors.splice(i, 1);
       
 29273 				removedFromList = true;
       
 29274 				break;
       
 29275 			}
       
 29276 		}
       
 29277 
       
 29278 		// Select another editor since the active one was removed
       
 29279 		if (EditorManager.activeEditor == editor) {
       
 29280 			EditorManager.activeEditor = editors[0];
       
 29281 		}
       
 29282 
       
 29283 		// Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor
       
 29284 		if (EditorManager.focusedEditor == editor) {
       
 29285 			EditorManager.focusedEditor = null;
       
 29286 		}
       
 29287 
       
 29288 		return removedFromList;
       
 29289 	}
       
 29290 
       
 29291 	function purgeDestroyedEditor(editor) {
       
 29292 		// User has manually destroyed the editor lets clean up the mess
       
 29293 		if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
       
 29294 			removeEditorFromList(editor);
       
 29295 			editor.unbindAllNativeEvents();
       
 29296 			editor.destroy(true);
       
 29297 			editor = null;
       
 29298 		}
       
 29299 
       
 29300 		return editor;
       
 29301 	}
       
 29302 
       
 29303 	EditorManager = {
       
 29304 		/**
       
 29305 		 * Dom query instance.
       
 29306 		 *
       
 29307 		 * @property $
       
 29308 		 * @type tinymce.dom.DomQuery
       
 29309 		 */
       
 29310 		$: DomQuery,
       
 29311 
       
 29312 		/**
       
 29313 		 * Major version of TinyMCE build.
       
 29314 		 *
       
 29315 		 * @property majorVersion
       
 29316 		 * @type String
       
 29317 		 */
       
 29318 		majorVersion: '4',
       
 29319 
       
 29320 		/**
       
 29321 		 * Minor version of TinyMCE build.
       
 29322 		 *
       
 29323 		 * @property minorVersion
       
 29324 		 * @type String
       
 29325 		 */
       
 29326 		minorVersion: '1.10',
       
 29327 
       
 29328 		/**
       
 29329 		 * Release date of TinyMCE build.
       
 29330 		 *
       
 29331 		 * @property releaseDate
       
 29332 		 * @type String
       
 29333 		 */
       
 29334 		releaseDate: '2015-05-05',
       
 29335 
       
 29336 		/**
       
 29337 		 * Collection of editor instances.
       
 29338 		 *
       
 29339 		 * @property editors
       
 29340 		 * @type Object
       
 29341 		 * @example
       
 29342 		 * for (edId in tinymce.editors)
       
 29343 		 *     tinymce.editors[edId].save();
       
 29344 		 */
       
 29345 		editors: [],
       
 29346 
       
 29347 		/**
       
 29348 		 * Collection of language pack data.
       
 29349 		 *
       
 29350 		 * @property i18n
       
 29351 		 * @type Object
       
 29352 		 */
       
 29353 		i18n: I18n,
       
 29354 
       
 29355 		/**
       
 29356 		 * Currently active editor instance.
       
 29357 		 *
       
 29358 		 * @property activeEditor
       
 29359 		 * @type tinymce.Editor
       
 29360 		 * @example
       
 29361 		 * tinyMCE.activeEditor.selection.getContent();
       
 29362 		 * tinymce.EditorManager.activeEditor.selection.getContent();
       
 29363 		 */
       
 29364 		activeEditor: null,
       
 29365 
       
 29366 		setup: function() {
       
 29367 			var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
       
 29368 
       
 29369 			// Get base URL for the current document
       
 29370 			documentBaseURL = document.location.href;
       
 29371 
       
 29372 			// Check if the URL is a document based format like: http://site/dir/file and file:///
       
 29373 			// leave other formats like applewebdata://... intact
       
 29374 			if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {
       
 29375 				documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
       
 29376 
       
 29377 				if (!/[\/\\]$/.test(documentBaseURL)) {
       
 29378 					documentBaseURL += '/';
       
 29379 				}
       
 29380 			}
       
 29381 
       
 29382 			// If tinymce is defined and has a base use that or use the old tinyMCEPreInit
       
 29383 			preInit = window.tinymce || window.tinyMCEPreInit;
       
 29384 			if (preInit) {
       
 29385 				baseURL = preInit.base || preInit.baseURL;
       
 29386 				suffix = preInit.suffix;
       
 29387 			} else {
       
 29388 				// Get base where the tinymce script is located
       
 29389 				var scripts = document.getElementsByTagName('script');
       
 29390 				for (var i = 0; i < scripts.length; i++) {
       
 29391 					src = scripts[i].src;
       
 29392 
       
 29393 					// Script types supported:
       
 29394 					// tinymce.js tinymce.min.js tinymce.dev.js
       
 29395 					// tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
       
 29396 					// tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
       
 29397 					if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
       
 29398 						if (src.indexOf('.min') != -1) {
       
 29399 							suffix = '.min';
       
 29400 						}
       
 29401 
       
 29402 						baseURL = src.substring(0, src.lastIndexOf('/'));
       
 29403 						break;
       
 29404 					}
       
 29405 				}
       
 29406 
       
 29407 				// We didn't find any baseURL by looking at the script elements
       
 29408 				// Try to use the document.currentScript as a fallback
       
 29409 				if (!baseURL && document.currentScript) {
       
 29410 					src = document.currentScript.src;
       
 29411 
       
 29412 					if (src.indexOf('.min') != -1) {
       
 29413 						suffix = '.min';
       
 29414 					}
       
 29415 
       
 29416 					baseURL = src.substring(0, src.lastIndexOf('/'));
       
 29417 				}
       
 29418 			}
       
 29419 
       
 29420 			/**
       
 29421 			 * Base URL where the root directory if TinyMCE is located.
       
 29422 			 *
       
 29423 			 * @property baseURL
       
 29424 			 * @type String
       
 29425 			 */
       
 29426 			self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
       
 29427 
       
 29428 			/**
       
 29429 			 * Document base URL where the current document is located.
       
 29430 			 *
       
 29431 			 * @property documentBaseURL
       
 29432 			 * @type String
       
 29433 			 */
       
 29434 			self.documentBaseURL = documentBaseURL;
       
 29435 
       
 29436 			/**
       
 29437 			 * Absolute baseURI for the installation path of TinyMCE.
       
 29438 			 *
       
 29439 			 * @property baseURI
       
 29440 			 * @type tinymce.util.URI
       
 29441 			 */
       
 29442 			self.baseURI = new URI(self.baseURL);
       
 29443 
       
 29444 			/**
       
 29445 			 * Current suffix to add to each plugin/theme that gets loaded for example ".min".
       
 29446 			 *
       
 29447 			 * @property suffix
       
 29448 			 * @type String
       
 29449 			 */
       
 29450 			self.suffix = suffix;
       
 29451 
       
 29452 			self.focusManager = new FocusManager(self);
       
 29453 		},
       
 29454 
       
 29455 		/**
       
 29456 		 * Initializes a set of editors. This method will create editors based on various settings.
       
 29457 		 *
       
 29458 		 * @method init
       
 29459 		 * @param {Object} settings Settings object to be passed to each editor instance.
       
 29460 		 * @example
       
 29461 		 * // Initializes a editor using the longer method
       
 29462 		 * tinymce.EditorManager.init({
       
 29463 		 *    some_settings : 'some value'
       
 29464 		 * });
       
 29465 		 *
       
 29466 		 * // Initializes a editor instance using the shorter version
       
 29467 		 * tinyMCE.init({
       
 29468 		 *    some_settings : 'some value'
       
 29469 		 * });
       
 29470 		 */
       
 29471 		init: function(settings) {
       
 29472 			var self = this, editors = [];
       
 29473 
       
 29474 			function createId(elm) {
       
 29475 				var id = elm.id;
       
 29476 
       
 29477 				// Use element id, or unique name or generate a unique id
       
 29478 				if (!id) {
       
 29479 					id = elm.name;
       
 29480 
       
 29481 					if (id && !DOM.get(id)) {
       
 29482 						id = elm.name;
       
 29483 					} else {
       
 29484 						// Generate unique name
       
 29485 						id = DOM.uniqueId();
       
 29486 					}
       
 29487 
       
 29488 					elm.setAttribute('id', id);
       
 29489 				}
       
 29490 
       
 29491 				return id;
       
 29492 			}
       
 29493 
       
 29494 			function createEditor(id, settings, targetElm) {
       
 29495 				if (!purgeDestroyedEditor(self.get(id))) {
       
 29496 					var editor = new Editor(id, settings, self);
       
 29497 
       
 29498 					editor.targetElm = editor.targetElm || targetElm;
       
 29499 					editors.push(editor);
       
 29500 					editor.render();
       
 29501 				}
       
 29502 			}
       
 29503 
       
 29504 			function execCallback(name) {
       
 29505 				var callback = settings[name];
       
 29506 
       
 29507 				if (!callback) {
       
 29508 					return;
       
 29509 				}
       
 29510 
       
 29511 				return callback.apply(self, Array.prototype.slice.call(arguments, 2));
       
 29512 			}
       
 29513 
       
 29514 			function hasClass(elm, className) {
       
 29515 				return className.constructor === RegExp ? className.test(elm.className) : DOM.hasClass(elm, className);
       
 29516 			}
       
 29517 
       
 29518 			function readyHandler() {
       
 29519 				var l, co;
       
 29520 
       
 29521 				DOM.unbind(window, 'ready', readyHandler);
       
 29522 
       
 29523 				execCallback('onpageload');
       
 29524 
       
 29525 				if (settings.types) {
       
 29526 					// Process type specific selector
       
 29527 					each(settings.types, function(type) {
       
 29528 						each(DOM.select(type.selector), function(elm) {
       
 29529 							createEditor(createId(elm), extend({}, settings, type), elm);
       
 29530 						});
       
 29531 					});
       
 29532 
       
 29533 					return;
       
 29534 				} else if (settings.selector) {
       
 29535 					// Process global selector
       
 29536 					each(DOM.select(settings.selector), function(elm) {
       
 29537 						createEditor(createId(elm), settings, elm);
       
 29538 					});
       
 29539 
       
 29540 					return;
       
 29541 				} else if (settings.target) {
       
 29542 					createEditor(createId(settings.target), settings);
       
 29543 				}
       
 29544 
       
 29545 				// Fallback to old setting
       
 29546 				switch (settings.mode) {
       
 29547 					case "exact":
       
 29548 						l = settings.elements || '';
       
 29549 
       
 29550 						if (l.length > 0) {
       
 29551 							each(explode(l), function(id) {
       
 29552 								var elm;
       
 29553 
       
 29554 								if ((elm = DOM.get(id))) {
       
 29555 									createEditor(id, settings, elm);
       
 29556 								} else {
       
 29557 									each(document.forms, function(f) {
       
 29558 										each(f.elements, function(e) {
       
 29559 											if (e.name === id) {
       
 29560 												id = 'mce_editor_' + instanceCounter++;
       
 29561 												DOM.setAttrib(e, 'id', id);
       
 29562 												createEditor(id, settings, e);
       
 29563 											}
       
 29564 										});
       
 29565 									});
       
 29566 								}
       
 29567 							});
       
 29568 						}
       
 29569 						break;
       
 29570 
       
 29571 					case "textareas":
       
 29572 					case "specific_textareas":
       
 29573 						each(DOM.select('textarea'), function(elm) {
       
 29574 							if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) {
       
 29575 								return;
       
 29576 							}
       
 29577 
       
 29578 							if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) {
       
 29579 								createEditor(createId(elm), settings, elm);
       
 29580 							}
       
 29581 						});
       
 29582 						break;
       
 29583 				}
       
 29584 
       
 29585 				// Call onInit when all editors are initialized
       
 29586 				if (settings.oninit) {
       
 29587 					l = co = 0;
       
 29588 
       
 29589 					each(editors, function(ed) {
       
 29590 						co++;
       
 29591 
       
 29592 						if (!ed.initialized) {
       
 29593 							// Wait for it
       
 29594 							ed.on('init', function() {
       
 29595 								l++;
       
 29596 
       
 29597 								// All done
       
 29598 								if (l == co) {
       
 29599 									execCallback('oninit');
       
 29600 								}
       
 29601 							});
       
 29602 						} else {
       
 29603 							l++;
       
 29604 						}
       
 29605 
       
 29606 						// All done
       
 29607 						if (l == co) {
       
 29608 							execCallback('oninit');
       
 29609 						}
       
 29610 					});
       
 29611 				}
       
 29612 			}
       
 29613 
       
 29614 			self.settings = settings;
       
 29615 
       
 29616 			DOM.bind(window, 'ready', readyHandler);
       
 29617 		},
       
 29618 
       
 29619 		/**
       
 29620 		 * Returns a editor instance by id.
       
 29621 		 *
       
 29622 		 * @method get
       
 29623 		 * @param {String/Number} id Editor instance id or index to return.
       
 29624 		 * @return {tinymce.Editor} Editor instance to return.
       
 29625 		 * @example
       
 29626 		 * // Adds an onclick event to an editor by id (shorter version)
       
 29627 		 * tinymce.get('mytextbox').on('click', function(e) {
       
 29628 		 *    ed.windowManager.alert('Hello world!');
       
 29629 		 * });
       
 29630 		 *
       
 29631 		 * // Adds an onclick event to an editor by id (longer version)
       
 29632 		 * tinymce.EditorManager.get('mytextbox').on('click', function(e) {
       
 29633 		 *    ed.windowManager.alert('Hello world!');
       
 29634 		 * });
       
 29635 		 */
       
 29636 		get: function(id) {
       
 29637 			if (!arguments.length) {
       
 29638 				return this.editors;
       
 29639 			}
       
 29640 
       
 29641 			return id in this.editors ? this.editors[id] : null;
       
 29642 		},
       
 29643 
       
 29644 		/**
       
 29645 		 * Adds an editor instance to the editor collection. This will also set it as the active editor.
       
 29646 		 *
       
 29647 		 * @method add
       
 29648 		 * @param {tinymce.Editor} editor Editor instance to add to the collection.
       
 29649 		 * @return {tinymce.Editor} The same instance that got passed in.
       
 29650 		 */
       
 29651 		add: function(editor) {
       
 29652 			var self = this, editors = self.editors;
       
 29653 
       
 29654 			// Add named and index editor instance
       
 29655 			editors[editor.id] = editor;
       
 29656 			editors.push(editor);
       
 29657 
       
 29658 			// Doesn't call setActive method since we don't want
       
 29659 			// to fire a bunch of activate/deactivate calls while initializing
       
 29660 			self.activeEditor = editor;
       
 29661 
       
 29662 			/**
       
 29663 			 * Fires when an editor is added to the EditorManager collection.
       
 29664 			 *
       
 29665 			 * @event AddEditor
       
 29666 			 * @param {Object} e Event arguments.
       
 29667 			 */
       
 29668 			self.fire('AddEditor', {editor: editor});
       
 29669 
       
 29670 			if (!beforeUnloadDelegate) {
       
 29671 				beforeUnloadDelegate = function() {
       
 29672 					self.fire('BeforeUnload');
       
 29673 				};
       
 29674 
       
 29675 				DOM.bind(window, 'beforeunload', beforeUnloadDelegate);
       
 29676 			}
       
 29677 
       
 29678 			return editor;
       
 29679 		},
       
 29680 
       
 29681 		/**
       
 29682 		 * Creates an editor instance and adds it to the EditorManager collection.
       
 29683 		 *
       
 29684 		 * @method createEditor
       
 29685 		 * @param {String} id Instance id to use for editor.
       
 29686 		 * @param {Object} settings Editor instance settings.
       
 29687 		 * @return {tinymce.Editor} Editor instance that got created.
       
 29688 		 */
       
 29689 		createEditor: function(id, settings) {
       
 29690 			return this.add(new Editor(id, settings, this));
       
 29691 		},
       
 29692 
       
 29693 		/**
       
 29694 		 * Removes a editor or editors form page.
       
 29695 		 *
       
 29696 		 * @example
       
 29697 		 * // Remove all editors bound to divs
       
 29698 		 * tinymce.remove('div');
       
 29699 		 *
       
 29700 		 * // Remove all editors bound to textareas
       
 29701 		 * tinymce.remove('textarea');
       
 29702 		 *
       
 29703 		 * // Remove all editors
       
 29704 		 * tinymce.remove();
       
 29705 		 *
       
 29706 		 * // Remove specific instance by id
       
 29707 		 * tinymce.remove('#id');
       
 29708 		 *
       
 29709 		 * @method remove
       
 29710 		 * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove.
       
 29711 		 * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
       
 29712 		 */
       
 29713 		remove: function(selector) {
       
 29714 			var self = this, i, editors = self.editors, editor;
       
 29715 
       
 29716 			// Remove all editors
       
 29717 			if (!selector) {
       
 29718 				for (i = editors.length - 1; i >= 0; i--) {
       
 29719 					self.remove(editors[i]);
       
 29720 				}
       
 29721 
       
 29722 				return;
       
 29723 			}
       
 29724 
       
 29725 			// Remove editors by selector
       
 29726 			if (typeof selector == "string") {
       
 29727 				selector = selector.selector || selector;
       
 29728 
       
 29729 				each(DOM.select(selector), function(elm) {
       
 29730 					editor = editors[elm.id];
       
 29731 
       
 29732 					if (editor) {
       
 29733 						self.remove(editor);
       
 29734 					}
       
 29735 				});
       
 29736 
       
 29737 				return;
       
 29738 			}
       
 29739 
       
 29740 			// Remove specific editor
       
 29741 			editor = selector;
       
 29742 
       
 29743 			// Not in the collection
       
 29744 			if (!editors[editor.id]) {
       
 29745 				return null;
       
 29746 			}
       
 29747 
       
 29748 			/**
       
 29749 			 * Fires when an editor is removed from EditorManager collection.
       
 29750 			 *
       
 29751 			 * @event RemoveEditor
       
 29752 			 * @param {Object} e Event arguments.
       
 29753 			 */
       
 29754 			if (removeEditorFromList(editor)) {
       
 29755 				self.fire('RemoveEditor', {editor: editor});
       
 29756 			}
       
 29757 
       
 29758 			if (!editors.length) {
       
 29759 				DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
       
 29760 			}
       
 29761 
       
 29762 			editor.remove();
       
 29763 
       
 29764 			return editor;
       
 29765 		},
       
 29766 
       
 29767 		/**
       
 29768 		 * Executes a specific command on the currently active editor.
       
 29769 		 *
       
 29770 		 * @method execCommand
       
 29771 		 * @param {String} c Command to perform for example Bold.
       
 29772 		 * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
       
 29773 		 * @param {String} v Optional value parameter like for example an URL to a link.
       
 29774 		 * @return {Boolean} true/false if the command was executed or not.
       
 29775 		 */
       
 29776 		execCommand: function(cmd, ui, value) {
       
 29777 			var self = this, editor = self.get(value);
       
 29778 
       
 29779 			// Manager commands
       
 29780 			switch (cmd) {
       
 29781 				case "mceAddEditor":
       
 29782 					if (!self.get(value)) {
       
 29783 						new Editor(value, self.settings, self).render();
       
 29784 					}
       
 29785 
       
 29786 					return true;
       
 29787 
       
 29788 				case "mceRemoveEditor":
       
 29789 					if (editor) {
       
 29790 						editor.remove();
       
 29791 					}
       
 29792 
       
 29793 					return true;
       
 29794 
       
 29795 				case 'mceToggleEditor':
       
 29796 					if (!editor) {
       
 29797 						self.execCommand('mceAddEditor', 0, value);
       
 29798 						return true;
       
 29799 					}
       
 29800 
       
 29801 					if (editor.isHidden()) {
       
 29802 						editor.show();
       
 29803 					} else {
       
 29804 						editor.hide();
       
 29805 					}
       
 29806 
       
 29807 					return true;
       
 29808 			}
       
 29809 
       
 29810 			// Run command on active editor
       
 29811 			if (self.activeEditor) {
       
 29812 				return self.activeEditor.execCommand(cmd, ui, value);
       
 29813 			}
       
 29814 
       
 29815 			return false;
       
 29816 		},
       
 29817 
       
 29818 		/**
       
 29819 		 * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
       
 29820 		 *
       
 29821 		 * @method triggerSave
       
 29822 		 * @example
       
 29823 		 * // Saves all contents
       
 29824 		 * tinyMCE.triggerSave();
       
 29825 		 */
       
 29826 		triggerSave: function() {
       
 29827 			each(this.editors, function(editor) {
       
 29828 				editor.save();
       
 29829 			});
       
 29830 		},
       
 29831 
       
 29832 		/**
       
 29833 		 * Adds a language pack, this gets called by the loaded language files like en.js.
       
 29834 		 *
       
 29835 		 * @method addI18n
       
 29836 		 * @param {String} code Optional language code.
       
 29837 		 * @param {Object} items Name/value object with translations.
       
 29838 		 */
       
 29839 		addI18n: function(code, items) {
       
 29840 			I18n.add(code, items);
       
 29841 		},
       
 29842 
       
 29843 		/**
       
 29844 		 * Translates the specified string using the language pack items.
       
 29845 		 *
       
 29846 		 * @method translate
       
 29847 		 * @param {String/Array/Object} text String to translate
       
 29848 		 * @return {String} Translated string.
       
 29849 		 */
       
 29850 		translate: function(text) {
       
 29851 			return I18n.translate(text);
       
 29852 		},
       
 29853 
       
 29854 		/**
       
 29855 		 * Sets the active editor instance and fires the deactivate/activate events.
       
 29856 		 *
       
 29857 		 * @method setActive
       
 29858 		 * @param {tinymce.Editor} editor Editor instance to set as the active instance.
       
 29859 		 */
       
 29860 		setActive: function(editor) {
       
 29861 			var activeEditor = this.activeEditor;
       
 29862 
       
 29863 			if (this.activeEditor != editor) {
       
 29864 				if (activeEditor) {
       
 29865 					activeEditor.fire('deactivate', {relatedTarget: editor});
       
 29866 				}
       
 29867 
       
 29868 				editor.fire('activate', {relatedTarget: activeEditor});
       
 29869 			}
       
 29870 
       
 29871 			this.activeEditor = editor;
       
 29872 		}
       
 29873 	};
       
 29874 
       
 29875 	extend(EditorManager, Observable);
       
 29876 
       
 29877 	EditorManager.setup();
       
 29878 
       
 29879 	// Export EditorManager as tinymce/tinymce in global namespace
       
 29880 	window.tinymce = window.tinyMCE = EditorManager;
       
 29881 
       
 29882 	return EditorManager;
       
 29883 });
       
 29884 
       
 29885 // Included from: js/tinymce/classes/LegacyInput.js
       
 29886 
       
 29887 /**
       
 29888  * LegacyInput.js
       
 29889  *
       
 29890  * Copyright, Moxiecode Systems AB
       
 29891  * Released under LGPL License.
       
 29892  *
       
 29893  * License: http://www.tinymce.com/license
       
 29894  * Contributing: http://www.tinymce.com/contributing
       
 29895  */
       
 29896 
       
 29897 define("tinymce/LegacyInput", [
       
 29898 	"tinymce/EditorManager",
       
 29899 	"tinymce/util/Tools"
       
 29900 ], function(EditorManager, Tools) {
       
 29901 	var each = Tools.each, explode = Tools.explode;
       
 29902 
       
 29903 	EditorManager.on('AddEditor', function(e) {
       
 29904 		var editor = e.editor;
       
 29905 
       
 29906 		editor.on('preInit', function() {
       
 29907 			var filters, fontSizes, dom, settings = editor.settings;
       
 29908 
       
 29909 			function replaceWithSpan(node, styles) {
       
 29910 				each(styles, function(value, name) {
       
 29911 					if (value) {
       
 29912 						dom.setStyle(node, name, value);
       
 29913 					}
       
 29914 				});
       
 29915 
       
 29916 				dom.rename(node, 'span');
       
 29917 			}
       
 29918 
       
 29919 			function convert(e) {
       
 29920 				dom = editor.dom;
       
 29921 
       
 29922 				if (settings.convert_fonts_to_spans) {
       
 29923 					each(dom.select('font,u,strike', e.node), function(node) {
       
 29924 						filters[node.nodeName.toLowerCase()](dom, node);
       
 29925 					});
       
 29926 				}
       
 29927 			}
       
 29928 
       
 29929 			if (settings.inline_styles) {
       
 29930 				fontSizes = explode(settings.font_size_legacy_values);
       
 29931 
       
 29932 				filters = {
       
 29933 					font: function(dom, node) {
       
 29934 						replaceWithSpan(node, {
       
 29935 							backgroundColor: node.style.backgroundColor,
       
 29936 							color: node.color,
       
 29937 							fontFamily: node.face,
       
 29938 							fontSize: fontSizes[parseInt(node.size, 10) - 1]
       
 29939 						});
       
 29940 					},
       
 29941 
       
 29942 					u: function(dom, node) {
       
 29943 						// HTML5 allows U element
       
 29944 						if (editor.settings.schema === "html4") {
       
 29945 							replaceWithSpan(node, {
       
 29946 								textDecoration: 'underline'
       
 29947 							});
       
 29948 						}
       
 29949 					},
       
 29950 
       
 29951 					strike: function(dom, node) {
       
 29952 						replaceWithSpan(node, {
       
 29953 							textDecoration: 'line-through'
       
 29954 						});
       
 29955 					}
       
 29956 				};
       
 29957 
       
 29958 				editor.on('PreProcess SetContent', convert);
       
 29959 			}
       
 29960 		});
       
 29961 	});
       
 29962 });
       
 29963 
       
 29964 // Included from: js/tinymce/classes/util/XHR.js
       
 29965 
       
 29966 /**
       
 29967  * XHR.js
       
 29968  *
       
 29969  * Copyright, Moxiecode Systems AB
       
 29970  * Released under LGPL License.
       
 29971  *
       
 29972  * License: http://www.tinymce.com/license
       
 29973  * Contributing: http://www.tinymce.com/contributing
       
 29974  */
       
 29975 
       
 29976 /**
       
 29977  * This class enables you to send XMLHTTPRequests cross browser.
       
 29978  * @class tinymce.util.XHR
       
 29979  * @mixes tinymce.util.Observable
       
 29980  * @static
       
 29981  * @example
       
 29982  * // Sends a low level Ajax request
       
 29983  * tinymce.util.XHR.send({
       
 29984  *    url: 'someurl',
       
 29985  *    success: function(text) {
       
 29986  *       console.debug(text);
       
 29987  *    }
       
 29988  * });
       
 29989  *
       
 29990  * // Add custom header to XHR request
       
 29991  * tinymce.util.XHR.on('beforeSend', function(e) {
       
 29992  *     e.xhr.setRequestHeader('X-Requested-With', 'Something');
       
 29993  * });
       
 29994  */
       
 29995 define("tinymce/util/XHR", [
       
 29996 	"tinymce/util/Observable",
       
 29997 	"tinymce/util/Tools"
       
 29998 ], function(Observable, Tools) {
       
 29999 	var XHR = {
       
 30000 		/**
       
 30001 		 * Sends a XMLHTTPRequest.
       
 30002 		 * Consult the Wiki for details on what settings this method takes.
       
 30003 		 *
       
 30004 		 * @method send
       
 30005 		 * @param {Object} settings Object will target URL, callbacks and other info needed to make the request.
       
 30006 		 */
       
 30007 		send: function(settings) {
       
 30008 			var xhr, count = 0;
       
 30009 
       
 30010 			function ready() {
       
 30011 				if (!settings.async || xhr.readyState == 4 || count++ > 10000) {
       
 30012 					if (settings.success && count < 10000 && xhr.status == 200) {
       
 30013 						settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings);
       
 30014 					} else if (settings.error) {
       
 30015 						settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings);
       
 30016 					}
       
 30017 
       
 30018 					xhr = null;
       
 30019 				} else {
       
 30020 					setTimeout(ready, 10);
       
 30021 				}
       
 30022 			}
       
 30023 
       
 30024 			// Default settings
       
 30025 			settings.scope = settings.scope || this;
       
 30026 			settings.success_scope = settings.success_scope || settings.scope;
       
 30027 			settings.error_scope = settings.error_scope || settings.scope;
       
 30028 			settings.async = settings.async === false ? false : true;
       
 30029 			settings.data = settings.data || '';
       
 30030 
       
 30031 			xhr = new XMLHttpRequest();
       
 30032 
       
 30033 			if (xhr) {
       
 30034 				if (xhr.overrideMimeType) {
       
 30035 					xhr.overrideMimeType(settings.content_type);
       
 30036 				}
       
 30037 
       
 30038 				xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
       
 30039 
       
 30040 				if (settings.crossDomain) {
       
 30041 					xhr.withCredentials = true;
       
 30042 				}
       
 30043 
       
 30044 				if (settings.content_type) {
       
 30045 					xhr.setRequestHeader('Content-Type', settings.content_type);
       
 30046 				}
       
 30047 
       
 30048 				xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
       
 30049 
       
 30050 				xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr;
       
 30051 				xhr.send(settings.data);
       
 30052 
       
 30053 				// Syncronous request
       
 30054 				if (!settings.async) {
       
 30055 					return ready();
       
 30056 				}
       
 30057 
       
 30058 				// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
       
 30059 				setTimeout(ready, 10);
       
 30060 			}
       
 30061 		}
       
 30062 	};
       
 30063 
       
 30064 	Tools.extend(XHR, Observable);
       
 30065 
       
 30066 	return XHR;
       
 30067 });
       
 30068 
       
 30069 // Included from: js/tinymce/classes/util/JSON.js
       
 30070 
       
 30071 /**
       
 30072  * JSON.js
       
 30073  *
       
 30074  * Copyright, Moxiecode Systems AB
       
 30075  * Released under LGPL License.
       
 30076  *
       
 30077  * License: http://www.tinymce.com/license
       
 30078  * Contributing: http://www.tinymce.com/contributing
       
 30079  */
       
 30080 
       
 30081 /**
       
 30082  * JSON parser and serializer class.
       
 30083  *
       
 30084  * @class tinymce.util.JSON
       
 30085  * @static
       
 30086  * @example
       
 30087  * // JSON parse a string into an object
       
 30088  * var obj = tinymce.util.JSON.parse(somestring);
       
 30089  *
       
 30090  * // JSON serialize a object into an string
       
 30091  * var str = tinymce.util.JSON.serialize(obj);
       
 30092  */
       
 30093 define("tinymce/util/JSON", [], function() {
       
 30094 	function serialize(o, quote) {
       
 30095 		var i, v, t, name;
       
 30096 
       
 30097 		quote = quote || '"';
       
 30098 
       
 30099 		if (o === null) {
       
 30100 			return 'null';
       
 30101 		}
       
 30102 
       
 30103 		t = typeof o;
       
 30104 
       
 30105 		if (t == 'string') {
       
 30106 			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
       
 30107 
       
 30108 			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
       
 30109 				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
       
 30110 				if (quote === '"' && a === "'") {
       
 30111 					return a;
       
 30112 				}
       
 30113 
       
 30114 				i = v.indexOf(b);
       
 30115 
       
 30116 				if (i + 1) {
       
 30117 					return '\\' + v.charAt(i + 1);
       
 30118 				}
       
 30119 
       
 30120 				a = b.charCodeAt().toString(16);
       
 30121 
       
 30122 				return '\\u' + '0000'.substring(a.length) + a;
       
 30123 			}) + quote;
       
 30124 		}
       
 30125 
       
 30126 		if (t == 'object') {
       
 30127 			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
       
 30128 				for (i = 0, v = '['; i < o.length; i++) {
       
 30129 					v += (i > 0 ? ',' : '') + serialize(o[i], quote);
       
 30130 				}
       
 30131 
       
 30132 				return v + ']';
       
 30133 			}
       
 30134 
       
 30135 			v = '{';
       
 30136 
       
 30137 			for (name in o) {
       
 30138 				if (o.hasOwnProperty(name)) {
       
 30139 					v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name +
       
 30140 						quote + ':' + serialize(o[name], quote) : '';
       
 30141 				}
       
 30142 			}
       
 30143 
       
 30144 			return v + '}';
       
 30145 		}
       
 30146 
       
 30147 		return '' + o;
       
 30148 	}
       
 30149 
       
 30150 	return {
       
 30151 		/**
       
 30152 		 * Serializes the specified object as a JSON string.
       
 30153 		 *
       
 30154 		 * @method serialize
       
 30155 		 * @param {Object} obj Object to serialize as a JSON string.
       
 30156 		 * @param {String} quote Optional quote string defaults to ".
       
 30157 		 * @return {string} JSON string serialized from input.
       
 30158 		 */
       
 30159 		serialize: serialize,
       
 30160 
       
 30161 		/**
       
 30162 		 * Unserializes/parses the specified JSON string into a object.
       
 30163 		 *
       
 30164 		 * @method parse
       
 30165 		 * @param {string} s JSON String to parse into a JavaScript object.
       
 30166 		 * @return {Object} Object from input JSON string or undefined if it failed.
       
 30167 		 */
       
 30168 		parse: function(text) {
       
 30169 			try {
       
 30170 				// Trick uglify JS
       
 30171 				return window[String.fromCharCode(101) + 'val']('(' + text + ')');
       
 30172 			} catch (ex) {
       
 30173 				// Ignore
       
 30174 			}
       
 30175 		}
       
 30176 
       
 30177 		/**#@-*/
       
 30178 	};
       
 30179 });
       
 30180 
       
 30181 // Included from: js/tinymce/classes/util/JSONRequest.js
       
 30182 
       
 30183 /**
       
 30184  * JSONRequest.js
       
 30185  *
       
 30186  * Copyright, Moxiecode Systems AB
       
 30187  * Released under LGPL License.
       
 30188  *
       
 30189  * License: http://www.tinymce.com/license
       
 30190  * Contributing: http://www.tinymce.com/contributing
       
 30191  */
       
 30192 
       
 30193 /**
       
 30194  * This class enables you to use JSON-RPC to call backend methods.
       
 30195  *
       
 30196  * @class tinymce.util.JSONRequest
       
 30197  * @example
       
 30198  * var json = new tinymce.util.JSONRequest({
       
 30199  *     url: 'somebackend.php'
       
 30200  * });
       
 30201  *
       
 30202  * // Send RPC call 1
       
 30203  * json.send({
       
 30204  *     method: 'someMethod1',
       
 30205  *     params: ['a', 'b'],
       
 30206  *     success: function(result) {
       
 30207  *         console.dir(result);
       
 30208  *     }
       
 30209  * });
       
 30210  *
       
 30211  * // Send RPC call 2
       
 30212  * json.send({
       
 30213  *     method: 'someMethod2',
       
 30214  *     params: ['a', 'b'],
       
 30215  *     success: function(result) {
       
 30216  *         console.dir(result);
       
 30217  *     }
       
 30218  * });
       
 30219  */
       
 30220 define("tinymce/util/JSONRequest", [
       
 30221 	"tinymce/util/JSON",
       
 30222 	"tinymce/util/XHR",
       
 30223 	"tinymce/util/Tools"
       
 30224 ], function(JSON, XHR, Tools) {
       
 30225 	var extend = Tools.extend;
       
 30226 
       
 30227 	function JSONRequest(settings) {
       
 30228 		this.settings = extend({}, settings);
       
 30229 		this.count = 0;
       
 30230 	}
       
 30231 
       
 30232 	/**
       
 30233 	 * Simple helper function to send a JSON-RPC request without the need to initialize an object.
       
 30234 	 * Consult the Wiki API documentation for more details on what you can pass to this function.
       
 30235 	 *
       
 30236 	 * @method sendRPC
       
 30237 	 * @static
       
 30238 	 * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc.
       
 30239 	 */
       
 30240 	JSONRequest.sendRPC = function(o) {
       
 30241 		return new JSONRequest().send(o);
       
 30242 	};
       
 30243 
       
 30244 	JSONRequest.prototype = {
       
 30245 		/**
       
 30246 		 * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function.
       
 30247 		 *
       
 30248 		 * @method send
       
 30249 		 * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc.
       
 30250 		 */
       
 30251 		send: function(args) {
       
 30252 			var ecb = args.error, scb = args.success;
       
 30253 
       
 30254 			args = extend(this.settings, args);
       
 30255 
       
 30256 			args.success = function(c, x) {
       
 30257 				c = JSON.parse(c);
       
 30258 
       
 30259 				if (typeof c == 'undefined') {
       
 30260 					c = {
       
 30261 						error: 'JSON Parse error.'
       
 30262 					};
       
 30263 				}
       
 30264 
       
 30265 				if (c.error) {
       
 30266 					ecb.call(args.error_scope || args.scope, c.error, x);
       
 30267 				} else {
       
 30268 					scb.call(args.success_scope || args.scope, c.result);
       
 30269 				}
       
 30270 			};
       
 30271 
       
 30272 			args.error = function(ty, x) {
       
 30273 				if (ecb) {
       
 30274 					ecb.call(args.error_scope || args.scope, ty, x);
       
 30275 				}
       
 30276 			};
       
 30277 
       
 30278 			args.data = JSON.serialize({
       
 30279 				id: args.id || 'c' + (this.count++),
       
 30280 				method: args.method,
       
 30281 				params: args.params
       
 30282 			});
       
 30283 
       
 30284 			// JSON content type for Ruby on rails. Bug: #1883287
       
 30285 			args.content_type = 'application/json';
       
 30286 
       
 30287 			XHR.send(args);
       
 30288 		}
       
 30289 	};
       
 30290 
       
 30291 	return JSONRequest;
       
 30292 });
       
 30293 
       
 30294 // Included from: js/tinymce/classes/util/JSONP.js
       
 30295 
       
 30296 /**
       
 30297  * JSONP.js
       
 30298  *
       
 30299  * Copyright, Moxiecode Systems AB
       
 30300  * Released under LGPL License.
       
 30301  *
       
 30302  * License: http://www.tinymce.com/license
       
 30303  * Contributing: http://www.tinymce.com/contributing
       
 30304  */
       
 30305 
       
 30306 define("tinymce/util/JSONP", [
       
 30307 	"tinymce/dom/DOMUtils"
       
 30308 ], function(DOMUtils) {
       
 30309 	return {
       
 30310 		callbacks: {},
       
 30311 		count: 0,
       
 30312 
       
 30313 		send: function(settings) {
       
 30314 			var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count;
       
 30315 			var id = 'tinymce_jsonp_' + count;
       
 30316 
       
 30317 			self.callbacks[count] = function(json) {
       
 30318 				dom.remove(id);
       
 30319 				delete self.callbacks[count];
       
 30320 
       
 30321 				settings.callback(json);
       
 30322 			};
       
 30323 
       
 30324 			dom.add(dom.doc.body, 'script', {
       
 30325 				id: id,
       
 30326 				src: settings.url,
       
 30327 				type: 'text/javascript'
       
 30328 			});
       
 30329 
       
 30330 			self.count++;
       
 30331 		}
       
 30332 	};
       
 30333 });
       
 30334 
       
 30335 // Included from: js/tinymce/classes/util/LocalStorage.js
       
 30336 
       
 30337 /**
       
 30338  * LocalStorage.js
       
 30339  *
       
 30340  * Copyright, Moxiecode Systems AB
       
 30341  * Released under LGPL License.
       
 30342  *
       
 30343  * License: http://www.tinymce.com/license
       
 30344  * Contributing: http://www.tinymce.com/contributing
       
 30345  */
       
 30346 
       
 30347 /**
       
 30348  * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
       
 30349  * Storage is done using userData on IE 7 and a special serialization format. The format is designed
       
 30350  * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
       
 30351  * makes it possible to store for example HTML data.
       
 30352  *
       
 30353  * Storage format for userData:
       
 30354  * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
       
 30355  *
       
 30356  * For example this data key1=value1,key2=value2 would be:
       
 30357  * 4,key1,6,value1,4,key2,6,value2
       
 30358  *
       
 30359  * @class tinymce.util.LocalStorage
       
 30360  * @static
       
 30361  * @version 4.0
       
 30362  * @example
       
 30363  * tinymce.util.LocalStorage.setItem('key', 'value');
       
 30364  * var value = tinymce.util.LocalStorage.getItem('key');
       
 30365  */
       
 30366 define("tinymce/util/LocalStorage", [], function() {
       
 30367 	var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
       
 30368 
       
 30369 	// Check for native support
       
 30370 	try {
       
 30371 		if (window.localStorage) {
       
 30372 			return localStorage;
       
 30373 		}
       
 30374 	} catch (ex) {
       
 30375 		// Ignore
       
 30376 	}
       
 30377 
       
 30378 	userDataKey = "tinymce";
       
 30379 	storageElm = document.documentElement;
       
 30380 	hasOldIEDataSupport = !!storageElm.addBehavior;
       
 30381 
       
 30382 	if (hasOldIEDataSupport) {
       
 30383 		storageElm.addBehavior('#default#userData');
       
 30384 	}
       
 30385 
       
 30386 	/**
       
 30387 	 * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
       
 30388 	 */
       
 30389 	function updateKeys() {
       
 30390 		keys = [];
       
 30391 
       
 30392 		for (var key in items) {
       
 30393 			keys.push(key);
       
 30394 		}
       
 30395 
       
 30396 		LocalStorage.length = keys.length;
       
 30397 	}
       
 30398 
       
 30399 	/**
       
 30400 	 * Loads the userData string and parses it into the items structure.
       
 30401 	 */
       
 30402 	function load() {
       
 30403 		var key, data, value, pos = 0;
       
 30404 
       
 30405 		items = {};
       
 30406 
       
 30407 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
       
 30408 		if (!hasOldIEDataSupport) {
       
 30409 			return;
       
 30410 		}
       
 30411 
       
 30412 		function next(end) {
       
 30413 			var value, nextPos;
       
 30414 
       
 30415 			nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
       
 30416 			if (nextPos === -1 || nextPos > data.length) {
       
 30417 				return null;
       
 30418 			}
       
 30419 
       
 30420 			value = data.substring(pos, nextPos);
       
 30421 			pos = nextPos + 1;
       
 30422 
       
 30423 			return value;
       
 30424 		}
       
 30425 
       
 30426 		storageElm.load(userDataKey);
       
 30427 		data = storageElm.getAttribute(userDataKey) || '';
       
 30428 
       
 30429 		do {
       
 30430 			var offset = next();
       
 30431 			if (offset === null) {
       
 30432 				break;
       
 30433 			}
       
 30434 
       
 30435 			key = next(parseInt(offset, 32) || 0);
       
 30436 			if (key !== null) {
       
 30437 				offset = next();
       
 30438 				if (offset === null) {
       
 30439 					break;
       
 30440 				}
       
 30441 
       
 30442 				value = next(parseInt(offset, 32) || 0);
       
 30443 
       
 30444 				if (key) {
       
 30445 					items[key] = value;
       
 30446 				}
       
 30447 			}
       
 30448 		} while (key !== null);
       
 30449 
       
 30450 		updateKeys();
       
 30451 	}
       
 30452 
       
 30453 	/**
       
 30454 	 * Saves the items structure into a the userData format.
       
 30455 	 */
       
 30456 	function save() {
       
 30457 		var value, data = '';
       
 30458 
       
 30459 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
       
 30460 		if (!hasOldIEDataSupport) {
       
 30461 			return;
       
 30462 		}
       
 30463 
       
 30464 		for (var key in items) {
       
 30465 			value = items[key];
       
 30466 			data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
       
 30467 		}
       
 30468 
       
 30469 		storageElm.setAttribute(userDataKey, data);
       
 30470 
       
 30471 		try {
       
 30472 			storageElm.save(userDataKey);
       
 30473 		} catch (ex) {
       
 30474 			// Ignore disk full
       
 30475 		}
       
 30476 
       
 30477 		updateKeys();
       
 30478 	}
       
 30479 
       
 30480 	LocalStorage = {
       
 30481 		/**
       
 30482 		 * Length of the number of items in storage.
       
 30483 		 *
       
 30484 		 * @property length
       
 30485 		 * @type Number
       
 30486 		 * @return {Number} Number of items in storage.
       
 30487 		 */
       
 30488 		//length:0,
       
 30489 
       
 30490 		/**
       
 30491 		 * Returns the key name by index.
       
 30492 		 *
       
 30493 		 * @method key
       
 30494 		 * @param {Number} index Index of key to return.
       
 30495 		 * @return {String} Key value or null if it wasn't found.
       
 30496 		 */
       
 30497 		key: function(index) {
       
 30498 			return keys[index];
       
 30499 		},
       
 30500 
       
 30501 		/**
       
 30502 		 * Returns the value if the specified key or null if it wasn't found.
       
 30503 		 *
       
 30504 		 * @method getItem
       
 30505 		 * @param {String} key Key of item to retrive.
       
 30506 		 * @return {String} Value of the specified item or null if it wasn't found.
       
 30507 		 */
       
 30508 		getItem: function(key) {
       
 30509 			return key in items ? items[key] : null;
       
 30510 		},
       
 30511 
       
 30512 		/**
       
 30513 		 * Sets the value of the specified item by it's key.
       
 30514 		 *
       
 30515 		 * @method setItem
       
 30516 		 * @param {String} key Key of the item to set.
       
 30517 		 * @param {String} value Value of the item to set.
       
 30518 		 */
       
 30519 		setItem: function(key, value) {
       
 30520 			items[key] = "" + value;
       
 30521 			save();
       
 30522 		},
       
 30523 
       
 30524 		/**
       
 30525 		 * Removes the specified item by key.
       
 30526 		 *
       
 30527 		 * @method removeItem
       
 30528 		 * @param {String} key Key of item to remove.
       
 30529 		 */
       
 30530 		removeItem: function(key) {
       
 30531 			delete items[key];
       
 30532 			save();
       
 30533 		},
       
 30534 
       
 30535 		/**
       
 30536 		 * Removes all items.
       
 30537 		 *
       
 30538 		 * @method clear
       
 30539 		 */
       
 30540 		clear: function() {
       
 30541 			items = {};
       
 30542 			save();
       
 30543 		}
       
 30544 	};
       
 30545 
       
 30546 	load();
       
 30547 
       
 30548 	return LocalStorage;
       
 30549 });
       
 30550 
       
 30551 // Included from: js/tinymce/classes/Compat.js
       
 30552 
       
 30553 /**
       
 30554  * Compat.js
       
 30555  *
       
 30556  * Copyright, Moxiecode Systems AB
       
 30557  * Released under LGPL License.
       
 30558  *
       
 30559  * License: http://www.tinymce.com/license
       
 30560  * Contributing: http://www.tinymce.com/contributing
       
 30561  */
       
 30562 
       
 30563 /**
       
 30564  * TinyMCE core class.
       
 30565  *
       
 30566  * @static
       
 30567  * @class tinymce
       
 30568  * @borrow-members tinymce.EditorManager
       
 30569  * @borrow-members tinymce.util.Tools
       
 30570  */
       
 30571 define("tinymce/Compat", [
       
 30572 	"tinymce/dom/DOMUtils",
       
 30573 	"tinymce/dom/EventUtils",
       
 30574 	"tinymce/dom/ScriptLoader",
       
 30575 	"tinymce/AddOnManager",
       
 30576 	"tinymce/util/Tools",
       
 30577 	"tinymce/Env"
       
 30578 ], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) {
       
 30579 	var tinymce = window.tinymce;
       
 30580 
       
 30581 	/**
       
 30582 	 * @property {tinymce.dom.DOMUtils} DOM Global DOM instance.
       
 30583 	 * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance.
       
 30584 	 * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance.
       
 30585 	 * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance.
       
 30586 	 */
       
 30587 	tinymce.DOM = DOMUtils.DOM;
       
 30588 	tinymce.ScriptLoader = ScriptLoader.ScriptLoader;
       
 30589 	tinymce.PluginManager = AddOnManager.PluginManager;
       
 30590 	tinymce.ThemeManager = AddOnManager.ThemeManager;
       
 30591 
       
 30592 	tinymce.dom = tinymce.dom || {};
       
 30593 	tinymce.dom.Event = EventUtils.Event;
       
 30594 
       
 30595 	Tools.each(Tools, function(func, key) {
       
 30596 		tinymce[key] = func;
       
 30597 	});
       
 30598 
       
 30599 	Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
       
 30600 		tinymce[name] = Env[name.substr(2).toLowerCase()];
       
 30601 	});
       
 30602 
       
 30603 	return {};
       
 30604 });
       
 30605 
       
 30606 // Describe the different namespaces
       
 30607 
       
 30608 /**
       
 30609  * Root level namespace this contains classes directly releated to the TinyMCE editor.
       
 30610  *
       
 30611  * @namespace tinymce
       
 30612  */
       
 30613 
       
 30614 /**
       
 30615  * Contains classes for handling the browsers DOM.
       
 30616  *
       
 30617  * @namespace tinymce.dom
       
 30618  */
       
 30619 
       
 30620 /**
       
 30621  * Contains html parser and serializer logic.
       
 30622  *
       
 30623  * @namespace tinymce.html
       
 30624  */
       
 30625 
       
 30626 /**
       
 30627  * Contains the different UI types such as buttons, listboxes etc.
       
 30628  *
       
 30629  * @namespace tinymce.ui
       
 30630  */
       
 30631 
       
 30632 /**
       
 30633  * Contains various utility classes such as json parser, cookies etc.
       
 30634  *
       
 30635  * @namespace tinymce.util
       
 30636  */
       
 30637 
       
 30638 // Included from: js/tinymce/classes/ui/Layout.js
       
 30639 
       
 30640 /**
       
 30641  * Layout.js
       
 30642  *
       
 30643  * Copyright, Moxiecode Systems AB
       
 30644  * Released under LGPL License.
       
 30645  *
       
 30646  * License: http://www.tinymce.com/license
       
 30647  * Contributing: http://www.tinymce.com/contributing
       
 30648  */
       
 30649 
       
 30650 /**
       
 30651  * Base layout manager class.
       
 30652  *
       
 30653  * @class tinymce.ui.Layout
       
 30654  */
       
 30655 define("tinymce/ui/Layout", [
       
 30656 	"tinymce/util/Class",
       
 30657 	"tinymce/util/Tools"
       
 30658 ], function(Class, Tools) {
       
 30659 	"use strict";
       
 30660 
       
 30661 	return Class.extend({
       
 30662 		Defaults: {
       
 30663 			firstControlClass: 'first',
       
 30664 			lastControlClass: 'last'
       
 30665 		},
       
 30666 
       
 30667 		/**
       
 30668 		 * Constructs a layout instance with the specified settings.
       
 30669 		 *
       
 30670 		 * @constructor
       
 30671 		 * @param {Object} settings Name/value object with settings.
       
 30672 		 */
       
 30673 		init: function(settings) {
       
 30674 			this.settings = Tools.extend({}, this.Defaults, settings);
       
 30675 		},
       
 30676 
       
 30677 		/**
       
 30678 		 * This method gets invoked before the layout renders the controls.
       
 30679 		 *
       
 30680 		 * @method preRender
       
 30681 		 * @param {tinymce.ui.Container} container Container instance to preRender.
       
 30682 		 */
       
 30683 		preRender: function(container) {
       
 30684 			container.addClass(this.settings.containerClass, 'body');
       
 30685 		},
       
 30686 
       
 30687 		/**
       
 30688 		 * Applies layout classes to the container.
       
 30689 		 *
       
 30690 		 * @private
       
 30691 		 */
       
 30692 		applyClasses: function(container) {
       
 30693 			var self = this, settings = self.settings, items, firstClass, lastClass;
       
 30694 
       
 30695 			items = container.items().filter(':visible');
       
 30696 			firstClass = settings.firstControlClass;
       
 30697 			lastClass = settings.lastControlClass;
       
 30698 
       
 30699 			items.each(function(item) {
       
 30700 				item.removeClass(firstClass).removeClass(lastClass);
       
 30701 
       
 30702 				if (settings.controlClass) {
       
 30703 					item.addClass(settings.controlClass);
       
 30704 				}
       
 30705 			});
       
 30706 
       
 30707 			items.eq(0).addClass(firstClass);
       
 30708 			items.eq(-1).addClass(lastClass);
       
 30709 		},
       
 30710 
       
 30711 		/**
       
 30712 		 * Renders the specified container and any layout specific HTML.
       
 30713 		 *
       
 30714 		 * @method renderHtml
       
 30715 		 * @param {tinymce.ui.Container} container Container to render HTML for.
       
 30716 		 */
       
 30717 		renderHtml: function(container) {
       
 30718 			var self = this, settings = self.settings, items, html = '';
       
 30719 
       
 30720 			items = container.items();
       
 30721 			items.eq(0).addClass(settings.firstControlClass);
       
 30722 			items.eq(-1).addClass(settings.lastControlClass);
       
 30723 
       
 30724 			items.each(function(item) {
       
 30725 				if (settings.controlClass) {
       
 30726 					item.addClass(settings.controlClass);
       
 30727 				}
       
 30728 
       
 30729 				html += item.renderHtml();
       
 30730 			});
       
 30731 
       
 30732 			return html;
       
 30733 		},
       
 30734 
       
 30735 		/**
       
 30736 		 * Recalculates the positions of the controls in the specified container.
       
 30737 		 *
       
 30738 		 * @method recalc
       
 30739 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 30740 		 */
       
 30741 		recalc: function() {
       
 30742 		},
       
 30743 
       
 30744 		/**
       
 30745 		 * This method gets invoked after the layout renders the controls.
       
 30746 		 *
       
 30747 		 * @method postRender
       
 30748 		 * @param {tinymce.ui.Container} container Container instance to postRender.
       
 30749 		 */
       
 30750 		postRender: function() {
       
 30751 		}
       
 30752 	});
       
 30753 });
       
 30754 
       
 30755 // Included from: js/tinymce/classes/ui/AbsoluteLayout.js
       
 30756 
       
 30757 /**
       
 30758  * AbsoluteLayout.js
       
 30759  *
       
 30760  * Copyright, Moxiecode Systems AB
       
 30761  * Released under LGPL License.
       
 30762  *
       
 30763  * License: http://www.tinymce.com/license
       
 30764  * Contributing: http://www.tinymce.com/contributing
       
 30765  */
       
 30766 
       
 30767 /**
       
 30768  * LayoutManager for absolute positioning. This layout manager is more of
       
 30769  * a base class for other layouts but can be created and used directly.
       
 30770  *
       
 30771  * @-x-less AbsoluteLayout.less
       
 30772  * @class tinymce.ui.AbsoluteLayout
       
 30773  * @extends tinymce.ui.Layout
       
 30774  */
       
 30775 define("tinymce/ui/AbsoluteLayout", [
       
 30776 	"tinymce/ui/Layout"
       
 30777 ], function(Layout) {
       
 30778 	"use strict";
       
 30779 
       
 30780 	return Layout.extend({
       
 30781 		Defaults: {
       
 30782 			containerClass: 'abs-layout',
       
 30783 			controlClass: 'abs-layout-item'
       
 30784 		},
       
 30785 
       
 30786 		/**
       
 30787 		 * Recalculates the positions of the controls in the specified container.
       
 30788 		 *
       
 30789 		 * @method recalc
       
 30790 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 30791 		 */
       
 30792 		recalc: function(container) {
       
 30793 			container.items().filter(':visible').each(function(ctrl) {
       
 30794 				var settings = ctrl.settings;
       
 30795 
       
 30796 				ctrl.layoutRect({
       
 30797 					x: settings.x,
       
 30798 					y: settings.y,
       
 30799 					w: settings.w,
       
 30800 					h: settings.h
       
 30801 				});
       
 30802 
       
 30803 				if (ctrl.recalc) {
       
 30804 					ctrl.recalc();
       
 30805 				}
       
 30806 			});
       
 30807 		},
       
 30808 
       
 30809 		/**
       
 30810 		 * Renders the specified container and any layout specific HTML.
       
 30811 		 *
       
 30812 		 * @method renderHtml
       
 30813 		 * @param {tinymce.ui.Container} container Container to render HTML for.
       
 30814 		 */
       
 30815 		renderHtml: function(container) {
       
 30816 			return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container);
       
 30817 		}
       
 30818 	});
       
 30819 });
       
 30820 
       
 30821 // Included from: js/tinymce/classes/ui/Tooltip.js
       
 30822 
       
 30823 /**
       
 30824  * Tooltip.js
       
 30825  *
       
 30826  * Copyright, Moxiecode Systems AB
       
 30827  * Released under LGPL License.
       
 30828  *
       
 30829  * License: http://www.tinymce.com/license
       
 30830  * Contributing: http://www.tinymce.com/contributing
       
 30831  */
       
 30832 
       
 30833 /**
       
 30834  * Creates a tooltip instance.
       
 30835  *
       
 30836  * @-x-less ToolTip.less
       
 30837  * @class tinymce.ui.ToolTip
       
 30838  * @extends tinymce.ui.Control
       
 30839  * @mixes tinymce.ui.Movable
       
 30840  */
       
 30841 define("tinymce/ui/Tooltip", [
       
 30842 	"tinymce/ui/Control",
       
 30843 	"tinymce/ui/Movable"
       
 30844 ], function(Control, Movable) {
       
 30845 	return Control.extend({
       
 30846 		Mixins: [Movable],
       
 30847 
       
 30848 		Defaults: {
       
 30849 			classes: 'widget tooltip tooltip-n'
       
 30850 		},
       
 30851 
       
 30852 		/**
       
 30853 		 * Sets/gets the current label text.
       
 30854 		 *
       
 30855 		 * @method text
       
 30856 		 * @param {String} [text] New label text.
       
 30857 		 * @return {String|tinymce.ui.Tooltip} Current text or current label instance.
       
 30858 		 */
       
 30859 		text: function(value) {
       
 30860 			var self = this;
       
 30861 
       
 30862 			if (typeof value != "undefined") {
       
 30863 				self._value = value;
       
 30864 
       
 30865 				if (self._rendered) {
       
 30866 					self.getEl().lastChild.innerHTML = self.encode(value);
       
 30867 				}
       
 30868 
       
 30869 				return self;
       
 30870 			}
       
 30871 
       
 30872 			return self._value;
       
 30873 		},
       
 30874 
       
 30875 		/**
       
 30876 		 * Renders the control as a HTML string.
       
 30877 		 *
       
 30878 		 * @method renderHtml
       
 30879 		 * @return {String} HTML representing the control.
       
 30880 		 */
       
 30881 		renderHtml: function() {
       
 30882 			var self = this, prefix = self.classPrefix;
       
 30883 
       
 30884 			return (
       
 30885 				'<div id="' + self._id + '" class="' + self.classes() + '" role="presentation">' +
       
 30886 					'<div class="' + prefix + 'tooltip-arrow"></div>' +
       
 30887 					'<div class="' + prefix + 'tooltip-inner">' + self.encode(self._text) + '</div>' +
       
 30888 				'</div>'
       
 30889 			);
       
 30890 		},
       
 30891 
       
 30892 		/**
       
 30893 		 * Repaints the control after a layout operation.
       
 30894 		 *
       
 30895 		 * @method repaint
       
 30896 		 */
       
 30897 		repaint: function() {
       
 30898 			var self = this, style, rect;
       
 30899 
       
 30900 			style = self.getEl().style;
       
 30901 			rect = self._layoutRect;
       
 30902 
       
 30903 			style.left = rect.x + 'px';
       
 30904 			style.top = rect.y + 'px';
       
 30905 			style.zIndex = 0xFFFF + 0xFFFF;
       
 30906 		}
       
 30907 	});
       
 30908 });
       
 30909 
       
 30910 // Included from: js/tinymce/classes/ui/Widget.js
       
 30911 
       
 30912 /**
       
 30913  * Widget.js
       
 30914  *
       
 30915  * Copyright, Moxiecode Systems AB
       
 30916  * Released under LGPL License.
       
 30917  *
       
 30918  * License: http://www.tinymce.com/license
       
 30919  * Contributing: http://www.tinymce.com/contributing
       
 30920  */
       
 30921 
       
 30922 /**
       
 30923  * Widget base class a widget is a control that has a tooltip and some basic states.
       
 30924  *
       
 30925  * @class tinymce.ui.Widget
       
 30926  * @extends tinymce.ui.Control
       
 30927  */
       
 30928 define("tinymce/ui/Widget", [
       
 30929 	"tinymce/ui/Control",
       
 30930 	"tinymce/ui/Tooltip"
       
 30931 ], function(Control, Tooltip) {
       
 30932 	"use strict";
       
 30933 
       
 30934 	var tooltip;
       
 30935 
       
 30936 	var Widget = Control.extend({
       
 30937 		/**
       
 30938 		 * Constructs a instance with the specified settings.
       
 30939 		 *
       
 30940 		 * @constructor
       
 30941 		 * @param {Object} settings Name/value object with settings.
       
 30942 		 * @setting {String} tooltip Tooltip text to display when hovering.
       
 30943 		 * @setting {Boolean} autofocus True if the control should be focused when rendered.
       
 30944 		 * @setting {String} text Text to display inside widget.
       
 30945 		 */
       
 30946 		init: function(settings) {
       
 30947 			var self = this;
       
 30948 
       
 30949 			self._super(settings);
       
 30950 			settings = self.settings;
       
 30951 			self.canFocus = true;
       
 30952 
       
 30953 			if (settings.tooltip && Widget.tooltips !== false) {
       
 30954 				self.on('mouseenter', function(e) {
       
 30955 					var tooltip = self.tooltip().moveTo(-0xFFFF);
       
 30956 
       
 30957 					if (e.control == self) {
       
 30958 						var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
       
 30959 
       
 30960 						tooltip.toggleClass('tooltip-n', rel == 'bc-tc');
       
 30961 						tooltip.toggleClass('tooltip-nw', rel == 'bc-tl');
       
 30962 						tooltip.toggleClass('tooltip-ne', rel == 'bc-tr');
       
 30963 
       
 30964 						tooltip.moveRel(self.getEl(), rel);
       
 30965 					} else {
       
 30966 						tooltip.hide();
       
 30967 					}
       
 30968 				});
       
 30969 
       
 30970 				self.on('mouseleave mousedown click', function() {
       
 30971 					self.tooltip().hide();
       
 30972 				});
       
 30973 			}
       
 30974 
       
 30975 			self.aria('label', settings.ariaLabel || settings.tooltip);
       
 30976 		},
       
 30977 
       
 30978 		/**
       
 30979 		 * Returns the current tooltip instance.
       
 30980 		 *
       
 30981 		 * @method tooltip
       
 30982 		 * @return {tinymce.ui.Tooltip} Tooltip instance.
       
 30983 		 */
       
 30984 		tooltip: function() {
       
 30985 			if (!tooltip) {
       
 30986 				tooltip = new Tooltip({type: 'tooltip'});
       
 30987 				tooltip.renderTo();
       
 30988 			}
       
 30989 
       
 30990 			return tooltip;
       
 30991 		},
       
 30992 
       
 30993 		/**
       
 30994 		 * Sets/gets the active state of the widget.
       
 30995 		 *
       
 30996 		 * @method active
       
 30997 		 * @param {Boolean} [state] State if the control is active.
       
 30998 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
       
 30999 		 */
       
 31000 		active: function(state) {
       
 31001 			var self = this, undef;
       
 31002 
       
 31003 			if (state !== undef) {
       
 31004 				self.aria('pressed', state);
       
 31005 				self.toggleClass('active', state);
       
 31006 			}
       
 31007 
       
 31008 			return self._super(state);
       
 31009 		},
       
 31010 
       
 31011 		/**
       
 31012 		 * Sets/gets the disabled state of the widget.
       
 31013 		 *
       
 31014 		 * @method disabled
       
 31015 		 * @param {Boolean} [state] State if the control is disabled.
       
 31016 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
       
 31017 		 */
       
 31018 		disabled: function(state) {
       
 31019 			var self = this, undef;
       
 31020 
       
 31021 			if (state !== undef) {
       
 31022 				self.aria('disabled', state);
       
 31023 				self.toggleClass('disabled', state);
       
 31024 			}
       
 31025 
       
 31026 			return self._super(state);
       
 31027 		},
       
 31028 
       
 31029 		/**
       
 31030 		 * Called after the control has been rendered.
       
 31031 		 *
       
 31032 		 * @method postRender
       
 31033 		 */
       
 31034 		postRender: function() {
       
 31035 			var self = this, settings = self.settings;
       
 31036 
       
 31037 			self._rendered = true;
       
 31038 
       
 31039 			self._super();
       
 31040 
       
 31041 			if (!self.parent() && (settings.width || settings.height)) {
       
 31042 				self.initLayoutRect();
       
 31043 				self.repaint();
       
 31044 			}
       
 31045 
       
 31046 			if (settings.autofocus) {
       
 31047 				self.focus();
       
 31048 			}
       
 31049 		},
       
 31050 
       
 31051 		/**
       
 31052 		 * Removes the current control from DOM and from UI collections.
       
 31053 		 *
       
 31054 		 * @method remove
       
 31055 		 * @return {tinymce.ui.Control} Current control instance.
       
 31056 		 */
       
 31057 		remove: function() {
       
 31058 			this._super();
       
 31059 
       
 31060 			if (tooltip) {
       
 31061 				tooltip.remove();
       
 31062 				tooltip = null;
       
 31063 			}
       
 31064 		}
       
 31065 	});
       
 31066 
       
 31067 	return Widget;
       
 31068 });
       
 31069 
       
 31070 // Included from: js/tinymce/classes/ui/Button.js
       
 31071 
       
 31072 /**
       
 31073  * Button.js
       
 31074  *
       
 31075  * Copyright, Moxiecode Systems AB
       
 31076  * Released under LGPL License.
       
 31077  *
       
 31078  * License: http://www.tinymce.com/license
       
 31079  * Contributing: http://www.tinymce.com/contributing
       
 31080  */
       
 31081 
       
 31082 /**
       
 31083  * This class is used to create buttons. You can create them directly or through the Factory.
       
 31084  *
       
 31085  * @example
       
 31086  * // Create and render a button to the body element
       
 31087  * tinymce.ui.Factory.create({
       
 31088  *     type: 'button',
       
 31089  *     text: 'My button'
       
 31090  * }).renderTo(document.body);
       
 31091  *
       
 31092  * @-x-less Button.less
       
 31093  * @class tinymce.ui.Button
       
 31094  * @extends tinymce.ui.Widget
       
 31095  */
       
 31096 define("tinymce/ui/Button", [
       
 31097 	"tinymce/ui/Widget"
       
 31098 ], function(Widget) {
       
 31099 	"use strict";
       
 31100 
       
 31101 	return Widget.extend({
       
 31102 		Defaults: {
       
 31103 			classes: "widget btn",
       
 31104 			role: "button"
       
 31105 		},
       
 31106 
       
 31107 		/**
       
 31108 		 * Constructs a new button instance with the specified settings.
       
 31109 		 *
       
 31110 		 * @constructor
       
 31111 		 * @param {Object} settings Name/value object with settings.
       
 31112 		 * @setting {String} size Size of the button small|medium|large.
       
 31113 		 * @setting {String} image Image to use for icon.
       
 31114 		 * @setting {String} icon Icon to use for button.
       
 31115 		 */
       
 31116 		init: function(settings) {
       
 31117 			var self = this, size;
       
 31118 
       
 31119 			self.on('click mousedown', function(e) {
       
 31120 				e.preventDefault();
       
 31121 			});
       
 31122 
       
 31123 			self._super(settings);
       
 31124 			size = settings.size;
       
 31125 
       
 31126 			if (settings.subtype) {
       
 31127 				self.addClass(settings.subtype);
       
 31128 			}
       
 31129 
       
 31130 			if (size) {
       
 31131 				self.addClass('btn-' + size);
       
 31132 			}
       
 31133 		},
       
 31134 
       
 31135 		/**
       
 31136 		 * Sets/gets the current button icon.
       
 31137 		 *
       
 31138 		 * @method icon
       
 31139 		 * @param {String} [icon] New icon identifier.
       
 31140 		 * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
       
 31141 		 */
       
 31142 		icon: function(icon) {
       
 31143 			var self = this, prefix = self.classPrefix;
       
 31144 
       
 31145 			if (typeof icon == 'undefined') {
       
 31146 				return self.settings.icon;
       
 31147 			}
       
 31148 
       
 31149 			self.settings.icon = icon;
       
 31150 			icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
       
 31151 
       
 31152 			if (self._rendered) {
       
 31153 				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
       
 31154 
       
 31155 				if (icon) {
       
 31156 					if (!iconElm || iconElm != btnElm.firstChild) {
       
 31157 						iconElm = document.createElement('i');
       
 31158 						btnElm.insertBefore(iconElm, btnElm.firstChild);
       
 31159 					}
       
 31160 
       
 31161 					iconElm.className = icon;
       
 31162 				} else if (iconElm) {
       
 31163 					btnElm.removeChild(iconElm);
       
 31164 				}
       
 31165 
       
 31166 				self.text(self._text); // Set text again to fix whitespace between icon + text
       
 31167 			}
       
 31168 
       
 31169 			return self;
       
 31170 		},
       
 31171 
       
 31172 		/**
       
 31173 		 * Repaints the button for example after it's been resizes by a layout engine.
       
 31174 		 *
       
 31175 		 * @method repaint
       
 31176 		 */
       
 31177 		repaint: function() {
       
 31178 			var btnStyle = this.getEl().firstChild.style;
       
 31179 
       
 31180 			btnStyle.width = btnStyle.height = "100%";
       
 31181 
       
 31182 			this._super();
       
 31183 		},
       
 31184 
       
 31185 		/**
       
 31186 		 * Sets/gets the current button text.
       
 31187 		 *
       
 31188 		 * @method text
       
 31189 		 * @param {String} [text] New button text.
       
 31190 		 * @return {String|tinymce.ui.Button} Current text or current Button instance.
       
 31191 		 */
       
 31192 		text: function(text) {
       
 31193 			var self = this;
       
 31194 
       
 31195 			if (self._rendered) {
       
 31196 				var textNode = self.getEl().lastChild.lastChild;
       
 31197 				if (textNode) {
       
 31198 					textNode.data = self.translate(text);
       
 31199 				}
       
 31200 			}
       
 31201 
       
 31202 			return self._super(text);
       
 31203 		},
       
 31204 
       
 31205 		/**
       
 31206 		 * Renders the control as a HTML string.
       
 31207 		 *
       
 31208 		 * @method renderHtml
       
 31209 		 * @return {String} HTML representing the control.
       
 31210 		 */
       
 31211 		renderHtml: function() {
       
 31212 			var self = this, id = self._id, prefix = self.classPrefix;
       
 31213 			var icon = self.settings.icon, image;
       
 31214 
       
 31215 			image = self.settings.image;
       
 31216 			if (image) {
       
 31217 				icon = 'none';
       
 31218 
       
 31219 				// Support for [high dpi, low dpi] image sources
       
 31220 				if (typeof image != "string") {
       
 31221 					image = window.getSelection ? image[0] : image[1];
       
 31222 				}
       
 31223 
       
 31224 				image = ' style="background-image: url(\'' + image + '\')"';
       
 31225 			} else {
       
 31226 				image = '';
       
 31227 			}
       
 31228 
       
 31229 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
       
 31230 
       
 31231 			return (
       
 31232 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
       
 31233 					'<button role="presentation" type="button" tabindex="-1">' +
       
 31234 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 31235 						(self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') +
       
 31236 					'</button>' +
       
 31237 				'</div>'
       
 31238 			);
       
 31239 		}
       
 31240 	});
       
 31241 });
       
 31242 
       
 31243 // Included from: js/tinymce/classes/ui/ButtonGroup.js
       
 31244 
       
 31245 /**
       
 31246  * ButtonGroup.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 control enables you to put multiple buttons into a group. This is
       
 31257  * useful when you want to combine similar toolbar buttons into a group.
       
 31258  *
       
 31259  * @example
       
 31260  * // Create and render a buttongroup with two buttons to the body element
       
 31261  * tinymce.ui.Factory.create({
       
 31262  *     type: 'buttongroup',
       
 31263  *     items: [
       
 31264  *         {text: 'Button A'},
       
 31265  *         {text: 'Button B'}
       
 31266  *     ]
       
 31267  * }).renderTo(document.body);
       
 31268  *
       
 31269  * @-x-less ButtonGroup.less
       
 31270  * @class tinymce.ui.ButtonGroup
       
 31271  * @extends tinymce.ui.Container
       
 31272  */
       
 31273 define("tinymce/ui/ButtonGroup", [
       
 31274 	"tinymce/ui/Container"
       
 31275 ], function(Container) {
       
 31276 	"use strict";
       
 31277 
       
 31278 	return Container.extend({
       
 31279 		Defaults: {
       
 31280 			defaultType: 'button',
       
 31281 			role: 'group'
       
 31282 		},
       
 31283 
       
 31284 		/**
       
 31285 		 * Renders the control as a HTML string.
       
 31286 		 *
       
 31287 		 * @method renderHtml
       
 31288 		 * @return {String} HTML representing the control.
       
 31289 		 */
       
 31290 		renderHtml: function() {
       
 31291 			var self = this, layout = self._layout;
       
 31292 
       
 31293 			self.addClass('btn-group');
       
 31294 			self.preRender();
       
 31295 			layout.preRender(self);
       
 31296 
       
 31297 			return (
       
 31298 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
       
 31299 					'<div id="' + self._id + '-body">' +
       
 31300 						(self.settings.html || '') + layout.renderHtml(self) +
       
 31301 					'</div>' +
       
 31302 				'</div>'
       
 31303 			);
       
 31304 		}
       
 31305 	});
       
 31306 });
       
 31307 
       
 31308 // Included from: js/tinymce/classes/ui/Checkbox.js
       
 31309 
       
 31310 /**
       
 31311  * Checkbox.js
       
 31312  *
       
 31313  * Copyright, Moxiecode Systems AB
       
 31314  * Released under LGPL License.
       
 31315  *
       
 31316  * License: http://www.tinymce.com/license
       
 31317  * Contributing: http://www.tinymce.com/contributing
       
 31318  */
       
 31319 
       
 31320 /**
       
 31321  * This control creates a custom checkbox.
       
 31322  *
       
 31323  * @example
       
 31324  * // Create and render a checkbox to the body element
       
 31325  * tinymce.ui.Factory.create({
       
 31326  *     type: 'checkbox',
       
 31327  *     checked: true,
       
 31328  *     text: 'My checkbox'
       
 31329  * }).renderTo(document.body);
       
 31330  *
       
 31331  * @-x-less Checkbox.less
       
 31332  * @class tinymce.ui.Checkbox
       
 31333  * @extends tinymce.ui.Widget
       
 31334  */
       
 31335 define("tinymce/ui/Checkbox", [
       
 31336 	"tinymce/ui/Widget"
       
 31337 ], function(Widget) {
       
 31338 	"use strict";
       
 31339 
       
 31340 	return Widget.extend({
       
 31341 		Defaults: {
       
 31342 			classes: "checkbox",
       
 31343 			role: "checkbox",
       
 31344 			checked: false
       
 31345 		},
       
 31346 
       
 31347 		/**
       
 31348 		 * Constructs a new Checkbox instance with the specified settings.
       
 31349 		 *
       
 31350 		 * @constructor
       
 31351 		 * @param {Object} settings Name/value object with settings.
       
 31352 		 * @setting {Boolean} checked True if the checkbox should be checked by default.
       
 31353 		 */
       
 31354 		init: function(settings) {
       
 31355 			var self = this;
       
 31356 
       
 31357 			self._super(settings);
       
 31358 
       
 31359 			self.on('click mousedown', function(e) {
       
 31360 				e.preventDefault();
       
 31361 			});
       
 31362 
       
 31363 			self.on('click', function(e) {
       
 31364 				e.preventDefault();
       
 31365 
       
 31366 				if (!self.disabled()) {
       
 31367 					self.checked(!self.checked());
       
 31368 				}
       
 31369 			});
       
 31370 
       
 31371 			self.checked(self.settings.checked);
       
 31372 		},
       
 31373 
       
 31374 		/**
       
 31375 		 * Getter/setter function for the checked state.
       
 31376 		 *
       
 31377 		 * @method checked
       
 31378 		 * @param {Boolean} [state] State to be set.
       
 31379 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
       
 31380 		 */
       
 31381 		checked: function(state) {
       
 31382 			var self = this;
       
 31383 
       
 31384 			if (typeof state != "undefined") {
       
 31385 				if (state) {
       
 31386 					self.addClass('checked');
       
 31387 				} else {
       
 31388 					self.removeClass('checked');
       
 31389 				}
       
 31390 
       
 31391 				self._checked = state;
       
 31392 				self.aria('checked', state);
       
 31393 
       
 31394 				return self;
       
 31395 			}
       
 31396 
       
 31397 			return self._checked;
       
 31398 		},
       
 31399 
       
 31400 		/**
       
 31401 		 * Getter/setter function for the value state.
       
 31402 		 *
       
 31403 		 * @method value
       
 31404 		 * @param {Boolean} [state] State to be set.
       
 31405 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
       
 31406 		 */
       
 31407 		value: function(state) {
       
 31408 			return this.checked(state);
       
 31409 		},
       
 31410 
       
 31411 		/**
       
 31412 		 * Renders the control as a HTML string.
       
 31413 		 *
       
 31414 		 * @method renderHtml
       
 31415 		 * @return {String} HTML representing the control.
       
 31416 		 */
       
 31417 		renderHtml: function() {
       
 31418 			var self = this, id = self._id, prefix = self.classPrefix;
       
 31419 
       
 31420 			return (
       
 31421 				'<div id="' + id + '" class="' + self.classes() + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' +
       
 31422 					'<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' +
       
 31423 					'<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' +
       
 31424 				'</div>'
       
 31425 			);
       
 31426 		}
       
 31427 	});
       
 31428 });
       
 31429 
       
 31430 // Included from: js/tinymce/classes/ui/ComboBox.js
       
 31431 
       
 31432 /**
       
 31433  * ComboBox.js
       
 31434  *
       
 31435  * Copyright, Moxiecode Systems AB
       
 31436  * Released under LGPL License.
       
 31437  *
       
 31438  * License: http://www.tinymce.com/license
       
 31439  * Contributing: http://www.tinymce.com/contributing
       
 31440  */
       
 31441 
       
 31442 /**
       
 31443  * This class creates a combobox control. Select box that you select a value from or
       
 31444  * type a value into.
       
 31445  *
       
 31446  * @-x-less ComboBox.less
       
 31447  * @class tinymce.ui.ComboBox
       
 31448  * @extends tinymce.ui.Widget
       
 31449  */
       
 31450 define("tinymce/ui/ComboBox", [
       
 31451 	"tinymce/ui/Widget",
       
 31452 	"tinymce/ui/Factory",
       
 31453 	"tinymce/ui/DomUtils"
       
 31454 ], function(Widget, Factory, DomUtils) {
       
 31455 	"use strict";
       
 31456 
       
 31457 	return Widget.extend({
       
 31458 		/**
       
 31459 		 * Constructs a new control instance with the specified settings.
       
 31460 		 *
       
 31461 		 * @constructor
       
 31462 		 * @param {Object} settings Name/value object with settings.
       
 31463 		 * @setting {String} placeholder Placeholder text to display.
       
 31464 		 */
       
 31465 		init: function(settings) {
       
 31466 			var self = this;
       
 31467 
       
 31468 			self._super(settings);
       
 31469 			self.addClass('combobox');
       
 31470 			self.subinput = true;
       
 31471 			self.ariaTarget = 'inp'; // TODO: Figure out a better way
       
 31472 
       
 31473 			settings = self.settings;
       
 31474 			settings.menu = settings.menu || settings.values;
       
 31475 
       
 31476 			if (settings.menu) {
       
 31477 				settings.icon = 'caret';
       
 31478 			}
       
 31479 
       
 31480 			self.on('click', function(e) {
       
 31481 				var elm = e.target, root = self.getEl();
       
 31482 
       
 31483 				while (elm && elm != root) {
       
 31484 					if (elm.id && elm.id.indexOf('-open') != -1) {
       
 31485 						self.fire('action');
       
 31486 
       
 31487 						if (settings.menu) {
       
 31488 							self.showMenu();
       
 31489 
       
 31490 							if (e.aria) {
       
 31491 								self.menu.items()[0].focus();
       
 31492 							}
       
 31493 						}
       
 31494 					}
       
 31495 
       
 31496 					elm = elm.parentNode;
       
 31497 				}
       
 31498 			});
       
 31499 
       
 31500 			// TODO: Rework this
       
 31501 			self.on('keydown', function(e) {
       
 31502 				if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
       
 31503 					self.parents().reverse().each(function(ctrl) {
       
 31504 						e.preventDefault();
       
 31505 						self.fire('change');
       
 31506 
       
 31507 						if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
       
 31508 							ctrl.fire('submit', {data: ctrl.toJSON()});
       
 31509 							return false;
       
 31510 						}
       
 31511 					});
       
 31512 				}
       
 31513 			});
       
 31514 
       
 31515 			if (settings.placeholder) {
       
 31516 				self.addClass('placeholder');
       
 31517 
       
 31518 				self.on('focusin', function() {
       
 31519 					if (!self._hasOnChange) {
       
 31520 						DomUtils.on(self.getEl('inp'), 'change', function() {
       
 31521 							self.fire('change');
       
 31522 						});
       
 31523 
       
 31524 						self._hasOnChange = true;
       
 31525 					}
       
 31526 
       
 31527 					if (self.hasClass('placeholder')) {
       
 31528 						self.getEl('inp').value = '';
       
 31529 						self.removeClass('placeholder');
       
 31530 					}
       
 31531 				});
       
 31532 
       
 31533 				self.on('focusout', function() {
       
 31534 					if (self.value().length === 0) {
       
 31535 						self.getEl('inp').value = settings.placeholder;
       
 31536 						self.addClass('placeholder');
       
 31537 					}
       
 31538 				});
       
 31539 			}
       
 31540 		},
       
 31541 
       
 31542 		showMenu: function() {
       
 31543 			var self = this, settings = self.settings, menu;
       
 31544 
       
 31545 			if (!self.menu) {
       
 31546 				menu = settings.menu || [];
       
 31547 
       
 31548 				// Is menu array then auto constuct menu control
       
 31549 				if (menu.length) {
       
 31550 					menu = {
       
 31551 						type: 'menu',
       
 31552 						items: menu
       
 31553 					};
       
 31554 				} else {
       
 31555 					menu.type = menu.type || 'menu';
       
 31556 				}
       
 31557 
       
 31558 				self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
       
 31559 				self.fire('createmenu');
       
 31560 				self.menu.reflow();
       
 31561 				self.menu.on('cancel', function(e) {
       
 31562 					if (e.control === self.menu) {
       
 31563 						self.focus();
       
 31564 					}
       
 31565 				});
       
 31566 
       
 31567 				self.menu.on('show hide', function(e) {
       
 31568 					e.control.items().each(function(ctrl) {
       
 31569 						ctrl.active(ctrl.value() == self.value());
       
 31570 					});
       
 31571 				}).fire('show');
       
 31572 
       
 31573 				self.menu.on('select', function(e) {
       
 31574 					self.value(e.control.value());
       
 31575 				});
       
 31576 
       
 31577 				self.on('focusin', function(e) {
       
 31578 					if (e.target.tagName.toUpperCase() == 'INPUT') {
       
 31579 						self.menu.hide();
       
 31580 					}
       
 31581 				});
       
 31582 
       
 31583 				self.aria('expanded', true);
       
 31584 			}
       
 31585 
       
 31586 			self.menu.show();
       
 31587 			self.menu.layoutRect({w: self.layoutRect().w});
       
 31588 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
       
 31589 		},
       
 31590 
       
 31591 		/**
       
 31592 		 * Getter/setter function for the control value.
       
 31593 		 *
       
 31594 		 * @method value
       
 31595 		 * @param {String} [value] Value to be set.
       
 31596 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
       
 31597 		 */
       
 31598 		value: function(value) {
       
 31599 			var self = this;
       
 31600 
       
 31601 			if (typeof value != "undefined") {
       
 31602 				self._value = value;
       
 31603 				self.removeClass('placeholder');
       
 31604 
       
 31605 				if (self._rendered) {
       
 31606 					self.getEl('inp').value = value;
       
 31607 				}
       
 31608 
       
 31609 				return self;
       
 31610 			}
       
 31611 
       
 31612 			if (self._rendered) {
       
 31613 				value = self.getEl('inp').value;
       
 31614 
       
 31615 				if (value != self.settings.placeholder) {
       
 31616 					return value;
       
 31617 				}
       
 31618 
       
 31619 				return '';
       
 31620 			}
       
 31621 
       
 31622 			return self._value;
       
 31623 		},
       
 31624 
       
 31625 		/**
       
 31626 		 * Getter/setter function for the disabled state.
       
 31627 		 *
       
 31628 		 * @method value
       
 31629 		 * @param {Boolean} [state] State to be set.
       
 31630 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
       
 31631 		 */
       
 31632 		disabled: function(state) {
       
 31633 			var self = this;
       
 31634 
       
 31635 			if (self._rendered && typeof state != 'undefined') {
       
 31636 				self.getEl('inp').disabled = state;
       
 31637 			}
       
 31638 
       
 31639 			return self._super(state);
       
 31640 		},
       
 31641 
       
 31642 		/**
       
 31643 		 * Focuses the input area of the control.
       
 31644 		 *
       
 31645 		 * @method focus
       
 31646 		 */
       
 31647 		focus: function() {
       
 31648 			this.getEl('inp').focus();
       
 31649 		},
       
 31650 
       
 31651 		/**
       
 31652 		 * Repaints the control after a layout operation.
       
 31653 		 *
       
 31654 		 * @method repaint
       
 31655 		 */
       
 31656 		repaint: function() {
       
 31657 			var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
       
 31658 			var width, lineHeight;
       
 31659 
       
 31660 			if (openElm) {
       
 31661 				width = rect.w - DomUtils.getSize(openElm).width - 10;
       
 31662 			} else {
       
 31663 				width = rect.w - 10;
       
 31664 			}
       
 31665 
       
 31666 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
       
 31667 			var doc = document;
       
 31668 			if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
       
 31669 				lineHeight = (self.layoutRect().h - 2) + 'px';
       
 31670 			}
       
 31671 
       
 31672 			DomUtils.css(elm.firstChild, {
       
 31673 				width: width,
       
 31674 				lineHeight: lineHeight
       
 31675 			});
       
 31676 
       
 31677 			self._super();
       
 31678 
       
 31679 			return self;
       
 31680 		},
       
 31681 
       
 31682 		/**
       
 31683 		 * Post render method. Called after the control has been rendered to the target.
       
 31684 		 *
       
 31685 		 * @method postRender
       
 31686 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
       
 31687 		 */
       
 31688 		postRender: function() {
       
 31689 			var self = this;
       
 31690 
       
 31691 			DomUtils.on(this.getEl('inp'), 'change', function() {
       
 31692 				self.fire('change');
       
 31693 			});
       
 31694 
       
 31695 			return self._super();
       
 31696 		},
       
 31697 
       
 31698 		remove: function() {
       
 31699 			DomUtils.off(this.getEl('inp'));
       
 31700 			this._super();
       
 31701 		},
       
 31702 
       
 31703 		/**
       
 31704 		 * Renders the control as a HTML string.
       
 31705 		 *
       
 31706 		 * @method renderHtml
       
 31707 		 * @return {String} HTML representing the control.
       
 31708 		 */
       
 31709 		renderHtml: function() {
       
 31710 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
       
 31711 			var value = settings.value || settings.placeholder || '';
       
 31712 			var icon, text, openBtnHtml = '', extraAttrs = '';
       
 31713 
       
 31714 			if ("spellcheck" in settings) {
       
 31715 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
       
 31716 			}
       
 31717 
       
 31718 			if (settings.maxLength) {
       
 31719 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
       
 31720 			}
       
 31721 
       
 31722 			if (settings.size) {
       
 31723 				extraAttrs += ' size="' + settings.size + '"';
       
 31724 			}
       
 31725 
       
 31726 			if (settings.subtype) {
       
 31727 				extraAttrs += ' type="' + settings.subtype + '"';
       
 31728 			}
       
 31729 
       
 31730 			if (self.disabled()) {
       
 31731 				extraAttrs += ' disabled="disabled"';
       
 31732 			}
       
 31733 
       
 31734 			icon = settings.icon;
       
 31735 			if (icon && icon != 'caret') {
       
 31736 				icon = prefix + 'ico ' + prefix + 'i-' + settings.icon;
       
 31737 			}
       
 31738 
       
 31739 			text = self._text;
       
 31740 
       
 31741 			if (icon || text) {
       
 31742 				openBtnHtml = (
       
 31743 					'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' +
       
 31744 						'<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' +
       
 31745 							(icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') +
       
 31746 							(text ? (icon ? ' ' : '') + text : '') +
       
 31747 						'</button>' +
       
 31748 					'</div>'
       
 31749 				);
       
 31750 
       
 31751 				self.addClass('has-open');
       
 31752 			}
       
 31753 
       
 31754 			return (
       
 31755 				'<div id="' + id + '" class="' + self.classes() + '">' +
       
 31756 					'<input id="' + id + '-inp" class="' + prefix + 'textbox ' + prefix + 'placeholder" value="' +
       
 31757 					value + '" hidefocus="1"' + extraAttrs + ' />' +
       
 31758 					openBtnHtml +
       
 31759 				'</div>'
       
 31760 			);
       
 31761 		}
       
 31762 	});
       
 31763 });
       
 31764 
       
 31765 // Included from: js/tinymce/classes/ui/ColorBox.js
       
 31766 
       
 31767 /**
       
 31768  * ColorBox.js
       
 31769  *
       
 31770  * Copyright, Moxiecode Systems AB
       
 31771  * Released under LGPL License.
       
 31772  *
       
 31773  * License: http://www.tinymce.com/license
       
 31774  * Contributing: http://www.tinymce.com/contributing
       
 31775  */
       
 31776 
       
 31777 /**
       
 31778  * This widget lets you enter colors and browse for colors by pressing the color button. It also displays
       
 31779  * a preview of the current color.
       
 31780  *
       
 31781  * @-x-less ColorBox.less
       
 31782  * @class tinymce.ui.ColorBox
       
 31783  * @extends tinymce.ui.ComboBox
       
 31784  */
       
 31785 define("tinymce/ui/ColorBox", [
       
 31786 	"tinymce/ui/ComboBox"
       
 31787 ], function(ComboBox) {
       
 31788 	"use strict";
       
 31789 
       
 31790 	return ComboBox.extend({
       
 31791 		/**
       
 31792 		 * Constructs a new control instance with the specified settings.
       
 31793 		 *
       
 31794 		 * @constructor
       
 31795 		 * @param {Object} settings Name/value object with settings.
       
 31796 		 */
       
 31797 		init: function(settings) {
       
 31798 			var self = this;
       
 31799 
       
 31800 			settings.spellcheck = false;
       
 31801 
       
 31802 			if (settings.onaction) {
       
 31803 				settings.icon = 'none';
       
 31804 			}
       
 31805 
       
 31806 			self._super(settings);
       
 31807 
       
 31808 			self.addClass('colorbox');
       
 31809 			self.on('change keyup postrender', function() {
       
 31810 				self.repaintColor(self.value());
       
 31811 			});
       
 31812 		},
       
 31813 
       
 31814 		repaintColor: function(value) {
       
 31815 			var elm = this.getEl().getElementsByTagName('i')[0];
       
 31816 
       
 31817 			if (elm) {
       
 31818 				try {
       
 31819 					elm.style.background = value;
       
 31820 				} catch (ex) {
       
 31821 					// Ignore
       
 31822 				}
       
 31823 			}
       
 31824 		},
       
 31825 
       
 31826 		value: function(value) {
       
 31827 			var self = this;
       
 31828 
       
 31829 			if (typeof value != "undefined") {
       
 31830 				if (self._rendered) {
       
 31831 					self.repaintColor(value);
       
 31832 				}
       
 31833 			}
       
 31834 
       
 31835 			return self._super(value);
       
 31836 		}
       
 31837 	});
       
 31838 });
       
 31839 
       
 31840 // Included from: js/tinymce/classes/ui/PanelButton.js
       
 31841 
       
 31842 /**
       
 31843  * PanelButton.js
       
 31844  *
       
 31845  * Copyright, Moxiecode Systems AB
       
 31846  * Released under LGPL License.
       
 31847  *
       
 31848  * License: http://www.tinymce.com/license
       
 31849  * Contributing: http://www.tinymce.com/contributing
       
 31850  */
       
 31851 
       
 31852 /**
       
 31853  * Creates a new panel button.
       
 31854  *
       
 31855  * @class tinymce.ui.PanelButton
       
 31856  * @extends tinymce.ui.Button
       
 31857  */
       
 31858 define("tinymce/ui/PanelButton", [
       
 31859 	"tinymce/ui/Button",
       
 31860 	"tinymce/ui/FloatPanel"
       
 31861 ], function(Button, FloatPanel) {
       
 31862 	"use strict";
       
 31863 
       
 31864 	return Button.extend({
       
 31865 		/**
       
 31866 		 * Shows the panel for the button.
       
 31867 		 *
       
 31868 		 * @method showPanel
       
 31869 		 */
       
 31870 		showPanel: function() {
       
 31871 			var self = this, settings = self.settings;
       
 31872 
       
 31873 			self.active(true);
       
 31874 
       
 31875 			if (!self.panel) {
       
 31876 				var panelSettings = settings.panel;
       
 31877 
       
 31878 				// Wrap panel in grid layout if type if specified
       
 31879 				// This makes it possible to add forms or other containers directly in the panel option
       
 31880 				if (panelSettings.type) {
       
 31881 					panelSettings = {
       
 31882 						layout: 'grid',
       
 31883 						items: panelSettings
       
 31884 					};
       
 31885 				}
       
 31886 
       
 31887 				panelSettings.role = panelSettings.role || 'dialog';
       
 31888 				panelSettings.popover = true;
       
 31889 				panelSettings.autohide = true;
       
 31890 				panelSettings.ariaRoot = true;
       
 31891 
       
 31892 				self.panel = new FloatPanel(panelSettings).on('hide', function() {
       
 31893 					self.active(false);
       
 31894 				}).on('cancel', function(e) {
       
 31895 					e.stopPropagation();
       
 31896 					self.focus();
       
 31897 					self.hidePanel();
       
 31898 				}).parent(self).renderTo(self.getContainerElm());
       
 31899 
       
 31900 				self.panel.fire('show');
       
 31901 				self.panel.reflow();
       
 31902 			} else {
       
 31903 				self.panel.show();
       
 31904 			}
       
 31905 
       
 31906 			self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc']));
       
 31907 		},
       
 31908 
       
 31909 		/**
       
 31910 		 * Hides the panel for the button.
       
 31911 		 *
       
 31912 		 * @method hidePanel
       
 31913 		 */
       
 31914 		hidePanel: function() {
       
 31915 			var self = this;
       
 31916 
       
 31917 			if (self.panel) {
       
 31918 				self.panel.hide();
       
 31919 			}
       
 31920 		},
       
 31921 
       
 31922 		/**
       
 31923 		 * Called after the control has been rendered.
       
 31924 		 *
       
 31925 		 * @method postRender
       
 31926 		 */
       
 31927 		postRender: function() {
       
 31928 			var self = this;
       
 31929 
       
 31930 			self.aria('haspopup', true);
       
 31931 
       
 31932 			self.on('click', function(e) {
       
 31933 				if (e.control === self) {
       
 31934 					if (self.panel && self.panel.visible()) {
       
 31935 						self.hidePanel();
       
 31936 					} else {
       
 31937 						self.showPanel();
       
 31938 						self.panel.focus(!!e.aria);
       
 31939 					}
       
 31940 				}
       
 31941 			});
       
 31942 
       
 31943 			return self._super();
       
 31944 		},
       
 31945 
       
 31946 		remove: function() {
       
 31947 			if (this.panel) {
       
 31948 				this.panel.remove();
       
 31949 				this.panel = null;
       
 31950 			}
       
 31951 
       
 31952 			return this._super();
       
 31953 		}
       
 31954 	});
       
 31955 });
       
 31956 
       
 31957 // Included from: js/tinymce/classes/ui/ColorButton.js
       
 31958 
       
 31959 /**
       
 31960  * ColorButton.js
       
 31961  *
       
 31962  * Copyright, Moxiecode Systems AB
       
 31963  * Released under LGPL License.
       
 31964  *
       
 31965  * License: http://www.tinymce.com/license
       
 31966  * Contributing: http://www.tinymce.com/contributing
       
 31967  */
       
 31968 
       
 31969 /**
       
 31970  * This class creates a color button control. This is a split button in which the main
       
 31971  * button has a visual representation of the currently selected color. When clicked
       
 31972  * the caret button displays a color picker, allowing the user to select a new color.
       
 31973  *
       
 31974  * @-x-less ColorButton.less
       
 31975  * @class tinymce.ui.ColorButton
       
 31976  * @extends tinymce.ui.PanelButton
       
 31977  */
       
 31978 define("tinymce/ui/ColorButton", [
       
 31979 	"tinymce/ui/PanelButton",
       
 31980 	"tinymce/dom/DOMUtils"
       
 31981 ], function(PanelButton, DomUtils) {
       
 31982 	"use strict";
       
 31983 
       
 31984 	var DOM = DomUtils.DOM;
       
 31985 
       
 31986 	return PanelButton.extend({
       
 31987 		/**
       
 31988 		 * Constructs a new ColorButton instance with the specified settings.
       
 31989 		 *
       
 31990 		 * @constructor
       
 31991 		 * @param {Object} settings Name/value object with settings.
       
 31992 		 */
       
 31993 		init: function(settings) {
       
 31994 			this._super(settings);
       
 31995 			this.addClass('colorbutton');
       
 31996 		},
       
 31997 
       
 31998 		/**
       
 31999 		 * Getter/setter for the current color.
       
 32000 		 *
       
 32001 		 * @method color
       
 32002 		 * @param {String} [color] Color to set.
       
 32003 		 * @return {String|tinymce.ui.ColorButton} Current color or current instance.
       
 32004 		 */
       
 32005 		color: function(color) {
       
 32006 			if (color) {
       
 32007 				this._color = color;
       
 32008 				this.getEl('preview').style.backgroundColor = color;
       
 32009 				return this;
       
 32010 			}
       
 32011 
       
 32012 			return this._color;
       
 32013 		},
       
 32014 
       
 32015 		/**
       
 32016 		 * Resets the current color.
       
 32017 		 *
       
 32018 		 * @method resetColor
       
 32019 		 * @return {tinymce.ui.ColorButton} Current instance.
       
 32020 		 */
       
 32021 		resetColor: function() {
       
 32022 			this._color = null;
       
 32023 			this.getEl('preview').style.backgroundColor = null;
       
 32024 			return this;
       
 32025 		},
       
 32026 
       
 32027 		/**
       
 32028 		 * Renders the control as a HTML string.
       
 32029 		 *
       
 32030 		 * @method renderHtml
       
 32031 		 * @return {String} HTML representing the control.
       
 32032 		 */
       
 32033 		renderHtml: function() {
       
 32034 			var self = this, id = self._id, prefix = self.classPrefix;
       
 32035 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
       
 32036 			var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '';
       
 32037 
       
 32038 			return (
       
 32039 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1" aria-haspopup="true">' +
       
 32040 					'<button role="presentation" hidefocus="1" type="button" tabindex="-1">' +
       
 32041 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 32042 						'<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' +
       
 32043 						(self._text ? (icon ? ' ' : '') + (self._text) : '') +
       
 32044 					'</button>' +
       
 32045 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
       
 32046 						' <i class="' + prefix + 'caret"></i>' +
       
 32047 					'</button>' +
       
 32048 				'</div>'
       
 32049 			);
       
 32050 		},
       
 32051 
       
 32052 		/**
       
 32053 		 * Called after the control has been rendered.
       
 32054 		 *
       
 32055 		 * @method postRender
       
 32056 		 */
       
 32057 		postRender: function() {
       
 32058 			var self = this, onClickHandler = self.settings.onclick;
       
 32059 
       
 32060 			self.on('click', function(e) {
       
 32061 				if (e.aria && e.aria.key == 'down') {
       
 32062 					return;
       
 32063 				}
       
 32064 
       
 32065 				if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) {
       
 32066 					e.stopImmediatePropagation();
       
 32067 					onClickHandler.call(self, e);
       
 32068 				}
       
 32069 			});
       
 32070 
       
 32071 			delete self.settings.onclick;
       
 32072 
       
 32073 			return self._super();
       
 32074 		}
       
 32075 	});
       
 32076 });
       
 32077 
       
 32078 // Included from: js/tinymce/classes/util/Color.js
       
 32079 
       
 32080 /**
       
 32081  * Color.js
       
 32082  *
       
 32083  * Copyright, Moxiecode Systems AB
       
 32084  * Released under LGPL License.
       
 32085  *
       
 32086  * License: http://www.tinymce.com/license
       
 32087  * Contributing: http://www.tinymce.com/contributing
       
 32088  */
       
 32089 
       
 32090 /**
       
 32091  * This class lets you parse/serialize colors and convert rgb/hsb.
       
 32092  *
       
 32093  * @class tinymce.util.Color
       
 32094  * @example
       
 32095  * var white = new tinymce.util.Color({r: 255, g: 255, b: 255});
       
 32096  * var red = new tinymce.util.Color('#FF0000');
       
 32097  *
       
 32098  * console.log(white.toHex(), red.toHsv());
       
 32099  */
       
 32100 define("tinymce/util/Color", [], function() {
       
 32101 	var min = Math.min, max = Math.max, round = Math.round;
       
 32102 
       
 32103 	/**
       
 32104 	 * Constructs a new color instance.
       
 32105 	 *
       
 32106 	 * @constructor
       
 32107 	 * @method Color
       
 32108 	 * @param {String} value Optional initial value to parse.
       
 32109 	 */
       
 32110 	function Color(value) {
       
 32111 		var self = this, r = 0, g = 0, b = 0;
       
 32112 
       
 32113 		function rgb2hsv(r, g, b) {
       
 32114 			var h, s, v, d, minRGB, maxRGB;
       
 32115 
       
 32116 			h = 0;
       
 32117 			s = 0;
       
 32118 			v = 0;
       
 32119 			r = r / 255;
       
 32120 			g = g / 255;
       
 32121 			b = b / 255;
       
 32122 
       
 32123 			minRGB = min(r, min(g, b));
       
 32124 			maxRGB = max(r, max(g, b));
       
 32125 
       
 32126 			if (minRGB == maxRGB) {
       
 32127 				v = minRGB;
       
 32128 
       
 32129 				return {
       
 32130 					h: 0,
       
 32131 					s: 0,
       
 32132 					v: v * 100
       
 32133 				};
       
 32134 			}
       
 32135 
       
 32136 			/*eslint no-nested-ternary:0 */
       
 32137 			d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r);
       
 32138 			h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5);
       
 32139 			h = 60 * (h - d / (maxRGB - minRGB));
       
 32140 			s = (maxRGB - minRGB) / maxRGB;
       
 32141 			v = maxRGB;
       
 32142 
       
 32143 			return {
       
 32144 				h: round(h),
       
 32145 				s: round(s * 100),
       
 32146 				v: round(v * 100)
       
 32147 			};
       
 32148 		}
       
 32149 
       
 32150 		function hsvToRgb(hue, saturation, brightness) {
       
 32151 			var side, chroma, x, match;
       
 32152 
       
 32153 			hue = (parseInt(hue, 10) || 0) % 360;
       
 32154 			saturation = parseInt(saturation, 10) / 100;
       
 32155 			brightness = parseInt(brightness, 10) / 100;
       
 32156 			saturation = max(0, min(saturation, 1));
       
 32157 			brightness = max(0, min(brightness, 1));
       
 32158 
       
 32159 			if (saturation === 0) {
       
 32160 				r = g = b = round(255 * brightness);
       
 32161 				return;
       
 32162 			}
       
 32163 
       
 32164 			side = hue / 60;
       
 32165 			chroma = brightness * saturation;
       
 32166 			x = chroma * (1 - Math.abs(side % 2 - 1));
       
 32167 			match = brightness - chroma;
       
 32168 
       
 32169 			switch (Math.floor(side)) {
       
 32170 				case 0:
       
 32171 					r = chroma;
       
 32172 					g = x;
       
 32173 					b = 0;
       
 32174 					break;
       
 32175 
       
 32176 				case 1:
       
 32177 					r = x;
       
 32178 					g = chroma;
       
 32179 					b = 0;
       
 32180 					break;
       
 32181 
       
 32182 				case 2:
       
 32183 					r = 0;
       
 32184 					g = chroma;
       
 32185 					b = x;
       
 32186 					break;
       
 32187 
       
 32188 				case 3:
       
 32189 					r = 0;
       
 32190 					g = x;
       
 32191 					b = chroma;
       
 32192 					break;
       
 32193 
       
 32194 				case 4:
       
 32195 					r = x;
       
 32196 					g = 0;
       
 32197 					b = chroma;
       
 32198 					break;
       
 32199 
       
 32200 				case 5:
       
 32201 					r = chroma;
       
 32202 					g = 0;
       
 32203 					b = x;
       
 32204 					break;
       
 32205 
       
 32206 				default:
       
 32207 					r = g = b = 0;
       
 32208 			}
       
 32209 
       
 32210 			r = round(255 * (r + match));
       
 32211 			g = round(255 * (g + match));
       
 32212 			b = round(255 * (b + match));
       
 32213 		}
       
 32214 
       
 32215 		/**
       
 32216 		 * Returns the hex string of the current color. For example: #ff00ff
       
 32217 		 *
       
 32218 		 * @method toHex
       
 32219 		 * @return {String} Hex string of current color.
       
 32220 		 */
       
 32221 		function toHex() {
       
 32222 			function hex(val) {
       
 32223 				val = parseInt(val, 10).toString(16);
       
 32224 
       
 32225 				return val.length > 1 ? val : '0' + val;
       
 32226 			}
       
 32227 
       
 32228 			return '#' + hex(r) + hex(g) + hex(b);
       
 32229 		}
       
 32230 
       
 32231 		/**
       
 32232 		 * Returns the r, g, b values of the color. Each channel has a range from 0-255.
       
 32233 		 *
       
 32234 		 * @method toRgb
       
 32235 		 * @return {Object} Object with r, g, b fields.
       
 32236 		 */
       
 32237 		function toRgb() {
       
 32238 			return {
       
 32239 				r: r,
       
 32240 				g: g,
       
 32241 				b: b
       
 32242 			};
       
 32243 		}
       
 32244 
       
 32245 		/**
       
 32246 		 * Returns the h, s, v values of the color. Ranges: h=0-360, s=0-100, v=0-100.
       
 32247 		 *
       
 32248 		 * @method toHsv
       
 32249 		 * @return {Object} Object with h, s, v fields.
       
 32250 		 */
       
 32251 		function toHsv() {
       
 32252 			return rgb2hsv(r, g, b);
       
 32253 		}
       
 32254 
       
 32255 		/**
       
 32256 		 * Parses the specified value and populates the color instance.
       
 32257 		 *
       
 32258 		 * Supported format examples:
       
 32259 		 *  * rbg(255,0,0)
       
 32260 		 *  * #ff0000
       
 32261 		 *  * #fff
       
 32262 		 *  * {r: 255, g: 0, b: 0}
       
 32263 		 *  * {h: 360, s: 100, v: 100}
       
 32264 		 *
       
 32265 		 * @method parse
       
 32266 		 * @param {Object/String} value Color value to parse.
       
 32267 		 * @return {tinymce.util.Color} Current color instance.
       
 32268 		 */
       
 32269 		function parse(value) {
       
 32270 			var matches;
       
 32271 
       
 32272 			if (typeof value == 'object') {
       
 32273 				if ("r" in value) {
       
 32274 					r = value.r;
       
 32275 					g = value.g;
       
 32276 					b = value.b;
       
 32277 				} else if ("v" in value) {
       
 32278 					hsvToRgb(value.h, value.s, value.v);
       
 32279 				}
       
 32280 			} else {
       
 32281 				if ((matches = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(value))) {
       
 32282 					r = parseInt(matches[1], 10);
       
 32283 					g = parseInt(matches[2], 10);
       
 32284 					b = parseInt(matches[3], 10);
       
 32285 				} else if ((matches = /#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(value))) {
       
 32286 					r = parseInt(matches[1], 16);
       
 32287 					g = parseInt(matches[2], 16);
       
 32288 					b = parseInt(matches[3], 16);
       
 32289 				} else if ((matches = /#([0-F])([0-F])([0-F])/gi.exec(value))) {
       
 32290 					r = parseInt(matches[1] + matches[1], 16);
       
 32291 					g = parseInt(matches[2] + matches[2], 16);
       
 32292 					b = parseInt(matches[3] + matches[3], 16);
       
 32293 				}
       
 32294 			}
       
 32295 
       
 32296 			r = r < 0 ? 0 : (r > 255 ? 255 : r);
       
 32297 			g = g < 0 ? 0 : (g > 255 ? 255 : g);
       
 32298 			b = b < 0 ? 0 : (b > 255 ? 255 : b);
       
 32299 
       
 32300 			return self;
       
 32301 		}
       
 32302 
       
 32303 		if (value) {
       
 32304 			parse(value);
       
 32305 		}
       
 32306 
       
 32307 		self.toRgb = toRgb;
       
 32308 		self.toHsv = toHsv;
       
 32309 		self.toHex = toHex;
       
 32310 		self.parse = parse;
       
 32311 	}
       
 32312 
       
 32313 	return Color;
       
 32314 });
       
 32315 
       
 32316 // Included from: js/tinymce/classes/ui/ColorPicker.js
       
 32317 
       
 32318 /**
       
 32319  * ColorPicker.js
       
 32320  *
       
 32321  * Copyright, Moxiecode Systems AB
       
 32322  * Released under LGPL License.
       
 32323  *
       
 32324  * License: http://www.tinymce.com/license
       
 32325  * Contributing: http://www.tinymce.com/contributing
       
 32326  */
       
 32327 
       
 32328 /**
       
 32329  * Color picker widget lets you select colors.
       
 32330  *
       
 32331  * @-x-less ColorPicker.less
       
 32332  * @class tinymce.ui.ColorPicker
       
 32333  * @extends tinymce.ui.Widget
       
 32334  */
       
 32335 define("tinymce/ui/ColorPicker", [
       
 32336 	"tinymce/ui/Widget",
       
 32337 	"tinymce/ui/DragHelper",
       
 32338 	"tinymce/ui/DomUtils",
       
 32339 	"tinymce/util/Color"
       
 32340 ], function(Widget, DragHelper, DomUtils, Color) {
       
 32341 	"use strict";
       
 32342 
       
 32343 	return Widget.extend({
       
 32344 		Defaults: {
       
 32345 			classes: "widget colorpicker"
       
 32346 		},
       
 32347 
       
 32348 		/**
       
 32349 		 * Constructs a new colorpicker instance with the specified settings.
       
 32350 		 *
       
 32351 		 * @constructor
       
 32352 		 * @param {Object} settings Name/value object with settings.
       
 32353 		 * @setting {String} color Initial color value.
       
 32354 		 */
       
 32355 		init: function(settings) {
       
 32356 			this._super(settings);
       
 32357 		},
       
 32358 
       
 32359 		postRender: function() {
       
 32360 			var self = this, color = self.color(), hsv, hueRootElm, huePointElm, svRootElm, svPointElm;
       
 32361 
       
 32362 			hueRootElm = self.getEl('h');
       
 32363 			huePointElm = self.getEl('hp');
       
 32364 			svRootElm = self.getEl('sv');
       
 32365 			svPointElm = self.getEl('svp');
       
 32366 
       
 32367 			function getPos(elm, event) {
       
 32368 				var pos = DomUtils.getPos(elm), x, y;
       
 32369 
       
 32370 				x = event.pageX - pos.x;
       
 32371 				y = event.pageY - pos.y;
       
 32372 
       
 32373 				x = Math.max(0, Math.min(x / elm.clientWidth, 1));
       
 32374 				y = Math.max(0, Math.min(y / elm.clientHeight, 1));
       
 32375 
       
 32376 				return {
       
 32377 					x: x,
       
 32378 					y: y
       
 32379 				};
       
 32380 			}
       
 32381 
       
 32382 			function updateColor(hsv, hueUpdate) {
       
 32383 				var hue = (360 - hsv.h) / 360;
       
 32384 
       
 32385 				DomUtils.css(huePointElm, {
       
 32386 					top: (hue * 100) + '%'
       
 32387 				});
       
 32388 
       
 32389 				if (!hueUpdate) {
       
 32390 					DomUtils.css(svPointElm, {
       
 32391 						left: hsv.s + '%',
       
 32392 						top: (100 - hsv.v) + '%'
       
 32393 					});
       
 32394 				}
       
 32395 
       
 32396 				svRootElm.style.background = new Color({s: 100, v: 100, h: hsv.h}).toHex();
       
 32397 				self.color().parse({s: hsv.s, v: hsv.v, h: hsv.h});
       
 32398 			}
       
 32399 
       
 32400 			function updateSaturationAndValue(e) {
       
 32401 				var pos;
       
 32402 
       
 32403 				pos = getPos(svRootElm, e);
       
 32404 				hsv.s = pos.x * 100;
       
 32405 				hsv.v = (1 - pos.y) * 100;
       
 32406 
       
 32407 				updateColor(hsv);
       
 32408 				self.fire('change');
       
 32409 			}
       
 32410 
       
 32411 			function updateHue(e) {
       
 32412 				var pos;
       
 32413 
       
 32414 				pos = getPos(hueRootElm, e);
       
 32415 				hsv = color.toHsv();
       
 32416 				hsv.h = (1 - pos.y) * 360;
       
 32417 				updateColor(hsv, true);
       
 32418 				self.fire('change');
       
 32419 			}
       
 32420 
       
 32421 			self._repaint = function() {
       
 32422 				hsv = color.toHsv();
       
 32423 				updateColor(hsv);
       
 32424 			};
       
 32425 
       
 32426 			self._super();
       
 32427 
       
 32428 			self._svdraghelper = new DragHelper(self._id + '-sv', {
       
 32429 				start: updateSaturationAndValue,
       
 32430 				drag: updateSaturationAndValue
       
 32431 			});
       
 32432 
       
 32433 			self._hdraghelper = new DragHelper(self._id + '-h', {
       
 32434 				start: updateHue,
       
 32435 				drag: updateHue
       
 32436 			});
       
 32437 
       
 32438 			self._repaint();
       
 32439 		},
       
 32440 
       
 32441 		rgb: function() {
       
 32442 			return this.color().toRgb();
       
 32443 		},
       
 32444 
       
 32445 		value: function(value) {
       
 32446 			var self = this;
       
 32447 
       
 32448 			if (arguments.length) {
       
 32449 				self.color().parse(value);
       
 32450 
       
 32451 				if (self._rendered) {
       
 32452 					self._repaint();
       
 32453 				}
       
 32454 			} else {
       
 32455 				return self.color().toHex();
       
 32456 			}
       
 32457 		},
       
 32458 
       
 32459 		color: function() {
       
 32460 			if (!this._color) {
       
 32461 				this._color = new Color();
       
 32462 			}
       
 32463 
       
 32464 			return this._color;
       
 32465 		},
       
 32466 
       
 32467 		/**
       
 32468 		 * Renders the control as a HTML string.
       
 32469 		 *
       
 32470 		 * @method renderHtml
       
 32471 		 * @return {String} HTML representing the control.
       
 32472 		 */
       
 32473 		renderHtml: function() {
       
 32474 			var self = this, id = self._id, prefix = self.classPrefix, hueHtml;
       
 32475 			var stops = '#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000';
       
 32476 
       
 32477 			function getOldIeFallbackHtml() {
       
 32478 				var i, l, html = '', gradientPrefix, stopsList;
       
 32479 
       
 32480 				gradientPrefix = 'filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=';
       
 32481 				stopsList = stops.split(',');
       
 32482 				for (i = 0, l = stopsList.length - 1; i < l; i++) {
       
 32483 					html += (
       
 32484 						'<div class="' + prefix + 'colorpicker-h-chunk" style="' +
       
 32485 							'height:' + (100 / l) + '%;' +
       
 32486 							gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ');' +
       
 32487 							'-ms-' + gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ')' +
       
 32488 						'"></div>'
       
 32489 					);
       
 32490 				}
       
 32491 
       
 32492 				return html;
       
 32493 			}
       
 32494 
       
 32495 			var gradientCssText = (
       
 32496 				'background: -ms-linear-gradient(top,' + stops + ');' +
       
 32497 				'background: linear-gradient(to bottom,' + stops + ');'
       
 32498 			);
       
 32499 
       
 32500 			hueHtml = (
       
 32501 				'<div id="' + id + '-h" class="' + prefix + 'colorpicker-h" style="' + gradientCssText + '">' +
       
 32502 					getOldIeFallbackHtml() +
       
 32503 					'<div id="' + id + '-hp" class="' + prefix + 'colorpicker-h-marker"></div>' +
       
 32504 				'</div>'
       
 32505 			);
       
 32506 
       
 32507 			return (
       
 32508 				'<div id="' + id + '" class="' + self.classes() + '">' +
       
 32509 					'<div id="' + id + '-sv" class="' + prefix + 'colorpicker-sv">' +
       
 32510 						'<div class="' + prefix + 'colorpicker-overlay1">' +
       
 32511 							'<div class="' + prefix + 'colorpicker-overlay2">' +
       
 32512 								'<div id="' + id + '-svp" class="' + prefix + 'colorpicker-selector1">' +
       
 32513 									'<div class="' + prefix + 'colorpicker-selector2"></div>' +
       
 32514 								'</div>' +
       
 32515 							'</div>' +
       
 32516 						'</div>' +
       
 32517 					'</div>' +
       
 32518 					hueHtml +
       
 32519 				'</div>'
       
 32520 			);
       
 32521 		}
       
 32522 	});
       
 32523 });
       
 32524 
       
 32525 // Included from: js/tinymce/classes/ui/Path.js
       
 32526 
       
 32527 /**
       
 32528  * Path.js
       
 32529  *
       
 32530  * Copyright, Moxiecode Systems AB
       
 32531  * Released under LGPL License.
       
 32532  *
       
 32533  * License: http://www.tinymce.com/license
       
 32534  * Contributing: http://www.tinymce.com/contributing
       
 32535  */
       
 32536 
       
 32537 /**
       
 32538  * Creates a new path control.
       
 32539  *
       
 32540  * @-x-less Path.less
       
 32541  * @class tinymce.ui.Path
       
 32542  * @extends tinymce.ui.Widget
       
 32543  */
       
 32544 define("tinymce/ui/Path", [
       
 32545 	"tinymce/ui/Widget"
       
 32546 ], function(Widget) {
       
 32547 	"use strict";
       
 32548 
       
 32549 	return Widget.extend({
       
 32550 		/**
       
 32551 		 * Constructs a instance with the specified settings.
       
 32552 		 *
       
 32553 		 * @constructor
       
 32554 		 * @param {Object} settings Name/value object with settings.
       
 32555 		 * @setting {String} delimiter Delimiter to display between items in path.
       
 32556 		 */
       
 32557 		init: function(settings) {
       
 32558 			var self = this;
       
 32559 
       
 32560 			if (!settings.delimiter) {
       
 32561 				settings.delimiter = '\u00BB';
       
 32562 			}
       
 32563 
       
 32564 			self._super(settings);
       
 32565 			self.addClass('path');
       
 32566 			self.canFocus = true;
       
 32567 
       
 32568 			self.on('click', function(e) {
       
 32569 				var index, target = e.target;
       
 32570 
       
 32571 				if ((index = target.getAttribute('data-index'))) {
       
 32572 					self.fire('select', {value: self.data()[index], index: index});
       
 32573 				}
       
 32574 			});
       
 32575 		},
       
 32576 
       
 32577 		/**
       
 32578 		 * Focuses the current control.
       
 32579 		 *
       
 32580 		 * @method focus
       
 32581 		 * @return {tinymce.ui.Control} Current control instance.
       
 32582 		 */
       
 32583 		focus: function() {
       
 32584 			var self = this;
       
 32585 
       
 32586 			self.getEl().firstChild.focus();
       
 32587 
       
 32588 			return self;
       
 32589 		},
       
 32590 
       
 32591 		/**
       
 32592 		 * Sets/gets the data to be used for the path.
       
 32593 		 *
       
 32594 		 * @method data
       
 32595 		 * @param {Array} data Array with items name is rendered to path.
       
 32596 		 */
       
 32597 		data: function(data) {
       
 32598 			var self = this;
       
 32599 
       
 32600 			if (typeof data !== "undefined") {
       
 32601 				self._data = data;
       
 32602 				self.update();
       
 32603 
       
 32604 				return self;
       
 32605 			}
       
 32606 
       
 32607 			return self._data;
       
 32608 		},
       
 32609 
       
 32610 		/**
       
 32611 		 * Updated the path.
       
 32612 		 *
       
 32613 		 * @private
       
 32614 		 */
       
 32615 		update: function() {
       
 32616 			this.innerHtml(this._getPathHtml());
       
 32617 		},
       
 32618 
       
 32619 		/**
       
 32620 		 * Called after the control has been rendered.
       
 32621 		 *
       
 32622 		 * @method postRender
       
 32623 		 */
       
 32624 		postRender: function() {
       
 32625 			var self = this;
       
 32626 
       
 32627 			self._super();
       
 32628 
       
 32629 			self.data(self.settings.data);
       
 32630 		},
       
 32631 
       
 32632 		/**
       
 32633 		 * Renders the control as a HTML string.
       
 32634 		 *
       
 32635 		 * @method renderHtml
       
 32636 		 * @return {String} HTML representing the control.
       
 32637 		 */
       
 32638 		renderHtml: function() {
       
 32639 			var self = this;
       
 32640 
       
 32641 			return (
       
 32642 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
       
 32643 					self._getPathHtml() +
       
 32644 				'</div>'
       
 32645 			);
       
 32646 		},
       
 32647 
       
 32648 		_getPathHtml: function() {
       
 32649 			var self = this, parts = self._data || [], i, l, html = '', prefix = self.classPrefix;
       
 32650 
       
 32651 			for (i = 0, l = parts.length; i < l; i++) {
       
 32652 				html += (
       
 32653 					(i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') +
       
 32654 					'<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' +
       
 32655 					i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + i + '">' + parts[i].name + '</div>'
       
 32656 				);
       
 32657 			}
       
 32658 
       
 32659 			if (!html) {
       
 32660 				html = '<div class="' + prefix + 'path-item">\u00a0</div>';
       
 32661 			}
       
 32662 
       
 32663 			return html;
       
 32664 		}
       
 32665 	});
       
 32666 });
       
 32667 
       
 32668 // Included from: js/tinymce/classes/ui/ElementPath.js
       
 32669 
       
 32670 /**
       
 32671  * ElementPath.js
       
 32672  *
       
 32673  * Copyright, Moxiecode Systems AB
       
 32674  * Released under LGPL License.
       
 32675  *
       
 32676  * License: http://www.tinymce.com/license
       
 32677  * Contributing: http://www.tinymce.com/contributing
       
 32678  */
       
 32679 
       
 32680 /**
       
 32681  * This control creates an path for the current selections parent elements in TinyMCE.
       
 32682  *
       
 32683  * @class tinymce.ui.ElementPath
       
 32684  * @extends tinymce.ui.Path
       
 32685  */
       
 32686 define("tinymce/ui/ElementPath", [
       
 32687 	"tinymce/ui/Path",
       
 32688 	"tinymce/EditorManager"
       
 32689 ], function(Path, EditorManager) {
       
 32690 	return Path.extend({
       
 32691 		/**
       
 32692 		 * Post render method. Called after the control has been rendered to the target.
       
 32693 		 *
       
 32694 		 * @method postRender
       
 32695 		 * @return {tinymce.ui.ElementPath} Current combobox instance.
       
 32696 		 */
       
 32697 		postRender: function() {
       
 32698 			var self = this, editor = EditorManager.activeEditor;
       
 32699 
       
 32700 			function isHidden(elm) {
       
 32701 				if (elm.nodeType === 1) {
       
 32702 					if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) {
       
 32703 						return true;
       
 32704 					}
       
 32705 
       
 32706 					if (elm.getAttribute('data-mce-type') === 'bookmark') {
       
 32707 						return true;
       
 32708 					}
       
 32709 				}
       
 32710 
       
 32711 				return false;
       
 32712 			}
       
 32713 
       
 32714 			if (editor.settings.elementpath !== false) {
       
 32715 				self.on('select', function(e) {
       
 32716 					editor.focus();
       
 32717 					editor.selection.select(this.data()[e.index].element);
       
 32718 					editor.nodeChanged();
       
 32719 				});
       
 32720 
       
 32721 				editor.on('nodeChange', function(e) {
       
 32722 					var outParents = [], parents = e.parents, i = parents.length;
       
 32723 
       
 32724 					while (i--) {
       
 32725 						if (parents[i].nodeType == 1 && !isHidden(parents[i])) {
       
 32726 							var args = editor.fire('ResolveName', {
       
 32727 								name: parents[i].nodeName.toLowerCase(),
       
 32728 								target: parents[i]
       
 32729 							});
       
 32730 
       
 32731 							if (!args.isDefaultPrevented()) {
       
 32732 								outParents.push({name: args.name, element: parents[i]});
       
 32733 							}
       
 32734 
       
 32735 							if (args.isPropagationStopped()) {
       
 32736 								break;
       
 32737 							}
       
 32738 						}
       
 32739 					}
       
 32740 
       
 32741 					self.data(outParents);
       
 32742 				});
       
 32743 			}
       
 32744 
       
 32745 			return self._super();
       
 32746 		}
       
 32747 	});
       
 32748 });
       
 32749 
       
 32750 // Included from: js/tinymce/classes/ui/FormItem.js
       
 32751 
       
 32752 /**
       
 32753  * FormItem.js
       
 32754  *
       
 32755  * Copyright, Moxiecode Systems AB
       
 32756  * Released under LGPL License.
       
 32757  *
       
 32758  * License: http://www.tinymce.com/license
       
 32759  * Contributing: http://www.tinymce.com/contributing
       
 32760  */
       
 32761 
       
 32762 /**
       
 32763  * This class is a container created by the form element with
       
 32764  * a label and control item.
       
 32765  *
       
 32766  * @class tinymce.ui.FormItem
       
 32767  * @extends tinymce.ui.Container
       
 32768  * @setting {String} label Label to display for the form item.
       
 32769  */
       
 32770 define("tinymce/ui/FormItem", [
       
 32771 	"tinymce/ui/Container"
       
 32772 ], function(Container) {
       
 32773 	"use strict";
       
 32774 
       
 32775 	return Container.extend({
       
 32776 		Defaults: {
       
 32777 			layout: 'flex',
       
 32778 			align: 'center',
       
 32779 			defaults: {
       
 32780 				flex: 1
       
 32781 			}
       
 32782 		},
       
 32783 
       
 32784 		/**
       
 32785 		 * Renders the control as a HTML string.
       
 32786 		 *
       
 32787 		 * @method renderHtml
       
 32788 		 * @return {String} HTML representing the control.
       
 32789 		 */
       
 32790 		renderHtml: function() {
       
 32791 			var self = this, layout = self._layout, prefix = self.classPrefix;
       
 32792 
       
 32793 			self.addClass('formitem');
       
 32794 			layout.preRender(self);
       
 32795 
       
 32796 			return (
       
 32797 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
       
 32798 					(self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' +
       
 32799 						self.settings.title + '</div>') : '') +
       
 32800 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 32801 						(self.settings.html || '') + layout.renderHtml(self) +
       
 32802 					'</div>' +
       
 32803 				'</div>'
       
 32804 			);
       
 32805 		}
       
 32806 	});
       
 32807 });
       
 32808 
       
 32809 // Included from: js/tinymce/classes/ui/Form.js
       
 32810 
       
 32811 /**
       
 32812  * Form.js
       
 32813  *
       
 32814  * Copyright, Moxiecode Systems AB
       
 32815  * Released under LGPL License.
       
 32816  *
       
 32817  * License: http://www.tinymce.com/license
       
 32818  * Contributing: http://www.tinymce.com/contributing
       
 32819  */
       
 32820 
       
 32821 /**
       
 32822  * This class creates a form container. A form container has the ability
       
 32823  * to automatically wrap items in tinymce.ui.FormItem instances.
       
 32824  *
       
 32825  * Each FormItem instance is a container for the label and the item.
       
 32826  *
       
 32827  * @example
       
 32828  * tinymce.ui.Factory.create({
       
 32829  *     type: 'form',
       
 32830  *     items: [
       
 32831  *         {type: 'textbox', label: 'My text box'}
       
 32832  *     ]
       
 32833  * }).renderTo(document.body);
       
 32834  *
       
 32835  * @class tinymce.ui.Form
       
 32836  * @extends tinymce.ui.Container
       
 32837  */
       
 32838 define("tinymce/ui/Form", [
       
 32839 	"tinymce/ui/Container",
       
 32840 	"tinymce/ui/FormItem",
       
 32841 	"tinymce/util/Tools"
       
 32842 ], function(Container, FormItem, Tools) {
       
 32843 	"use strict";
       
 32844 
       
 32845 	return Container.extend({
       
 32846 		Defaults: {
       
 32847 			containerCls: 'form',
       
 32848 			layout: 'flex',
       
 32849 			direction: 'column',
       
 32850 			align: 'stretch',
       
 32851 			flex: 1,
       
 32852 			padding: 20,
       
 32853 			labelGap: 30,
       
 32854 			spacing: 10,
       
 32855 			callbacks: {
       
 32856 				submit: function() {
       
 32857 					this.submit();
       
 32858 				}
       
 32859 			}
       
 32860 		},
       
 32861 
       
 32862 		/**
       
 32863 		 * This method gets invoked before the control is rendered.
       
 32864 		 *
       
 32865 		 * @method preRender
       
 32866 		 */
       
 32867 		preRender: function() {
       
 32868 			var self = this, items = self.items();
       
 32869 
       
 32870 			if (!self.settings.formItemDefaults) {
       
 32871 				self.settings.formItemDefaults = {
       
 32872 					layout: 'flex',
       
 32873 					autoResize: "overflow",
       
 32874 					defaults: {flex: 1}
       
 32875 				};
       
 32876 			}
       
 32877 
       
 32878 			// Wrap any labeled items in FormItems
       
 32879 			items.each(function(ctrl) {
       
 32880 				var formItem, label = ctrl.settings.label;
       
 32881 
       
 32882 				if (label) {
       
 32883 					formItem = new FormItem(Tools.extend({
       
 32884 						items: {
       
 32885 							type: 'label',
       
 32886 							id: ctrl._id + '-l',
       
 32887 							text: label,
       
 32888 							flex: 0,
       
 32889 							forId: ctrl._id,
       
 32890 							disabled: ctrl.disabled()
       
 32891 						}
       
 32892 					}, self.settings.formItemDefaults));
       
 32893 
       
 32894 					formItem.type = 'formitem';
       
 32895 					ctrl.aria('labelledby', ctrl._id + '-l');
       
 32896 
       
 32897 					if (typeof ctrl.settings.flex == "undefined") {
       
 32898 						ctrl.settings.flex = 1;
       
 32899 					}
       
 32900 
       
 32901 					self.replace(ctrl, formItem);
       
 32902 					formItem.add(ctrl);
       
 32903 				}
       
 32904 			});
       
 32905 		},
       
 32906 
       
 32907 		/**
       
 32908 		 * Recalcs label widths.
       
 32909 		 *
       
 32910 		 * @private
       
 32911 		 */
       
 32912 		recalcLabels: function() {
       
 32913 			var self = this, maxLabelWidth = 0, labels = [], i, labelGap, items;
       
 32914 
       
 32915 			if (self.settings.labelGapCalc === false) {
       
 32916 				return;
       
 32917 			}
       
 32918 
       
 32919 			if (self.settings.labelGapCalc == "children") {
       
 32920 				items = self.find('formitem');
       
 32921 			} else {
       
 32922 				items = self.items();
       
 32923 			}
       
 32924 
       
 32925 			items.filter('formitem').each(function(item) {
       
 32926 				var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
       
 32927 
       
 32928 				maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
       
 32929 				labels.push(labelCtrl);
       
 32930 			});
       
 32931 
       
 32932 			labelGap = self.settings.labelGap || 0;
       
 32933 
       
 32934 			i = labels.length;
       
 32935 			while (i--) {
       
 32936 				labels[i].settings.minWidth = maxLabelWidth + labelGap;
       
 32937 			}
       
 32938 		},
       
 32939 
       
 32940 		/**
       
 32941 		 * Getter/setter for the visibility state.
       
 32942 		 *
       
 32943 		 * @method visible
       
 32944 		 * @param {Boolean} [state] True/false state to show/hide.
       
 32945 		 * @return {tinymce.ui.Form|Boolean} True/false state or current control.
       
 32946 		 */
       
 32947 		visible: function(state) {
       
 32948 			var val = this._super(state);
       
 32949 
       
 32950 			if (state === true && this._rendered) {
       
 32951 				this.recalcLabels();
       
 32952 			}
       
 32953 
       
 32954 			return val;
       
 32955 		},
       
 32956 
       
 32957 		/**
       
 32958 		 * Fires a submit event with the serialized form.
       
 32959 		 *
       
 32960 		 * @method submit
       
 32961 		 * @return {Object} Event arguments object.
       
 32962 		 */
       
 32963 		submit: function() {
       
 32964 			return this.fire('submit', {data: this.toJSON()});
       
 32965 		},
       
 32966 
       
 32967 		/**
       
 32968 		 * Post render method. Called after the control has been rendered to the target.
       
 32969 		 *
       
 32970 		 * @method postRender
       
 32971 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
       
 32972 		 */
       
 32973 		postRender: function() {
       
 32974 			var self = this;
       
 32975 
       
 32976 			self._super();
       
 32977 			self.recalcLabels();
       
 32978 			self.fromJSON(self.settings.data);
       
 32979 		}
       
 32980 	});
       
 32981 });
       
 32982 
       
 32983 // Included from: js/tinymce/classes/ui/FieldSet.js
       
 32984 
       
 32985 /**
       
 32986  * FieldSet.js
       
 32987  *
       
 32988  * Copyright, Moxiecode Systems AB
       
 32989  * Released under LGPL License.
       
 32990  *
       
 32991  * License: http://www.tinymce.com/license
       
 32992  * Contributing: http://www.tinymce.com/contributing
       
 32993  */
       
 32994 
       
 32995 /**
       
 32996  * This class creates fieldset containers.
       
 32997  *
       
 32998  * @-x-less FieldSet.less
       
 32999  * @class tinymce.ui.FieldSet
       
 33000  * @extends tinymce.ui.Form
       
 33001  */
       
 33002 define("tinymce/ui/FieldSet", [
       
 33003 	"tinymce/ui/Form"
       
 33004 ], function(Form) {
       
 33005 	"use strict";
       
 33006 
       
 33007 	return Form.extend({
       
 33008 		Defaults: {
       
 33009 			containerCls: 'fieldset',
       
 33010 			layout: 'flex',
       
 33011 			direction: 'column',
       
 33012 			align: 'stretch',
       
 33013 			flex: 1,
       
 33014 			padding: "25 15 5 15",
       
 33015 			labelGap: 30,
       
 33016 			spacing: 10,
       
 33017 			border: 1
       
 33018 		},
       
 33019 
       
 33020 		/**
       
 33021 		 * Renders the control as a HTML string.
       
 33022 		 *
       
 33023 		 * @method renderHtml
       
 33024 		 * @return {String} HTML representing the control.
       
 33025 		 */
       
 33026 		renderHtml: function() {
       
 33027 			var self = this, layout = self._layout, prefix = self.classPrefix;
       
 33028 
       
 33029 			self.preRender();
       
 33030 			layout.preRender(self);
       
 33031 
       
 33032 			return (
       
 33033 				'<fieldset id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
       
 33034 					(self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' +
       
 33035 						self.settings.title + '</legend>') : '') +
       
 33036 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 33037 						(self.settings.html || '') + layout.renderHtml(self) +
       
 33038 					'</div>' +
       
 33039 				'</fieldset>'
       
 33040 			);
       
 33041 		}
       
 33042 	});
       
 33043 });
       
 33044 
       
 33045 // Included from: js/tinymce/classes/ui/FilePicker.js
       
 33046 
       
 33047 /**
       
 33048  * FilePicker.js
       
 33049  *
       
 33050  * Copyright, Moxiecode Systems AB
       
 33051  * Released under LGPL License.
       
 33052  *
       
 33053  * License: http://www.tinymce.com/license
       
 33054  * Contributing: http://www.tinymce.com/contributing
       
 33055  */
       
 33056 
       
 33057 /*global tinymce:true */
       
 33058 
       
 33059 /**
       
 33060  * This class creates a file picker control.
       
 33061  *
       
 33062  * @class tinymce.ui.FilePicker
       
 33063  * @extends tinymce.ui.ComboBox
       
 33064  */
       
 33065 define("tinymce/ui/FilePicker", [
       
 33066 	"tinymce/ui/ComboBox",
       
 33067 	"tinymce/util/Tools"
       
 33068 ], function(ComboBox, Tools) {
       
 33069 	"use strict";
       
 33070 
       
 33071 	return ComboBox.extend({
       
 33072 		/**
       
 33073 		 * Constructs a new control instance with the specified settings.
       
 33074 		 *
       
 33075 		 * @constructor
       
 33076 		 * @param {Object} settings Name/value object with settings.
       
 33077 		 */
       
 33078 		init: function(settings) {
       
 33079 			var self = this, editor = tinymce.activeEditor, editorSettings = editor.settings;
       
 33080 			var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes;
       
 33081 
       
 33082 			settings.spellcheck = false;
       
 33083 
       
 33084 			fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types;
       
 33085 			if (fileBrowserCallbackTypes) {
       
 33086 				fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
       
 33087 			}
       
 33088 
       
 33089 			if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype]) {
       
 33090 				fileBrowserCallback = editorSettings.file_picker_callback;
       
 33091 				if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
       
 33092 					actionCallback = function() {
       
 33093 						var meta = self.fire('beforecall').meta;
       
 33094 
       
 33095 						meta = Tools.extend({filetype: settings.filetype}, meta);
       
 33096 
       
 33097 						// file_picker_callback(callback, currentValue, metaData)
       
 33098 						fileBrowserCallback.call(
       
 33099 							editor,
       
 33100 							function(value, meta) {
       
 33101 								self.value(value).fire('change', {meta: meta});
       
 33102 							},
       
 33103 							self.value(),
       
 33104 							meta
       
 33105 						);
       
 33106 					};
       
 33107 				} else {
       
 33108 					// Legacy callback: file_picker_callback(id, currentValue, filetype, window)
       
 33109 					fileBrowserCallback = editorSettings.file_browser_callback;
       
 33110 					if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
       
 33111 						actionCallback = function() {
       
 33112 							fileBrowserCallback(
       
 33113 								self.getEl('inp').id,
       
 33114 								self.value(),
       
 33115 								settings.filetype,
       
 33116 								window
       
 33117 							);
       
 33118 						};
       
 33119 					}
       
 33120 				}
       
 33121 			}
       
 33122 
       
 33123 			if (actionCallback) {
       
 33124 				settings.icon = 'browse';
       
 33125 				settings.onaction = actionCallback;
       
 33126 			}
       
 33127 
       
 33128 			self._super(settings);
       
 33129 		}
       
 33130 	});
       
 33131 });
       
 33132 
       
 33133 // Included from: js/tinymce/classes/ui/FitLayout.js
       
 33134 
       
 33135 /**
       
 33136  * FitLayout.js
       
 33137  *
       
 33138  * Copyright, Moxiecode Systems AB
       
 33139  * Released under LGPL License.
       
 33140  *
       
 33141  * License: http://www.tinymce.com/license
       
 33142  * Contributing: http://www.tinymce.com/contributing
       
 33143  */
       
 33144 
       
 33145 /**
       
 33146  * This layout manager will resize the control to be the size of it's parent container.
       
 33147  * In other words width: 100% and height: 100%.
       
 33148  *
       
 33149  * @-x-less FitLayout.less
       
 33150  * @class tinymce.ui.FitLayout
       
 33151  * @extends tinymce.ui.AbsoluteLayout
       
 33152  */
       
 33153 define("tinymce/ui/FitLayout", [
       
 33154 	"tinymce/ui/AbsoluteLayout"
       
 33155 ], function(AbsoluteLayout) {
       
 33156 	"use strict";
       
 33157 
       
 33158 	return AbsoluteLayout.extend({
       
 33159 		/**
       
 33160 		 * Recalculates the positions of the controls in the specified container.
       
 33161 		 *
       
 33162 		 * @method recalc
       
 33163 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 33164 		 */
       
 33165 		recalc: function(container) {
       
 33166 			var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox();
       
 33167 
       
 33168 			container.items().filter(':visible').each(function(ctrl) {
       
 33169 				ctrl.layoutRect({
       
 33170 					x: paddingBox.left,
       
 33171 					y: paddingBox.top,
       
 33172 					w: contLayoutRect.innerW - paddingBox.right - paddingBox.left,
       
 33173 					h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom
       
 33174 				});
       
 33175 
       
 33176 				if (ctrl.recalc) {
       
 33177 					ctrl.recalc();
       
 33178 				}
       
 33179 			});
       
 33180 		}
       
 33181 	});
       
 33182 });
       
 33183 
       
 33184 // Included from: js/tinymce/classes/ui/FlexLayout.js
       
 33185 
       
 33186 /**
       
 33187  * FlexLayout.js
       
 33188  *
       
 33189  * Copyright, Moxiecode Systems AB
       
 33190  * Released under LGPL License.
       
 33191  *
       
 33192  * License: http://www.tinymce.com/license
       
 33193  * Contributing: http://www.tinymce.com/contributing
       
 33194  */
       
 33195 
       
 33196 /**
       
 33197  * This layout manager works similar to the CSS flex box.
       
 33198  *
       
 33199  * @setting {String} direction row|row-reverse|column|column-reverse
       
 33200  * @setting {Number} flex A positive-number to flex by.
       
 33201  * @setting {String} align start|end|center|stretch
       
 33202  * @setting {String} pack start|end|justify
       
 33203  *
       
 33204  * @class tinymce.ui.FlexLayout
       
 33205  * @extends tinymce.ui.AbsoluteLayout
       
 33206  */
       
 33207 define("tinymce/ui/FlexLayout", [
       
 33208 	"tinymce/ui/AbsoluteLayout"
       
 33209 ], function(AbsoluteLayout) {
       
 33210 	"use strict";
       
 33211 
       
 33212 	return AbsoluteLayout.extend({
       
 33213 		/**
       
 33214 		 * Recalculates the positions of the controls in the specified container.
       
 33215 		 *
       
 33216 		 * @method recalc
       
 33217 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 33218 		 */
       
 33219 		recalc: function(container) {
       
 33220 			// A ton of variables, needs to be in the same scope for performance
       
 33221 			var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
       
 33222 			var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
       
 33223 			var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName;
       
 33224 			var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName;
       
 33225 			var alignDeltaSizeName, alignContentSizeName;
       
 33226 			var max = Math.max, min = Math.min;
       
 33227 
       
 33228 			// Get container items, properties and settings
       
 33229 			items = container.items().filter(':visible');
       
 33230 			contLayoutRect = container.layoutRect();
       
 33231 			contPaddingBox = container._paddingBox;
       
 33232 			contSettings = container.settings;
       
 33233 			direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
       
 33234 			align = contSettings.align;
       
 33235 			pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
       
 33236 			spacing = contSettings.spacing || 0;
       
 33237 
       
 33238 			if (direction == "row-reversed" || direction == "column-reverse") {
       
 33239 				items = items.set(items.toArray().reverse());
       
 33240 				direction = direction.split('-')[0];
       
 33241 			}
       
 33242 
       
 33243 			// Setup axis variable name for row/column direction since the calculations is the same
       
 33244 			if (direction == "column") {
       
 33245 				posName = "y";
       
 33246 				sizeName = "h";
       
 33247 				minSizeName = "minH";
       
 33248 				maxSizeName = "maxH";
       
 33249 				innerSizeName = "innerH";
       
 33250 				beforeName = 'top';
       
 33251 				deltaSizeName = "deltaH";
       
 33252 				contentSizeName = "contentH";
       
 33253 
       
 33254 				alignBeforeName = "left";
       
 33255 				alignSizeName = "w";
       
 33256 				alignAxisName = "x";
       
 33257 				alignInnerSizeName = "innerW";
       
 33258 				alignMinSizeName = "minW";
       
 33259 				alignAfterName = "right";
       
 33260 				alignDeltaSizeName = "deltaW";
       
 33261 				alignContentSizeName = "contentW";
       
 33262 			} else {
       
 33263 				posName = "x";
       
 33264 				sizeName = "w";
       
 33265 				minSizeName = "minW";
       
 33266 				maxSizeName = "maxW";
       
 33267 				innerSizeName = "innerW";
       
 33268 				beforeName = 'left';
       
 33269 				deltaSizeName = "deltaW";
       
 33270 				contentSizeName = "contentW";
       
 33271 
       
 33272 				alignBeforeName = "top";
       
 33273 				alignSizeName = "h";
       
 33274 				alignAxisName = "y";
       
 33275 				alignInnerSizeName = "innerH";
       
 33276 				alignMinSizeName = "minH";
       
 33277 				alignAfterName = "bottom";
       
 33278 				alignDeltaSizeName = "deltaH";
       
 33279 				alignContentSizeName = "contentH";
       
 33280 			}
       
 33281 
       
 33282 			// Figure out total flex, availableSpace and collect any max size elements
       
 33283 			availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
       
 33284 			maxAlignEndPos = totalFlex = 0;
       
 33285 			for (i = 0, l = items.length; i < l; i++) {
       
 33286 				ctrl = items[i];
       
 33287 				ctrlLayoutRect = ctrl.layoutRect();
       
 33288 				ctrlSettings = ctrl.settings;
       
 33289 				flex = ctrlSettings.flex;
       
 33290 				availableSpace -= (i < l - 1 ? spacing : 0);
       
 33291 
       
 33292 				if (flex > 0) {
       
 33293 					totalFlex += flex;
       
 33294 
       
 33295 					// Flexed item has a max size then we need to check if we will hit that size
       
 33296 					if (ctrlLayoutRect[maxSizeName]) {
       
 33297 						maxSizeItems.push(ctrl);
       
 33298 					}
       
 33299 
       
 33300 					ctrlLayoutRect.flex = flex;
       
 33301 				}
       
 33302 
       
 33303 				availableSpace -= ctrlLayoutRect[minSizeName];
       
 33304 
       
 33305 				// Calculate the align end position to be used to check for overflow/underflow
       
 33306 				size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
       
 33307 				if (size > maxAlignEndPos) {
       
 33308 					maxAlignEndPos = size;
       
 33309 				}
       
 33310 			}
       
 33311 
       
 33312 			// Calculate minW/minH
       
 33313 			rect = {};
       
 33314 			if (availableSpace < 0) {
       
 33315 				rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
       
 33316 			} else {
       
 33317 				rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
       
 33318 			}
       
 33319 
       
 33320 			rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
       
 33321 
       
 33322 			rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
       
 33323 			rect[alignContentSizeName] = maxAlignEndPos;
       
 33324 			rect.minW = min(rect.minW, contLayoutRect.maxW);
       
 33325 			rect.minH = min(rect.minH, contLayoutRect.maxH);
       
 33326 			rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
       
 33327 			rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
       
 33328 
       
 33329 			// Resize container container if minSize was changed
       
 33330 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
       
 33331 				rect.w = rect.minW;
       
 33332 				rect.h = rect.minH;
       
 33333 
       
 33334 				container.layoutRect(rect);
       
 33335 				this.recalc(container);
       
 33336 
       
 33337 				// Forced recalc for example if items are hidden/shown
       
 33338 				if (container._lastRect === null) {
       
 33339 					var parentCtrl = container.parent();
       
 33340 					if (parentCtrl) {
       
 33341 						parentCtrl._lastRect = null;
       
 33342 						parentCtrl.recalc();
       
 33343 					}
       
 33344 				}
       
 33345 
       
 33346 				return;
       
 33347 			}
       
 33348 
       
 33349 			// Handle max size elements, check if they will become to wide with current options
       
 33350 			ratio = availableSpace / totalFlex;
       
 33351 			for (i = 0, l = maxSizeItems.length; i < l; i++) {
       
 33352 				ctrl = maxSizeItems[i];
       
 33353 				ctrlLayoutRect = ctrl.layoutRect();
       
 33354 				maxSize = ctrlLayoutRect[maxSizeName];
       
 33355 				size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
       
 33356 
       
 33357 				if (size > maxSize) {
       
 33358 					availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
       
 33359 					totalFlex -= ctrlLayoutRect.flex;
       
 33360 					ctrlLayoutRect.flex = 0;
       
 33361 					ctrlLayoutRect.maxFlexSize = maxSize;
       
 33362 				} else {
       
 33363 					ctrlLayoutRect.maxFlexSize = 0;
       
 33364 				}
       
 33365 			}
       
 33366 
       
 33367 			// Setup new ratio, target layout rect, start position
       
 33368 			ratio = availableSpace / totalFlex;
       
 33369 			pos = contPaddingBox[beforeName];
       
 33370 			rect = {};
       
 33371 
       
 33372 			// Handle pack setting moves the start position to end, center
       
 33373 			if (totalFlex === 0) {
       
 33374 				if (pack == "end") {
       
 33375 					pos = availableSpace + contPaddingBox[beforeName];
       
 33376 				} else if (pack == "center") {
       
 33377 					pos = Math.round(
       
 33378 						(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
       
 33379 					) + contPaddingBox[beforeName];
       
 33380 
       
 33381 					if (pos < 0) {
       
 33382 						pos = contPaddingBox[beforeName];
       
 33383 					}
       
 33384 				} else if (pack == "justify") {
       
 33385 					pos = contPaddingBox[beforeName];
       
 33386 					spacing = Math.floor(availableSpace / (items.length - 1));
       
 33387 				}
       
 33388 			}
       
 33389 
       
 33390 			// Default aligning (start) the other ones needs to be calculated while doing the layout
       
 33391 			rect[alignAxisName] = contPaddingBox[alignBeforeName];
       
 33392 
       
 33393 			// Start laying out controls
       
 33394 			for (i = 0, l = items.length; i < l; i++) {
       
 33395 				ctrl = items[i];
       
 33396 				ctrlLayoutRect = ctrl.layoutRect();
       
 33397 				size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
       
 33398 
       
 33399 				// Align the control on the other axis
       
 33400 				if (align === "center") {
       
 33401 					rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
       
 33402 				} else if (align === "stretch") {
       
 33403 					rect[alignSizeName] = max(
       
 33404 						ctrlLayoutRect[alignMinSizeName] || 0,
       
 33405 						contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
       
 33406 					);
       
 33407 					rect[alignAxisName] = contPaddingBox[alignBeforeName];
       
 33408 				} else if (align === "end") {
       
 33409 					rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top;
       
 33410 				}
       
 33411 
       
 33412 				// Calculate new size based on flex
       
 33413 				if (ctrlLayoutRect.flex > 0) {
       
 33414 					size += ctrlLayoutRect.flex * ratio;
       
 33415 				}
       
 33416 
       
 33417 				rect[sizeName] = size;
       
 33418 				rect[posName] = pos;
       
 33419 				ctrl.layoutRect(rect);
       
 33420 
       
 33421 				// Recalculate containers
       
 33422 				if (ctrl.recalc) {
       
 33423 					ctrl.recalc();
       
 33424 				}
       
 33425 
       
 33426 				// Move x/y position
       
 33427 				pos += size + spacing;
       
 33428 			}
       
 33429 		}
       
 33430 	});
       
 33431 });
       
 33432 
       
 33433 // Included from: js/tinymce/classes/ui/FlowLayout.js
       
 33434 
       
 33435 /**
       
 33436  * FlowLayout.js
       
 33437  *
       
 33438  * Copyright, Moxiecode Systems AB
       
 33439  * Released under LGPL License.
       
 33440  *
       
 33441  * License: http://www.tinymce.com/license
       
 33442  * Contributing: http://www.tinymce.com/contributing
       
 33443  */
       
 33444 
       
 33445 /**
       
 33446  * This layout manager will place the controls by using the browsers native layout.
       
 33447  *
       
 33448  * @-x-less FlowLayout.less
       
 33449  * @class tinymce.ui.FlowLayout
       
 33450  * @extends tinymce.ui.Layout
       
 33451  */
       
 33452 define("tinymce/ui/FlowLayout", [
       
 33453 	"tinymce/ui/Layout"
       
 33454 ], function(Layout) {
       
 33455 	return Layout.extend({
       
 33456 		Defaults: {
       
 33457 			containerClass: 'flow-layout',
       
 33458 			controlClass: 'flow-layout-item',
       
 33459 			endClass: 'break'
       
 33460 		},
       
 33461 
       
 33462 		/**
       
 33463 		 * Recalculates the positions of the controls in the specified container.
       
 33464 		 *
       
 33465 		 * @method recalc
       
 33466 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 33467 		 */
       
 33468 		recalc: function(container) {
       
 33469 			container.items().filter(':visible').each(function(ctrl) {
       
 33470 				if (ctrl.recalc) {
       
 33471 					ctrl.recalc();
       
 33472 				}
       
 33473 			});
       
 33474 		}
       
 33475 	});
       
 33476 });
       
 33477 
       
 33478 // Included from: js/tinymce/classes/ui/FormatControls.js
       
 33479 
       
 33480 /**
       
 33481  * FormatControls.js
       
 33482  *
       
 33483  * Copyright, Moxiecode Systems AB
       
 33484  * Released under LGPL License.
       
 33485  *
       
 33486  * License: http://www.tinymce.com/license
       
 33487  * Contributing: http://www.tinymce.com/contributing
       
 33488  */
       
 33489 
       
 33490 /**
       
 33491  * Internal class containing all TinyMCE specific control types such as
       
 33492  * format listboxes, fontlist boxes, toolbar buttons etc.
       
 33493  *
       
 33494  * @class tinymce.ui.FormatControls
       
 33495  */
       
 33496 define("tinymce/ui/FormatControls", [
       
 33497 	"tinymce/ui/Control",
       
 33498 	"tinymce/ui/Widget",
       
 33499 	"tinymce/ui/FloatPanel",
       
 33500 	"tinymce/util/Tools",
       
 33501 	"tinymce/EditorManager",
       
 33502 	"tinymce/Env"
       
 33503 ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
       
 33504 	var each = Tools.each;
       
 33505 
       
 33506 	EditorManager.on('AddEditor', function(e) {
       
 33507 		if (e.editor.rtl) {
       
 33508 			Control.rtl = true;
       
 33509 		}
       
 33510 
       
 33511 		registerControls(e.editor);
       
 33512 	});
       
 33513 
       
 33514 	Control.translate = function(text) {
       
 33515 		return EditorManager.translate(text);
       
 33516 	};
       
 33517 
       
 33518 	Widget.tooltips = !Env.iOS;
       
 33519 
       
 33520 	function registerControls(editor) {
       
 33521 		var formatMenu;
       
 33522 
       
 33523 		function createListBoxChangeHandler(items, formatName) {
       
 33524 			return function() {
       
 33525 				var self = this;
       
 33526 
       
 33527 				editor.on('nodeChange', function(e) {
       
 33528 					var formatter = editor.formatter;
       
 33529 					var value = null;
       
 33530 
       
 33531 					each(e.parents, function(node) {
       
 33532 						each(items, function(item) {
       
 33533 							if (formatName) {
       
 33534 								if (formatter.matchNode(node, formatName, {value: item.value})) {
       
 33535 									value = item.value;
       
 33536 								}
       
 33537 							} else {
       
 33538 								if (formatter.matchNode(node, item.value)) {
       
 33539 									value = item.value;
       
 33540 								}
       
 33541 							}
       
 33542 
       
 33543 							if (value) {
       
 33544 								return false;
       
 33545 							}
       
 33546 						});
       
 33547 
       
 33548 						if (value) {
       
 33549 							return false;
       
 33550 						}
       
 33551 					});
       
 33552 
       
 33553 					self.value(value);
       
 33554 				});
       
 33555 			};
       
 33556 		}
       
 33557 
       
 33558 		function createFormats(formats) {
       
 33559 			formats = formats.replace(/;$/, '').split(';');
       
 33560 
       
 33561 			var i = formats.length;
       
 33562 			while (i--) {
       
 33563 				formats[i] = formats[i].split('=');
       
 33564 			}
       
 33565 
       
 33566 			return formats;
       
 33567 		}
       
 33568 
       
 33569 		function createFormatMenu() {
       
 33570 			var count = 0, newFormats = [];
       
 33571 
       
 33572 			var defaultStyleFormats = [
       
 33573 				{title: 'Headings', items: [
       
 33574 					{title: 'Heading 1', format: 'h1'},
       
 33575 					{title: 'Heading 2', format: 'h2'},
       
 33576 					{title: 'Heading 3', format: 'h3'},
       
 33577 					{title: 'Heading 4', format: 'h4'},
       
 33578 					{title: 'Heading 5', format: 'h5'},
       
 33579 					{title: 'Heading 6', format: 'h6'}
       
 33580 				]},
       
 33581 
       
 33582 				{title: 'Inline', items: [
       
 33583 					{title: 'Bold', icon: 'bold', format: 'bold'},
       
 33584 					{title: 'Italic', icon: 'italic', format: 'italic'},
       
 33585 					{title: 'Underline', icon: 'underline', format: 'underline'},
       
 33586 					{title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
       
 33587 					{title: 'Superscript', icon: 'superscript', format: 'superscript'},
       
 33588 					{title: 'Subscript', icon: 'subscript', format: 'subscript'},
       
 33589 					{title: 'Code', icon: 'code', format: 'code'}
       
 33590 				]},
       
 33591 
       
 33592 				{title: 'Blocks', items: [
       
 33593 					{title: 'Paragraph', format: 'p'},
       
 33594 					{title: 'Blockquote', format: 'blockquote'},
       
 33595 					{title: 'Div', format: 'div'},
       
 33596 					{title: 'Pre', format: 'pre'}
       
 33597 				]},
       
 33598 
       
 33599 				{title: 'Alignment', items: [
       
 33600 					{title: 'Left', icon: 'alignleft', format: 'alignleft'},
       
 33601 					{title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
       
 33602 					{title: 'Right', icon: 'alignright', format: 'alignright'},
       
 33603 					{title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
       
 33604 				]}
       
 33605 			];
       
 33606 
       
 33607 			function createMenu(formats) {
       
 33608 				var menu = [];
       
 33609 
       
 33610 				if (!formats) {
       
 33611 					return;
       
 33612 				}
       
 33613 
       
 33614 				each(formats, function(format) {
       
 33615 					var menuItem = {
       
 33616 						text: format.title,
       
 33617 						icon: format.icon
       
 33618 					};
       
 33619 
       
 33620 					if (format.items) {
       
 33621 						menuItem.menu = createMenu(format.items);
       
 33622 					} else {
       
 33623 						var formatName = format.format || "custom" + count++;
       
 33624 
       
 33625 						if (!format.format) {
       
 33626 							format.name = formatName;
       
 33627 							newFormats.push(format);
       
 33628 						}
       
 33629 
       
 33630 						menuItem.format = formatName;
       
 33631 						menuItem.cmd = format.cmd;
       
 33632 					}
       
 33633 
       
 33634 					menu.push(menuItem);
       
 33635 				});
       
 33636 
       
 33637 				return menu;
       
 33638 			}
       
 33639 
       
 33640 			function createStylesMenu() {
       
 33641 				var menu;
       
 33642 
       
 33643 				if (editor.settings.style_formats_merge) {
       
 33644 					if (editor.settings.style_formats) {
       
 33645 						menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats));
       
 33646 					} else {
       
 33647 						menu = createMenu(defaultStyleFormats);
       
 33648 					}
       
 33649 				} else {
       
 33650 					menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
       
 33651 				}
       
 33652 
       
 33653 				return menu;
       
 33654 			}
       
 33655 
       
 33656 			editor.on('init', function() {
       
 33657 				each(newFormats, function(format) {
       
 33658 					editor.formatter.register(format.name, format);
       
 33659 				});
       
 33660 			});
       
 33661 
       
 33662 			return {
       
 33663 				type: 'menu',
       
 33664 				items: createStylesMenu(),
       
 33665 				onPostRender: function(e) {
       
 33666 					editor.fire('renderFormatsMenu', {control: e.control});
       
 33667 				},
       
 33668 				itemDefaults: {
       
 33669 					preview: true,
       
 33670 
       
 33671 					textStyle: function() {
       
 33672 						if (this.settings.format) {
       
 33673 							return editor.formatter.getCssText(this.settings.format);
       
 33674 						}
       
 33675 					},
       
 33676 
       
 33677 					onPostRender: function() {
       
 33678 						var self = this;
       
 33679 
       
 33680 						self.parent().on('show', function() {
       
 33681 							var formatName, command;
       
 33682 
       
 33683 							formatName = self.settings.format;
       
 33684 							if (formatName) {
       
 33685 								self.disabled(!editor.formatter.canApply(formatName));
       
 33686 								self.active(editor.formatter.match(formatName));
       
 33687 							}
       
 33688 
       
 33689 							command = self.settings.cmd;
       
 33690 							if (command) {
       
 33691 								self.active(editor.queryCommandState(command));
       
 33692 							}
       
 33693 						});
       
 33694 					},
       
 33695 
       
 33696 					onclick: function() {
       
 33697 						if (this.settings.format) {
       
 33698 							toggleFormat(this.settings.format);
       
 33699 						}
       
 33700 
       
 33701 						if (this.settings.cmd) {
       
 33702 							editor.execCommand(this.settings.cmd);
       
 33703 						}
       
 33704 					}
       
 33705 				}
       
 33706 			};
       
 33707 		}
       
 33708 
       
 33709 		formatMenu = createFormatMenu();
       
 33710 
       
 33711 		// Simple format controls <control/format>:<UI text>
       
 33712 		each({
       
 33713 			bold: 'Bold',
       
 33714 			italic: 'Italic',
       
 33715 			underline: 'Underline',
       
 33716 			strikethrough: 'Strikethrough',
       
 33717 			subscript: 'Subscript',
       
 33718 			superscript: 'Superscript'
       
 33719 		}, function(text, name) {
       
 33720 			editor.addButton(name, {
       
 33721 				tooltip: text,
       
 33722 				onPostRender: function() {
       
 33723 					var self = this;
       
 33724 
       
 33725 					// TODO: Fix this
       
 33726 					if (editor.formatter) {
       
 33727 						editor.formatter.formatChanged(name, function(state) {
       
 33728 							self.active(state);
       
 33729 						});
       
 33730 					} else {
       
 33731 						editor.on('init', function() {
       
 33732 							editor.formatter.formatChanged(name, function(state) {
       
 33733 								self.active(state);
       
 33734 							});
       
 33735 						});
       
 33736 					}
       
 33737 				},
       
 33738 				onclick: function() {
       
 33739 					toggleFormat(name);
       
 33740 				}
       
 33741 			});
       
 33742 		});
       
 33743 
       
 33744 		// Simple command controls <control>:[<UI text>,<Command>]
       
 33745 		each({
       
 33746 			outdent: ['Decrease indent', 'Outdent'],
       
 33747 			indent: ['Increase indent', 'Indent'],
       
 33748 			cut: ['Cut', 'Cut'],
       
 33749 			copy: ['Copy', 'Copy'],
       
 33750 			paste: ['Paste', 'Paste'],
       
 33751 			help: ['Help', 'mceHelp'],
       
 33752 			selectall: ['Select all', 'SelectAll'],
       
 33753 			removeformat: ['Clear formatting', 'RemoveFormat'],
       
 33754 			visualaid: ['Visual aids', 'mceToggleVisualAid'],
       
 33755 			newdocument: ['New document', 'mceNewDocument']
       
 33756 		}, function(item, name) {
       
 33757 			editor.addButton(name, {
       
 33758 				tooltip: item[0],
       
 33759 				cmd: item[1]
       
 33760 			});
       
 33761 		});
       
 33762 
       
 33763 		// Simple command controls with format state
       
 33764 		each({
       
 33765 			blockquote: ['Blockquote', 'mceBlockQuote'],
       
 33766 			numlist: ['Numbered list', 'InsertOrderedList'],
       
 33767 			bullist: ['Bullet list', 'InsertUnorderedList'],
       
 33768 			subscript: ['Subscript', 'Subscript'],
       
 33769 			superscript: ['Superscript', 'Superscript'],
       
 33770 			alignleft: ['Align left', 'JustifyLeft'],
       
 33771 			aligncenter: ['Align center', 'JustifyCenter'],
       
 33772 			alignright: ['Align right', 'JustifyRight'],
       
 33773 			alignjustify: ['Justify', 'JustifyFull']
       
 33774 		}, function(item, name) {
       
 33775 			editor.addButton(name, {
       
 33776 				tooltip: item[0],
       
 33777 				cmd: item[1],
       
 33778 				onPostRender: function() {
       
 33779 					var self = this;
       
 33780 
       
 33781 					// TODO: Fix this
       
 33782 					if (editor.formatter) {
       
 33783 						editor.formatter.formatChanged(name, function(state) {
       
 33784 							self.active(state);
       
 33785 						});
       
 33786 					} else {
       
 33787 						editor.on('init', function() {
       
 33788 							editor.formatter.formatChanged(name, function(state) {
       
 33789 								self.active(state);
       
 33790 							});
       
 33791 						});
       
 33792 					}
       
 33793 				}
       
 33794 			});
       
 33795 		});
       
 33796 
       
 33797 		function toggleUndoRedoState(type) {
       
 33798 			return function() {
       
 33799 				var self = this;
       
 33800 
       
 33801 				type = type == 'redo' ? 'hasRedo' : 'hasUndo';
       
 33802 
       
 33803 				function checkState() {
       
 33804 					return editor.undoManager ? editor.undoManager[type]() : false;
       
 33805 				}
       
 33806 
       
 33807 				self.disabled(!checkState());
       
 33808 				editor.on('Undo Redo AddUndo TypingUndo ClearUndos', function() {
       
 33809 					self.disabled(!checkState());
       
 33810 				});
       
 33811 			};
       
 33812 		}
       
 33813 
       
 33814 		function toggleVisualAidState() {
       
 33815 			var self = this;
       
 33816 
       
 33817 			editor.on('VisualAid', function(e) {
       
 33818 				self.active(e.hasVisual);
       
 33819 			});
       
 33820 
       
 33821 			self.active(editor.hasVisual);
       
 33822 		}
       
 33823 
       
 33824 		editor.addButton('undo', {
       
 33825 			tooltip: 'Undo',
       
 33826 			onPostRender: toggleUndoRedoState('undo'),
       
 33827 			cmd: 'undo'
       
 33828 		});
       
 33829 
       
 33830 		editor.addButton('redo', {
       
 33831 			tooltip: 'Redo',
       
 33832 			onPostRender: toggleUndoRedoState('redo'),
       
 33833 			cmd: 'redo'
       
 33834 		});
       
 33835 
       
 33836 		editor.addMenuItem('newdocument', {
       
 33837 			text: 'New document',
       
 33838 			icon: 'newdocument',
       
 33839 			cmd: 'mceNewDocument'
       
 33840 		});
       
 33841 
       
 33842 		editor.addMenuItem('undo', {
       
 33843 			text: 'Undo',
       
 33844 			icon: 'undo',
       
 33845 			shortcut: 'Meta+Z',
       
 33846 			onPostRender: toggleUndoRedoState('undo'),
       
 33847 			cmd: 'undo'
       
 33848 		});
       
 33849 
       
 33850 		editor.addMenuItem('redo', {
       
 33851 			text: 'Redo',
       
 33852 			icon: 'redo',
       
 33853 			shortcut: 'Meta+Y',
       
 33854 			onPostRender: toggleUndoRedoState('redo'),
       
 33855 			cmd: 'redo'
       
 33856 		});
       
 33857 
       
 33858 		editor.addMenuItem('visualaid', {
       
 33859 			text: 'Visual aids',
       
 33860 			selectable: true,
       
 33861 			onPostRender: toggleVisualAidState,
       
 33862 			cmd: 'mceToggleVisualAid'
       
 33863 		});
       
 33864 
       
 33865 		each({
       
 33866 			cut: ['Cut', 'Cut', 'Meta+X'],
       
 33867 			copy: ['Copy', 'Copy', 'Meta+C'],
       
 33868 			paste: ['Paste', 'Paste', 'Meta+V'],
       
 33869 			selectall: ['Select all', 'SelectAll', 'Meta+A'],
       
 33870 			bold: ['Bold', 'Bold', 'Meta+B'],
       
 33871 			italic: ['Italic', 'Italic', 'Meta+I'],
       
 33872 			underline: ['Underline', 'Underline'],
       
 33873 			strikethrough: ['Strikethrough', 'Strikethrough'],
       
 33874 			subscript: ['Subscript', 'Subscript'],
       
 33875 			superscript: ['Superscript', 'Superscript'],
       
 33876 			removeformat: ['Clear formatting', 'RemoveFormat']
       
 33877 		}, function(item, name) {
       
 33878 			editor.addMenuItem(name, {
       
 33879 				text: item[0],
       
 33880 				icon: name,
       
 33881 				shortcut: item[2],
       
 33882 				cmd: item[1]
       
 33883 			});
       
 33884 		});
       
 33885 
       
 33886 		editor.on('mousedown', function() {
       
 33887 			FloatPanel.hideAll();
       
 33888 		});
       
 33889 
       
 33890 		function toggleFormat(fmt) {
       
 33891 			if (fmt.control) {
       
 33892 				fmt = fmt.control.value();
       
 33893 			}
       
 33894 
       
 33895 			if (fmt) {
       
 33896 				editor.execCommand('mceToggleFormat', false, fmt);
       
 33897 			}
       
 33898 		}
       
 33899 
       
 33900 		editor.addButton('styleselect', {
       
 33901 			type: 'menubutton',
       
 33902 			text: 'Formats',
       
 33903 			menu: formatMenu
       
 33904 		});
       
 33905 
       
 33906 		editor.addButton('formatselect', function() {
       
 33907 			var items = [], blocks = createFormats(editor.settings.block_formats ||
       
 33908 				'Paragraph=p;' +
       
 33909 				'Heading 1=h1;' +
       
 33910 				'Heading 2=h2;' +
       
 33911 				'Heading 3=h3;' +
       
 33912 				'Heading 4=h4;' +
       
 33913 				'Heading 5=h5;' +
       
 33914 				'Heading 6=h6;' +
       
 33915 				'Preformatted=pre'
       
 33916 			);
       
 33917 
       
 33918 			each(blocks, function(block) {
       
 33919 				items.push({
       
 33920 					text: block[0],
       
 33921 					value: block[1],
       
 33922 					textStyle: function() {
       
 33923 						return editor.formatter.getCssText(block[1]);
       
 33924 					}
       
 33925 				});
       
 33926 			});
       
 33927 
       
 33928 			return {
       
 33929 				type: 'listbox',
       
 33930 				text: blocks[0][0],
       
 33931 				values: items,
       
 33932 				fixedWidth: true,
       
 33933 				onselect: toggleFormat,
       
 33934 				onPostRender: createListBoxChangeHandler(items)
       
 33935 			};
       
 33936 		});
       
 33937 
       
 33938 		editor.addButton('fontselect', function() {
       
 33939 			var defaultFontsFormats =
       
 33940 				'Andale Mono=andale mono,monospace;' +
       
 33941 				'Arial=arial,helvetica,sans-serif;' +
       
 33942 				'Arial Black=arial black,sans-serif;' +
       
 33943 				'Book Antiqua=book antiqua,palatino,serif;' +
       
 33944 				'Comic Sans MS=comic sans ms,sans-serif;' +
       
 33945 				'Courier New=courier new,courier,monospace;' +
       
 33946 				'Georgia=georgia,palatino,serif;' +
       
 33947 				'Helvetica=helvetica,arial,sans-serif;' +
       
 33948 				'Impact=impact,sans-serif;' +
       
 33949 				'Symbol=symbol;' +
       
 33950 				'Tahoma=tahoma,arial,helvetica,sans-serif;' +
       
 33951 				'Terminal=terminal,monaco,monospace;' +
       
 33952 				'Times New Roman=times new roman,times,serif;' +
       
 33953 				'Trebuchet MS=trebuchet ms,geneva,sans-serif;' +
       
 33954 				'Verdana=verdana,geneva,sans-serif;' +
       
 33955 				'Webdings=webdings;' +
       
 33956 				'Wingdings=wingdings,zapf dingbats';
       
 33957 
       
 33958 			var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
       
 33959 
       
 33960 			each(fonts, function(font) {
       
 33961 				items.push({
       
 33962 					text: {raw: font[0]},
       
 33963 					value: font[1],
       
 33964 					textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
       
 33965 				});
       
 33966 			});
       
 33967 
       
 33968 			return {
       
 33969 				type: 'listbox',
       
 33970 				text: 'Font Family',
       
 33971 				tooltip: 'Font Family',
       
 33972 				values: items,
       
 33973 				fixedWidth: true,
       
 33974 				onPostRender: createListBoxChangeHandler(items, 'fontname'),
       
 33975 				onselect: function(e) {
       
 33976 					if (e.control.settings.value) {
       
 33977 						editor.execCommand('FontName', false, e.control.settings.value);
       
 33978 					}
       
 33979 				}
       
 33980 			};
       
 33981 		});
       
 33982 
       
 33983 		editor.addButton('fontsizeselect', function() {
       
 33984 			var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
       
 33985 			var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
       
 33986 
       
 33987 			each(fontsize_formats.split(' '), function(item) {
       
 33988 				var text = item, value = item;
       
 33989 				// Allow text=value font sizes.
       
 33990 				var values = item.split('=');
       
 33991 				if (values.length > 1) {
       
 33992 					text = values[0];
       
 33993 					value = values[1];
       
 33994 				}
       
 33995 				items.push({text: text, value: value});
       
 33996 			});
       
 33997 
       
 33998 			return {
       
 33999 				type: 'listbox',
       
 34000 				text: 'Font Sizes',
       
 34001 				tooltip: 'Font Sizes',
       
 34002 				values: items,
       
 34003 				fixedWidth: true,
       
 34004 				onPostRender: createListBoxChangeHandler(items, 'fontsize'),
       
 34005 				onclick: function(e) {
       
 34006 					if (e.control.settings.value) {
       
 34007 						editor.execCommand('FontSize', false, e.control.settings.value);
       
 34008 					}
       
 34009 				}
       
 34010 			};
       
 34011 		});
       
 34012 
       
 34013 		editor.addMenuItem('formats', {
       
 34014 			text: 'Formats',
       
 34015 			menu: formatMenu
       
 34016 		});
       
 34017 	}
       
 34018 });
       
 34019 
       
 34020 // Included from: js/tinymce/classes/ui/GridLayout.js
       
 34021 
       
 34022 /**
       
 34023  * GridLayout.js
       
 34024  *
       
 34025  * Copyright, Moxiecode Systems AB
       
 34026  * Released under LGPL License.
       
 34027  *
       
 34028  * License: http://www.tinymce.com/license
       
 34029  * Contributing: http://www.tinymce.com/contributing
       
 34030  */
       
 34031 
       
 34032 /**
       
 34033  * This layout manager places controls in a grid.
       
 34034  *
       
 34035  * @setting {Number} spacing Spacing between controls.
       
 34036  * @setting {Number} spacingH Horizontal spacing between controls.
       
 34037  * @setting {Number} spacingV Vertical spacing between controls.
       
 34038  * @setting {Number} columns Number of columns to use.
       
 34039  * @setting {String/Array} alignH start|end|center|stretch or array of values for each column.
       
 34040  * @setting {String/Array} alignV start|end|center|stretch or array of values for each column.
       
 34041  * @setting {String} pack start|end
       
 34042  *
       
 34043  * @class tinymce.ui.GridLayout
       
 34044  * @extends tinymce.ui.AbsoluteLayout
       
 34045  */
       
 34046 define("tinymce/ui/GridLayout", [
       
 34047 	"tinymce/ui/AbsoluteLayout"
       
 34048 ], function(AbsoluteLayout) {
       
 34049 	"use strict";
       
 34050 
       
 34051 	return AbsoluteLayout.extend({
       
 34052 		/**
       
 34053 		 * Recalculates the positions of the controls in the specified container.
       
 34054 		 *
       
 34055 		 * @method recalc
       
 34056 		 * @param {tinymce.ui.Container} container Container instance to recalc.
       
 34057 		 */
       
 34058 		recalc: function(container) {
       
 34059 			var settings = container.settings, rows, cols, items, contLayoutRect, width, height, rect,
       
 34060 				ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY,
       
 34061 				colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight, reverseRows, idx;
       
 34062 
       
 34063 			// Get layout settings
       
 34064 			settings = container.settings;
       
 34065 			items = container.items().filter(':visible');
       
 34066 			contLayoutRect = container.layoutRect();
       
 34067 			cols = settings.columns || Math.ceil(Math.sqrt(items.length));
       
 34068 			rows = Math.ceil(items.length / cols);
       
 34069 			spacingH = settings.spacingH || settings.spacing || 0;
       
 34070 			spacingV = settings.spacingV || settings.spacing || 0;
       
 34071 			alignH = settings.alignH || settings.align;
       
 34072 			alignV = settings.alignV || settings.align;
       
 34073 			contPaddingBox = container._paddingBox;
       
 34074 			reverseRows = 'reverseRows' in settings ? settings.reverseRows : container.isRtl();
       
 34075 
       
 34076 			if (alignH && typeof alignH == "string") {
       
 34077 				alignH = [alignH];
       
 34078 			}
       
 34079 
       
 34080 			if (alignV && typeof alignV == "string") {
       
 34081 				alignV = [alignV];
       
 34082 			}
       
 34083 
       
 34084 			// Zero padd columnWidths
       
 34085 			for (x = 0; x < cols; x++) {
       
 34086 				colWidths.push(0);
       
 34087 			}
       
 34088 
       
 34089 			// Zero padd rowHeights
       
 34090 			for (y = 0; y < rows; y++) {
       
 34091 				rowHeights.push(0);
       
 34092 			}
       
 34093 
       
 34094 			// Calculate columnWidths and rowHeights
       
 34095 			for (y = 0; y < rows; y++) {
       
 34096 				for (x = 0; x < cols; x++) {
       
 34097 					ctrl = items[y * cols + x];
       
 34098 
       
 34099 					// Out of bounds
       
 34100 					if (!ctrl) {
       
 34101 						break;
       
 34102 					}
       
 34103 
       
 34104 					ctrlLayoutRect = ctrl.layoutRect();
       
 34105 					ctrlMinWidth = ctrlLayoutRect.minW;
       
 34106 					ctrlMinHeight = ctrlLayoutRect.minH;
       
 34107 
       
 34108 					colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x];
       
 34109 					rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y];
       
 34110 				}
       
 34111 			}
       
 34112 
       
 34113 			// Calculate maxX
       
 34114 			availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right;
       
 34115 			for (maxX = 0, x = 0; x < cols; x++) {
       
 34116 				maxX += colWidths[x] + (x > 0 ? spacingH : 0);
       
 34117 				availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x];
       
 34118 			}
       
 34119 
       
 34120 			// Calculate maxY
       
 34121 			availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom;
       
 34122 			for (maxY = 0, y = 0; y < rows; y++) {
       
 34123 				maxY += rowHeights[y] + (y > 0 ? spacingV : 0);
       
 34124 				availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y];
       
 34125 			}
       
 34126 
       
 34127 			maxX += contPaddingBox.left + contPaddingBox.right;
       
 34128 			maxY += contPaddingBox.top + contPaddingBox.bottom;
       
 34129 
       
 34130 			// Calculate minW/minH
       
 34131 			rect = {};
       
 34132 			rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW);
       
 34133 			rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH);
       
 34134 
       
 34135 			rect.contentW = rect.minW - contLayoutRect.deltaW;
       
 34136 			rect.contentH = rect.minH - contLayoutRect.deltaH;
       
 34137 			rect.minW = Math.min(rect.minW, contLayoutRect.maxW);
       
 34138 			rect.minH = Math.min(rect.minH, contLayoutRect.maxH);
       
 34139 			rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth);
       
 34140 			rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight);
       
 34141 
       
 34142 			// Resize container container if minSize was changed
       
 34143 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
       
 34144 				rect.w = rect.minW;
       
 34145 				rect.h = rect.minH;
       
 34146 
       
 34147 				container.layoutRect(rect);
       
 34148 				this.recalc(container);
       
 34149 
       
 34150 				// Forced recalc for example if items are hidden/shown
       
 34151 				if (container._lastRect === null) {
       
 34152 					var parentCtrl = container.parent();
       
 34153 					if (parentCtrl) {
       
 34154 						parentCtrl._lastRect = null;
       
 34155 						parentCtrl.recalc();
       
 34156 					}
       
 34157 				}
       
 34158 
       
 34159 				return;
       
 34160 			}
       
 34161 
       
 34162 			// Update contentW/contentH so absEnd moves correctly
       
 34163 			if (contLayoutRect.autoResize) {
       
 34164 				rect = container.layoutRect(rect);
       
 34165 				rect.contentW = rect.minW - contLayoutRect.deltaW;
       
 34166 				rect.contentH = rect.minH - contLayoutRect.deltaH;
       
 34167 			}
       
 34168 
       
 34169 			var flexV;
       
 34170 
       
 34171 			if (settings.packV == 'start') {
       
 34172 				flexV = 0;
       
 34173 			} else {
       
 34174 				flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0;
       
 34175 			}
       
 34176 
       
 34177 			// Calculate totalFlex
       
 34178 			var totalFlex = 0;
       
 34179 			var flexWidths = settings.flexWidths;
       
 34180 			if (flexWidths) {
       
 34181 				for (x = 0; x < flexWidths.length; x++) {
       
 34182 					totalFlex += flexWidths[x];
       
 34183 				}
       
 34184 			} else {
       
 34185 				totalFlex = cols;
       
 34186 			}
       
 34187 
       
 34188 			// Calculate new column widths based on flex values
       
 34189 			var ratio = availableWidth / totalFlex;
       
 34190 			for (x = 0; x < cols; x++) {
       
 34191 				colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
       
 34192 			}
       
 34193 
       
 34194 			// Move/resize controls
       
 34195 			posY = contPaddingBox.top;
       
 34196 			for (y = 0; y < rows; y++) {
       
 34197 				posX = contPaddingBox.left;
       
 34198 				height = rowHeights[y] + flexV;
       
 34199 
       
 34200 				for (x = 0; x < cols; x++) {
       
 34201 					if (reverseRows) {
       
 34202 						idx = y * cols + cols - 1 - x;
       
 34203 					} else {
       
 34204 						idx = y * cols + x;
       
 34205 					}
       
 34206 
       
 34207 					ctrl = items[idx];
       
 34208 
       
 34209 					// No more controls to render then break
       
 34210 					if (!ctrl) {
       
 34211 						break;
       
 34212 					}
       
 34213 
       
 34214 					// Get control settings and calculate x, y
       
 34215 					ctrlSettings = ctrl.settings;
       
 34216 					ctrlLayoutRect = ctrl.layoutRect();
       
 34217 					width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth);
       
 34218 					ctrlLayoutRect.x = posX;
       
 34219 					ctrlLayoutRect.y = posY;
       
 34220 
       
 34221 					// Align control horizontal
       
 34222 					align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null);
       
 34223 					if (align == "center") {
       
 34224 						ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2);
       
 34225 					} else if (align == "right") {
       
 34226 						ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w;
       
 34227 					} else if (align == "stretch") {
       
 34228 						ctrlLayoutRect.w = width;
       
 34229 					}
       
 34230 
       
 34231 					// Align control vertical
       
 34232 					align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null);
       
 34233 					if (align == "center") {
       
 34234 						ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2);
       
 34235 					} else if (align == "bottom") {
       
 34236 						ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h;
       
 34237 					} else if (align == "stretch") {
       
 34238 						ctrlLayoutRect.h = height;
       
 34239 					}
       
 34240 
       
 34241 					ctrl.layoutRect(ctrlLayoutRect);
       
 34242 
       
 34243 					posX += width + spacingH;
       
 34244 
       
 34245 					if (ctrl.recalc) {
       
 34246 						ctrl.recalc();
       
 34247 					}
       
 34248 				}
       
 34249 
       
 34250 				posY += height + spacingV;
       
 34251 			}
       
 34252 		}
       
 34253 	});
       
 34254 });
       
 34255 
       
 34256 // Included from: js/tinymce/classes/ui/Iframe.js
       
 34257 
       
 34258 /**
       
 34259  * Iframe.js
       
 34260  *
       
 34261  * Copyright, Moxiecode Systems AB
       
 34262  * Released under LGPL License.
       
 34263  *
       
 34264  * License: http://www.tinymce.com/license
       
 34265  * Contributing: http://www.tinymce.com/contributing
       
 34266  */
       
 34267 
       
 34268 /*jshint scripturl:true */
       
 34269 
       
 34270 /**
       
 34271  * This class creates an iframe.
       
 34272  *
       
 34273  * @setting {String} url Url to open in the iframe.
       
 34274  *
       
 34275  * @-x-less Iframe.less
       
 34276  * @class tinymce.ui.Iframe
       
 34277  * @extends tinymce.ui.Widget
       
 34278  */
       
 34279 define("tinymce/ui/Iframe", [
       
 34280 	"tinymce/ui/Widget"
       
 34281 ], function(Widget) {
       
 34282 	"use strict";
       
 34283 
       
 34284 	return Widget.extend({
       
 34285 		/**
       
 34286 		 * Renders the control as a HTML string.
       
 34287 		 *
       
 34288 		 * @method renderHtml
       
 34289 		 * @return {String} HTML representing the control.
       
 34290 		 */
       
 34291 		renderHtml: function() {
       
 34292 			var self = this;
       
 34293 
       
 34294 			self.addClass('iframe');
       
 34295 			self.canFocus = false;
       
 34296 
       
 34297 			/*eslint no-script-url:0 */
       
 34298 			return (
       
 34299 				'<iframe id="' + self._id + '" class="' + self.classes() + '" tabindex="-1" src="' +
       
 34300 				(self.settings.url || "javascript:\'\'") + '" frameborder="0"></iframe>'
       
 34301 			);
       
 34302 		},
       
 34303 
       
 34304 		/**
       
 34305 		 * Setter for the iframe source.
       
 34306 		 *
       
 34307 		 * @method src
       
 34308 		 * @param {String} src Source URL for iframe.
       
 34309 		 */
       
 34310 		src: function(src) {
       
 34311 			this.getEl().src = src;
       
 34312 		},
       
 34313 
       
 34314 		/**
       
 34315 		 * Inner HTML for the iframe.
       
 34316 		 *
       
 34317 		 * @method html
       
 34318 		 * @param {String} html HTML string to set as HTML inside the iframe.
       
 34319 		 * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
       
 34320 		 * @return {tinymce.ui.Iframe} Current iframe control.
       
 34321 		 */
       
 34322 		html: function(html, callback) {
       
 34323 			var self = this, body = this.getEl().contentWindow.document.body;
       
 34324 
       
 34325 			// Wait for iframe to initialize IE 10 takes time
       
 34326 			if (!body) {
       
 34327 				setTimeout(function() {
       
 34328 					self.html(html);
       
 34329 				}, 0);
       
 34330 			} else {
       
 34331 				body.innerHTML = html;
       
 34332 
       
 34333 				if (callback) {
       
 34334 					callback();
       
 34335 				}
       
 34336 			}
       
 34337 
       
 34338 			return this;
       
 34339 		}
       
 34340 	});
       
 34341 });
       
 34342 
       
 34343 // Included from: js/tinymce/classes/ui/Label.js
       
 34344 
       
 34345 /**
       
 34346  * Label.js
       
 34347  *
       
 34348  * Copyright, Moxiecode Systems AB
       
 34349  * Released under LGPL License.
       
 34350  *
       
 34351  * License: http://www.tinymce.com/license
       
 34352  * Contributing: http://www.tinymce.com/contributing
       
 34353  */
       
 34354 
       
 34355 /**
       
 34356  * This class creates a label element. A label is a simple text control
       
 34357  * that can be bound to other controls.
       
 34358  *
       
 34359  * @-x-less Label.less
       
 34360  * @class tinymce.ui.Label
       
 34361  * @extends tinymce.ui.Widget
       
 34362  */
       
 34363 define("tinymce/ui/Label", [
       
 34364 	"tinymce/ui/Widget",
       
 34365 	"tinymce/ui/DomUtils"
       
 34366 ], function(Widget, DomUtils) {
       
 34367 	"use strict";
       
 34368 
       
 34369 	return Widget.extend({
       
 34370 		/**
       
 34371 		 * Constructs a instance with the specified settings.
       
 34372 		 *
       
 34373 		 * @constructor
       
 34374 		 * @param {Object} settings Name/value object with settings.
       
 34375 		 * @param {Boolean} multiline Multiline label.
       
 34376 		 */
       
 34377 		init: function(settings) {
       
 34378 			var self = this;
       
 34379 
       
 34380 			self._super(settings);
       
 34381 			self.addClass('widget');
       
 34382 			self.addClass('label');
       
 34383 			self.canFocus = false;
       
 34384 
       
 34385 			if (settings.multiline) {
       
 34386 				self.addClass('autoscroll');
       
 34387 			}
       
 34388 
       
 34389 			if (settings.strong) {
       
 34390 				self.addClass('strong');
       
 34391 			}
       
 34392 		},
       
 34393 
       
 34394 		/**
       
 34395 		 * Initializes the current controls layout rect.
       
 34396 		 * This will be executed by the layout managers to determine the
       
 34397 		 * default minWidth/minHeight etc.
       
 34398 		 *
       
 34399 		 * @method initLayoutRect
       
 34400 		 * @return {Object} Layout rect instance.
       
 34401 		 */
       
 34402 		initLayoutRect: function() {
       
 34403 			var self = this, layoutRect = self._super();
       
 34404 
       
 34405 			if (self.settings.multiline) {
       
 34406 				var size = DomUtils.getSize(self.getEl());
       
 34407 
       
 34408 				// Check if the text fits within maxW if not then try word wrapping it
       
 34409 				if (size.width > layoutRect.maxW) {
       
 34410 					layoutRect.minW = layoutRect.maxW;
       
 34411 					self.addClass('multiline');
       
 34412 				}
       
 34413 
       
 34414 				self.getEl().style.width = layoutRect.minW + 'px';
       
 34415 				layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height);
       
 34416 			}
       
 34417 
       
 34418 			return layoutRect;
       
 34419 		},
       
 34420 
       
 34421 		/**
       
 34422 		 * Repaints the control after a layout operation.
       
 34423 		 *
       
 34424 		 * @method repaint
       
 34425 		 */
       
 34426 		repaint: function() {
       
 34427 			var self = this;
       
 34428 
       
 34429 			if (!self.settings.multiline) {
       
 34430 				self.getEl().style.lineHeight = self.layoutRect().h + 'px';
       
 34431 			}
       
 34432 
       
 34433 			return self._super();
       
 34434 		},
       
 34435 
       
 34436 		/**
       
 34437 		 * Sets/gets the current label text.
       
 34438 		 *
       
 34439 		 * @method text
       
 34440 		 * @param {String} [text] New label text.
       
 34441 		 * @return {String|tinymce.ui.Label} Current text or current label instance.
       
 34442 		 */
       
 34443 		text: function(text) {
       
 34444 			var self = this;
       
 34445 
       
 34446 			if (self._rendered && text) {
       
 34447 				this.innerHtml(self.encode(text));
       
 34448 			}
       
 34449 
       
 34450 			return self._super(text);
       
 34451 		},
       
 34452 
       
 34453 		/**
       
 34454 		 * Renders the control as a HTML string.
       
 34455 		 *
       
 34456 		 * @method renderHtml
       
 34457 		 * @return {String} HTML representing the control.
       
 34458 		 */
       
 34459 		renderHtml: function() {
       
 34460 			var self = this, forId = self.settings.forId;
       
 34461 
       
 34462 			return (
       
 34463 				'<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId + '"' : '') + '>' +
       
 34464 					self.encode(self._text) +
       
 34465 				'</label>'
       
 34466 			);
       
 34467 		}
       
 34468 	});
       
 34469 });
       
 34470 
       
 34471 // Included from: js/tinymce/classes/ui/Toolbar.js
       
 34472 
       
 34473 /**
       
 34474  * Toolbar.js
       
 34475  *
       
 34476  * Copyright, Moxiecode Systems AB
       
 34477  * Released under LGPL License.
       
 34478  *
       
 34479  * License: http://www.tinymce.com/license
       
 34480  * Contributing: http://www.tinymce.com/contributing
       
 34481  */
       
 34482 
       
 34483 /**
       
 34484  * Creates a new toolbar.
       
 34485  *
       
 34486  * @class tinymce.ui.Toolbar
       
 34487  * @extends tinymce.ui.Container
       
 34488  */
       
 34489 define("tinymce/ui/Toolbar", [
       
 34490 	"tinymce/ui/Container"
       
 34491 ], function(Container) {
       
 34492 	"use strict";
       
 34493 
       
 34494 	return Container.extend({
       
 34495 		Defaults: {
       
 34496 			role: 'toolbar',
       
 34497 			layout: 'flow'
       
 34498 		},
       
 34499 
       
 34500 		/**
       
 34501 		 * Constructs a instance with the specified settings.
       
 34502 		 *
       
 34503 		 * @constructor
       
 34504 		 * @param {Object} settings Name/value object with settings.
       
 34505 		 */
       
 34506 		init: function(settings) {
       
 34507 			var self = this;
       
 34508 
       
 34509 			self._super(settings);
       
 34510 			self.addClass('toolbar');
       
 34511 		},
       
 34512 
       
 34513 		/**
       
 34514 		 * Called after the control has been rendered.
       
 34515 		 *
       
 34516 		 * @method postRender
       
 34517 		 */
       
 34518 		postRender: function() {
       
 34519 			var self = this;
       
 34520 
       
 34521 			self.items().addClass('toolbar-item');
       
 34522 
       
 34523 			return self._super();
       
 34524 		}
       
 34525 	});
       
 34526 });
       
 34527 
       
 34528 // Included from: js/tinymce/classes/ui/MenuBar.js
       
 34529 
       
 34530 /**
       
 34531  * MenuBar.js
       
 34532  *
       
 34533  * Copyright, Moxiecode Systems AB
       
 34534  * Released under LGPL License.
       
 34535  *
       
 34536  * License: http://www.tinymce.com/license
       
 34537  * Contributing: http://www.tinymce.com/contributing
       
 34538  */
       
 34539 
       
 34540 /**
       
 34541  * Creates a new menubar.
       
 34542  *
       
 34543  * @-x-less MenuBar.less
       
 34544  * @class tinymce.ui.MenuBar
       
 34545  * @extends tinymce.ui.Toolbar
       
 34546  */
       
 34547 define("tinymce/ui/MenuBar", [
       
 34548 	"tinymce/ui/Toolbar"
       
 34549 ], function(Toolbar) {
       
 34550 	"use strict";
       
 34551 
       
 34552 	return Toolbar.extend({
       
 34553 		Defaults: {
       
 34554 			role: 'menubar',
       
 34555 			containerCls: 'menubar',
       
 34556 			ariaRoot: true,
       
 34557 			defaults: {
       
 34558 				type: 'menubutton'
       
 34559 			}
       
 34560 		}
       
 34561 	});
       
 34562 });
       
 34563 
       
 34564 // Included from: js/tinymce/classes/ui/MenuButton.js
       
 34565 
       
 34566 /**
       
 34567  * MenuButton.js
       
 34568  *
       
 34569  * Copyright, Moxiecode Systems AB
       
 34570  * Released under LGPL License.
       
 34571  *
       
 34572  * License: http://www.tinymce.com/license
       
 34573  * Contributing: http://www.tinymce.com/contributing
       
 34574  */
       
 34575 
       
 34576 /**
       
 34577  * Creates a new menu button.
       
 34578  *
       
 34579  * @-x-less MenuButton.less
       
 34580  * @class tinymce.ui.MenuButton
       
 34581  * @extends tinymce.ui.Button
       
 34582  */
       
 34583 define("tinymce/ui/MenuButton", [
       
 34584 	"tinymce/ui/Button",
       
 34585 	"tinymce/ui/Factory",
       
 34586 	"tinymce/ui/MenuBar"
       
 34587 ], function(Button, Factory, MenuBar) {
       
 34588 	"use strict";
       
 34589 
       
 34590 	// TODO: Maybe add as some global function
       
 34591 	function isChildOf(node, parent) {
       
 34592 		while (node) {
       
 34593 			if (parent === node) {
       
 34594 				return true;
       
 34595 			}
       
 34596 
       
 34597 			node = node.parentNode;
       
 34598 		}
       
 34599 
       
 34600 		return false;
       
 34601 	}
       
 34602 
       
 34603 	var MenuButton = Button.extend({
       
 34604 		/**
       
 34605 		 * Constructs a instance with the specified settings.
       
 34606 		 *
       
 34607 		 * @constructor
       
 34608 		 * @param {Object} settings Name/value object with settings.
       
 34609 		 */
       
 34610 		init: function(settings) {
       
 34611 			var self = this;
       
 34612 
       
 34613 			self._renderOpen = true;
       
 34614 			self._super(settings);
       
 34615 
       
 34616 			self.addClass('menubtn');
       
 34617 
       
 34618 			if (settings.fixedWidth) {
       
 34619 				self.addClass('fixed-width');
       
 34620 			}
       
 34621 
       
 34622 			self.aria('haspopup', true);
       
 34623 			self.hasPopup = true;
       
 34624 		},
       
 34625 
       
 34626 		/**
       
 34627 		 * Shows the menu for the button.
       
 34628 		 *
       
 34629 		 * @method showMenu
       
 34630 		 */
       
 34631 		showMenu: function() {
       
 34632 			var self = this, settings = self.settings, menu;
       
 34633 
       
 34634 			if (self.menu && self.menu.visible()) {
       
 34635 				return self.hideMenu();
       
 34636 			}
       
 34637 
       
 34638 			if (!self.menu) {
       
 34639 				menu = settings.menu || [];
       
 34640 
       
 34641 				// Is menu array then auto constuct menu control
       
 34642 				if (menu.length) {
       
 34643 					menu = {
       
 34644 						type: 'menu',
       
 34645 						items: menu
       
 34646 					};
       
 34647 				} else {
       
 34648 					menu.type = menu.type || 'menu';
       
 34649 				}
       
 34650 
       
 34651 				self.menu = Factory.create(menu).parent(self).renderTo();
       
 34652 				self.fire('createmenu');
       
 34653 				self.menu.reflow();
       
 34654 				self.menu.on('cancel', function(e) {
       
 34655 					if (e.control.parent() === self.menu) {
       
 34656 						e.stopPropagation();
       
 34657 						self.focus();
       
 34658 						self.hideMenu();
       
 34659 					}
       
 34660 				});
       
 34661 
       
 34662 				// Move focus to button when a menu item is selected/clicked
       
 34663 				self.menu.on('select', function() {
       
 34664 					self.focus();
       
 34665 				});
       
 34666 
       
 34667 				self.menu.on('show hide', function(e) {
       
 34668 					if (e.control == self.menu) {
       
 34669 						self.activeMenu(e.type == 'show');
       
 34670 					}
       
 34671 
       
 34672 					self.aria('expanded', e.type == 'show');
       
 34673 				}).fire('show');
       
 34674 			}
       
 34675 
       
 34676 			self.menu.show();
       
 34677 			self.menu.layoutRect({w: self.layoutRect().w});
       
 34678 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
       
 34679 		},
       
 34680 
       
 34681 		/**
       
 34682 		 * Hides the menu for the button.
       
 34683 		 *
       
 34684 		 * @method hideMenu
       
 34685 		 */
       
 34686 		hideMenu: function() {
       
 34687 			var self = this;
       
 34688 
       
 34689 			if (self.menu) {
       
 34690 				self.menu.items().each(function(item) {
       
 34691 					if (item.hideMenu) {
       
 34692 						item.hideMenu();
       
 34693 					}
       
 34694 				});
       
 34695 
       
 34696 				self.menu.hide();
       
 34697 			}
       
 34698 		},
       
 34699 
       
 34700 		/**
       
 34701 		 * Sets the active menu state.
       
 34702 		 *
       
 34703 		 * @private
       
 34704 		 */
       
 34705 		activeMenu: function(state) {
       
 34706 			this.toggleClass('active', state);
       
 34707 		},
       
 34708 
       
 34709 		/**
       
 34710 		 * Renders the control as a HTML string.
       
 34711 		 *
       
 34712 		 * @method renderHtml
       
 34713 		 * @return {String} HTML representing the control.
       
 34714 		 */
       
 34715 		renderHtml: function() {
       
 34716 			var self = this, id = self._id, prefix = self.classPrefix;
       
 34717 			var icon = self.settings.icon, image;
       
 34718 
       
 34719 			image = self.settings.image;
       
 34720 			if (image) {
       
 34721 				icon = 'none';
       
 34722 
       
 34723 				// Support for [high dpi, low dpi] image sources
       
 34724 				if (typeof image != "string") {
       
 34725 					image = window.getSelection ? image[0] : image[1];
       
 34726 				}
       
 34727 
       
 34728 				image = ' style="background-image: url(\'' + image + '\')"';
       
 34729 			} else {
       
 34730 				image = '';
       
 34731 			}
       
 34732 
       
 34733 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
       
 34734 
       
 34735 			self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
       
 34736 
       
 34737 			return (
       
 34738 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
       
 34739 					'<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
       
 34740 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 34741 						'<span>' + (self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') + '</span>' +
       
 34742 						' <i class="' + prefix + 'caret"></i>' +
       
 34743 					'</button>' +
       
 34744 				'</div>'
       
 34745 			);
       
 34746 		},
       
 34747 
       
 34748 		/**
       
 34749 		 * Gets invoked after the control has been rendered.
       
 34750 		 *
       
 34751 		 * @method postRender
       
 34752 		 */
       
 34753 		postRender: function() {
       
 34754 			var self = this;
       
 34755 
       
 34756 			self.on('click', function(e) {
       
 34757 				if (e.control === self && isChildOf(e.target, self.getEl())) {
       
 34758 					self.showMenu();
       
 34759 
       
 34760 					if (e.aria) {
       
 34761 						self.menu.items()[0].focus();
       
 34762 					}
       
 34763 				}
       
 34764 			});
       
 34765 
       
 34766 			self.on('mouseenter', function(e) {
       
 34767 				var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
       
 34768 
       
 34769 				if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
       
 34770 					parent.items().filter('MenuButton').each(function(ctrl) {
       
 34771 						if (ctrl.hideMenu && ctrl != overCtrl) {
       
 34772 							if (ctrl.menu && ctrl.menu.visible()) {
       
 34773 								hasVisibleSiblingMenu = true;
       
 34774 							}
       
 34775 
       
 34776 							ctrl.hideMenu();
       
 34777 						}
       
 34778 					});
       
 34779 
       
 34780 					if (hasVisibleSiblingMenu) {
       
 34781 						overCtrl.focus(); // Fix for: #5887
       
 34782 						overCtrl.showMenu();
       
 34783 					}
       
 34784 				}
       
 34785 			});
       
 34786 
       
 34787 			return self._super();
       
 34788 		},
       
 34789 
       
 34790 		/**
       
 34791 		 * Sets/gets the current button text.
       
 34792 		 *
       
 34793 		 * @method text
       
 34794 		 * @param {String} [text] New button text.
       
 34795 		 * @return {String|tinymce.ui.MenuButton} Current text or current MenuButton instance.
       
 34796 		 */
       
 34797 		text: function(text) {
       
 34798 			var self = this, i, children;
       
 34799 
       
 34800 			if (self._rendered) {
       
 34801 				children = self.getEl('open').getElementsByTagName('span');
       
 34802 				for (i = 0; i < children.length; i++) {
       
 34803 					children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
       
 34804 				}
       
 34805 			}
       
 34806 
       
 34807 			return this._super(text);
       
 34808 		},
       
 34809 
       
 34810 		/**
       
 34811 		 * Removes the control and it's menus.
       
 34812 		 *
       
 34813 		 * @method remove
       
 34814 		 */
       
 34815 		remove: function() {
       
 34816 			this._super();
       
 34817 
       
 34818 			if (this.menu) {
       
 34819 				this.menu.remove();
       
 34820 			}
       
 34821 		}
       
 34822 	});
       
 34823 
       
 34824 	return MenuButton;
       
 34825 });
       
 34826 
       
 34827 // Included from: js/tinymce/classes/ui/ListBox.js
       
 34828 
       
 34829 /**
       
 34830  * ListBox.js
       
 34831  *
       
 34832  * Copyright, Moxiecode Systems AB
       
 34833  * Released under LGPL License.
       
 34834  *
       
 34835  * License: http://www.tinymce.com/license
       
 34836  * Contributing: http://www.tinymce.com/contributing
       
 34837  */
       
 34838 
       
 34839 /**
       
 34840  * Creates a new list box control.
       
 34841  *
       
 34842  * @-x-less ListBox.less
       
 34843  * @class tinymce.ui.ListBox
       
 34844  * @extends tinymce.ui.MenuButton
       
 34845  */
       
 34846 define("tinymce/ui/ListBox", [
       
 34847 	"tinymce/ui/MenuButton"
       
 34848 ], function(MenuButton) {
       
 34849 	"use strict";
       
 34850 
       
 34851 	return MenuButton.extend({
       
 34852 		/**
       
 34853 		 * Constructs a instance with the specified settings.
       
 34854 		 *
       
 34855 		 * @constructor
       
 34856 		 * @param {Object} settings Name/value object with settings.
       
 34857 		 * @setting {Array} values Array with values to add to list box.
       
 34858 		 */
       
 34859 		init: function(settings) {
       
 34860 			var self = this, values, selected, selectedText, lastItemCtrl;
       
 34861 
       
 34862 			function setSelected(menuValues) {
       
 34863 				// Try to find a selected value
       
 34864 				for (var i = 0; i < menuValues.length; i++) {
       
 34865 					selected = menuValues[i].selected || settings.value === menuValues[i].value;
       
 34866 
       
 34867 					if (selected) {
       
 34868 						selectedText = selectedText || menuValues[i].text;
       
 34869 						self._value = menuValues[i].value;
       
 34870 						break;
       
 34871 					}
       
 34872 
       
 34873 					// If the value has a submenu, try to find the selected values in that menu
       
 34874 					if (menuValues[i].menu) {
       
 34875 						setSelected(menuValues[i].menu);
       
 34876 					}
       
 34877 				}
       
 34878 			}
       
 34879 
       
 34880 			self._values = values = settings.values;
       
 34881 			if (values) {
       
 34882 				setSelected(values);
       
 34883 
       
 34884 				// Default with first item
       
 34885 				if (!selected && values.length > 0) {
       
 34886 					selectedText = values[0].text;
       
 34887 					self._value = values[0].value;
       
 34888 				}
       
 34889 
       
 34890 				settings.menu = values;
       
 34891 			}
       
 34892 
       
 34893 			settings.text = settings.text || selectedText || values[0].text;
       
 34894 
       
 34895 			self._super(settings);
       
 34896 			self.addClass('listbox');
       
 34897 
       
 34898 			self.on('select', function(e) {
       
 34899 				var ctrl = e.control;
       
 34900 
       
 34901 				if (lastItemCtrl) {
       
 34902 					e.lastControl = lastItemCtrl;
       
 34903 				}
       
 34904 
       
 34905 				if (settings.multiple) {
       
 34906 					ctrl.active(!ctrl.active());
       
 34907 				} else {
       
 34908 					self.value(e.control.settings.value);
       
 34909 				}
       
 34910 
       
 34911 				lastItemCtrl = ctrl;
       
 34912 			});
       
 34913 		},
       
 34914 
       
 34915 		/**
       
 34916 		 * Getter/setter function for the control value.
       
 34917 		 *
       
 34918 		 * @method value
       
 34919 		 * @param {String} [value] Value to be set.
       
 34920 		 * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
       
 34921 		 */
       
 34922 		value: function(value) {
       
 34923 			var self = this, active, selectedText, menu;
       
 34924 
       
 34925 			function activateByValue(menu, value) {
       
 34926 				menu.items().each(function(ctrl) {
       
 34927 					active = ctrl.value() === value;
       
 34928 
       
 34929 					if (active) {
       
 34930 						selectedText = selectedText || ctrl.text();
       
 34931 					}
       
 34932 
       
 34933 					ctrl.active(active);
       
 34934 
       
 34935 					if (ctrl.menu) {
       
 34936 						activateByValue(ctrl.menu, value);
       
 34937 					}
       
 34938 				});
       
 34939 			}
       
 34940 
       
 34941 			function setActiveValues(menuValues) {
       
 34942 				for (var i = 0; i < menuValues.length; i++) {
       
 34943 					active = menuValues[i].value == value;
       
 34944 
       
 34945 					if (active) {
       
 34946 						selectedText = selectedText || menuValues[i].text;
       
 34947 					}
       
 34948 
       
 34949 					menuValues[i].active = active;
       
 34950 
       
 34951 					if (menuValues[i].menu) {
       
 34952 						setActiveValues(menuValues[i].menu);
       
 34953 					}
       
 34954 				}
       
 34955 			}
       
 34956 
       
 34957 			if (typeof value != "undefined") {
       
 34958 				if (self.menu) {
       
 34959 					activateByValue(self.menu, value);
       
 34960 				} else {
       
 34961 					menu = self.settings.menu;
       
 34962 					setActiveValues(menu);
       
 34963 				}
       
 34964 
       
 34965 				self.text(selectedText || this.settings.text);
       
 34966 			}
       
 34967 
       
 34968 			return self._super(value);
       
 34969 		}
       
 34970 	});
       
 34971 });
       
 34972 
       
 34973 // Included from: js/tinymce/classes/ui/MenuItem.js
       
 34974 
       
 34975 /**
       
 34976  * MenuItem.js
       
 34977  *
       
 34978  * Copyright, Moxiecode Systems AB
       
 34979  * Released under LGPL License.
       
 34980  *
       
 34981  * License: http://www.tinymce.com/license
       
 34982  * Contributing: http://www.tinymce.com/contributing
       
 34983  */
       
 34984 
       
 34985 /**
       
 34986  * Creates a new menu item.
       
 34987  *
       
 34988  * @-x-less MenuItem.less
       
 34989  * @class tinymce.ui.MenuItem
       
 34990  * @extends tinymce.ui.Widget
       
 34991  */
       
 34992 define("tinymce/ui/MenuItem", [
       
 34993 	"tinymce/ui/Widget",
       
 34994 	"tinymce/ui/Factory",
       
 34995 	"tinymce/Env"
       
 34996 ], function(Widget, Factory, Env) {
       
 34997 	"use strict";
       
 34998 
       
 34999 	return Widget.extend({
       
 35000 		Defaults: {
       
 35001 			border: 0,
       
 35002 			role: 'menuitem'
       
 35003 		},
       
 35004 
       
 35005 		/**
       
 35006 		 * Constructs a instance with the specified settings.
       
 35007 		 *
       
 35008 		 * @constructor
       
 35009 		 * @param {Object} settings Name/value object with settings.
       
 35010 		 * @setting {Boolean} selectable Selectable menu.
       
 35011 		 * @setting {Array} menu Submenu array with items.
       
 35012 		 * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
       
 35013 		 */
       
 35014 		init: function(settings) {
       
 35015 			var self = this;
       
 35016 
       
 35017 			self.hasPopup = true;
       
 35018 
       
 35019 			self._super(settings);
       
 35020 
       
 35021 			settings = self.settings;
       
 35022 
       
 35023 			self.addClass('menu-item');
       
 35024 
       
 35025 			if (settings.menu) {
       
 35026 				self.addClass('menu-item-expand');
       
 35027 			}
       
 35028 
       
 35029 			if (settings.preview) {
       
 35030 				self.addClass('menu-item-preview');
       
 35031 			}
       
 35032 
       
 35033 			if (self._text === '-' || self._text === '|') {
       
 35034 				self.addClass('menu-item-sep');
       
 35035 				self.aria('role', 'separator');
       
 35036 				self._text = '-';
       
 35037 			}
       
 35038 
       
 35039 			if (settings.selectable) {
       
 35040 				self.aria('role', 'menuitemcheckbox');
       
 35041 				self.addClass('menu-item-checkbox');
       
 35042 				settings.icon = 'selected';
       
 35043 			}
       
 35044 
       
 35045 			if (!settings.preview && !settings.selectable) {
       
 35046 				self.addClass('menu-item-normal');
       
 35047 			}
       
 35048 
       
 35049 			self.on('mousedown', function(e) {
       
 35050 				e.preventDefault();
       
 35051 			});
       
 35052 
       
 35053 			if (settings.menu && !settings.ariaHideMenu) {
       
 35054 				self.aria('haspopup', true);
       
 35055 			}
       
 35056 		},
       
 35057 
       
 35058 		/**
       
 35059 		 * Returns true/false if the menuitem has sub menu.
       
 35060 		 *
       
 35061 		 * @method hasMenus
       
 35062 		 * @return {Boolean} True/false state if it has submenu.
       
 35063 		 */
       
 35064 		hasMenus: function() {
       
 35065 			return !!this.settings.menu;
       
 35066 		},
       
 35067 
       
 35068 		/**
       
 35069 		 * Shows the menu for the menu item.
       
 35070 		 *
       
 35071 		 * @method showMenu
       
 35072 		 */
       
 35073 		showMenu: function() {
       
 35074 			var self = this, settings = self.settings, menu, parent = self.parent();
       
 35075 
       
 35076 			parent.items().each(function(ctrl) {
       
 35077 				if (ctrl !== self) {
       
 35078 					ctrl.hideMenu();
       
 35079 				}
       
 35080 			});
       
 35081 
       
 35082 			if (settings.menu) {
       
 35083 				menu = self.menu;
       
 35084 
       
 35085 				if (!menu) {
       
 35086 					menu = settings.menu;
       
 35087 
       
 35088 					// Is menu array then auto constuct menu control
       
 35089 					if (menu.length) {
       
 35090 						menu = {
       
 35091 							type: 'menu',
       
 35092 							items: menu
       
 35093 						};
       
 35094 					} else {
       
 35095 						menu.type = menu.type || 'menu';
       
 35096 					}
       
 35097 
       
 35098 					if (parent.settings.itemDefaults) {
       
 35099 						menu.itemDefaults = parent.settings.itemDefaults;
       
 35100 					}
       
 35101 
       
 35102 					menu = self.menu = Factory.create(menu).parent(self).renderTo();
       
 35103 					menu.reflow();
       
 35104 					menu.on('cancel', function(e) {
       
 35105 						e.stopPropagation();
       
 35106 						self.focus();
       
 35107 						menu.hide();
       
 35108 					});
       
 35109 					menu.on('show hide', function(e) {
       
 35110 						e.control.items().each(function(ctrl) {
       
 35111 							ctrl.active(ctrl.settings.selected);
       
 35112 						});
       
 35113 					}).fire('show');
       
 35114 
       
 35115 					menu.on('hide', function(e) {
       
 35116 						if (e.control === menu) {
       
 35117 							self.removeClass('selected');
       
 35118 						}
       
 35119 					});
       
 35120 
       
 35121 					menu.submenu = true;
       
 35122 				} else {
       
 35123 					menu.show();
       
 35124 				}
       
 35125 
       
 35126 				menu._parentMenu = parent;
       
 35127 
       
 35128 				menu.addClass('menu-sub');
       
 35129 
       
 35130 				var rel = menu.testMoveRel(
       
 35131 					self.getEl(),
       
 35132 					self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
       
 35133 				);
       
 35134 
       
 35135 				menu.moveRel(self.getEl(), rel);
       
 35136 				menu.rel = rel;
       
 35137 
       
 35138 				rel = 'menu-sub-' + rel;
       
 35139 				menu.removeClass(menu._lastRel);
       
 35140 				menu.addClass(rel);
       
 35141 				menu._lastRel = rel;
       
 35142 
       
 35143 				self.addClass('selected');
       
 35144 				self.aria('expanded', true);
       
 35145 			}
       
 35146 		},
       
 35147 
       
 35148 		/**
       
 35149 		 * Hides the menu for the menu item.
       
 35150 		 *
       
 35151 		 * @method hideMenu
       
 35152 		 */
       
 35153 		hideMenu: function() {
       
 35154 			var self = this;
       
 35155 
       
 35156 			if (self.menu) {
       
 35157 				self.menu.items().each(function(item) {
       
 35158 					if (item.hideMenu) {
       
 35159 						item.hideMenu();
       
 35160 					}
       
 35161 				});
       
 35162 
       
 35163 				self.menu.hide();
       
 35164 				self.aria('expanded', false);
       
 35165 			}
       
 35166 
       
 35167 			return self;
       
 35168 		},
       
 35169 
       
 35170 		/**
       
 35171 		 * Renders the control as a HTML string.
       
 35172 		 *
       
 35173 		 * @method renderHtml
       
 35174 		 * @return {String} HTML representing the control.
       
 35175 		 */
       
 35176 		renderHtml: function() {
       
 35177 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
       
 35178 			var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
       
 35179 
       
 35180 			// Converts shortcut format to Mac/PC variants
       
 35181 			function convertShortcut(shortcut) {
       
 35182 				var i, value, replace = {};
       
 35183 
       
 35184 				if (Env.mac) {
       
 35185 					replace = {
       
 35186 						alt: '&#x2325;',
       
 35187 						ctrl: '&#x2318;',
       
 35188 						shift: '&#x21E7;',
       
 35189 						meta: '&#x2318;'
       
 35190 					};
       
 35191 				} else {
       
 35192 					replace = {
       
 35193 						meta: 'Ctrl'
       
 35194 					};
       
 35195 				}
       
 35196 
       
 35197 				shortcut = shortcut.split('+');
       
 35198 
       
 35199 				for (i = 0; i < shortcut.length; i++) {
       
 35200 					value = replace[shortcut[i].toLowerCase()];
       
 35201 
       
 35202 					if (value) {
       
 35203 						shortcut[i] = value;
       
 35204 					}
       
 35205 				}
       
 35206 
       
 35207 				return shortcut.join('+');
       
 35208 			}
       
 35209 
       
 35210 			if (icon) {
       
 35211 				self.parent().addClass('menu-has-icons');
       
 35212 			}
       
 35213 
       
 35214 			if (settings.image) {
       
 35215 				icon = 'none';
       
 35216 				image = ' style="background-image: url(\'' + settings.image + '\')"';
       
 35217 			}
       
 35218 
       
 35219 			if (shortcut) {
       
 35220 				shortcut = convertShortcut(shortcut);
       
 35221 			}
       
 35222 
       
 35223 			icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
       
 35224 
       
 35225 			return (
       
 35226 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
       
 35227 					(text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
       
 35228 					(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
       
 35229 					(shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
       
 35230 					(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
       
 35231 				'</div>'
       
 35232 			);
       
 35233 		},
       
 35234 
       
 35235 		/**
       
 35236 		 * Gets invoked after the control has been rendered.
       
 35237 		 *
       
 35238 		 * @method postRender
       
 35239 		 */
       
 35240 		postRender: function() {
       
 35241 			var self = this, settings = self.settings;
       
 35242 
       
 35243 			var textStyle = settings.textStyle;
       
 35244 			if (typeof textStyle == "function") {
       
 35245 				textStyle = textStyle.call(this);
       
 35246 			}
       
 35247 
       
 35248 			if (textStyle) {
       
 35249 				var textElm = self.getEl('text');
       
 35250 				if (textElm) {
       
 35251 					textElm.setAttribute('style', textStyle);
       
 35252 				}
       
 35253 			}
       
 35254 
       
 35255 			self.on('mouseenter click', function(e) {
       
 35256 				if (e.control === self) {
       
 35257 					if (!settings.menu && e.type === 'click') {
       
 35258 						self.fire('select');
       
 35259 						self.parent().hideAll();
       
 35260 					} else {
       
 35261 						self.showMenu();
       
 35262 
       
 35263 						if (e.aria) {
       
 35264 							self.menu.focus(true);
       
 35265 						}
       
 35266 					}
       
 35267 				}
       
 35268 			});
       
 35269 
       
 35270 			self._super();
       
 35271 
       
 35272 			return self;
       
 35273 		},
       
 35274 
       
 35275 		active: function(state) {
       
 35276 			if (typeof state != "undefined") {
       
 35277 				this.aria('checked', state);
       
 35278 			}
       
 35279 
       
 35280 			return this._super(state);
       
 35281 		},
       
 35282 
       
 35283 		/**
       
 35284 		 * Removes the control and it's menus.
       
 35285 		 *
       
 35286 		 * @method remove
       
 35287 		 */
       
 35288 		remove: function() {
       
 35289 			this._super();
       
 35290 
       
 35291 			if (this.menu) {
       
 35292 				this.menu.remove();
       
 35293 			}
       
 35294 		}
       
 35295 	});
       
 35296 });
       
 35297 
       
 35298 // Included from: js/tinymce/classes/ui/Menu.js
       
 35299 
       
 35300 /**
       
 35301  * Menu.js
       
 35302  *
       
 35303  * Copyright, Moxiecode Systems AB
       
 35304  * Released under LGPL License.
       
 35305  *
       
 35306  * License: http://www.tinymce.com/license
       
 35307  * Contributing: http://www.tinymce.com/contributing
       
 35308  */
       
 35309 
       
 35310 /**
       
 35311  * Creates a new menu.
       
 35312  *
       
 35313  * @-x-less Menu.less
       
 35314  * @class tinymce.ui.Menu
       
 35315  * @extends tinymce.ui.FloatPanel
       
 35316  */
       
 35317 define("tinymce/ui/Menu", [
       
 35318 	"tinymce/ui/FloatPanel",
       
 35319 	"tinymce/ui/MenuItem",
       
 35320 	"tinymce/util/Tools"
       
 35321 ], function(FloatPanel, MenuItem, Tools) {
       
 35322 	"use strict";
       
 35323 
       
 35324 	var Menu = FloatPanel.extend({
       
 35325 		Defaults: {
       
 35326 			defaultType: 'menuitem',
       
 35327 			border: 1,
       
 35328 			layout: 'stack',
       
 35329 			role: 'application',
       
 35330 			bodyRole: 'menu',
       
 35331 			ariaRoot: true
       
 35332 		},
       
 35333 
       
 35334 		/**
       
 35335 		 * Constructs a instance with the specified settings.
       
 35336 		 *
       
 35337 		 * @constructor
       
 35338 		 * @param {Object} settings Name/value object with settings.
       
 35339 		 */
       
 35340 		init: function(settings) {
       
 35341 			var self = this;
       
 35342 
       
 35343 			settings.autohide = true;
       
 35344 			settings.constrainToViewport = true;
       
 35345 
       
 35346 			if (settings.itemDefaults) {
       
 35347 				var items = settings.items, i = items.length;
       
 35348 
       
 35349 				while (i--) {
       
 35350 					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
       
 35351 				}
       
 35352 			}
       
 35353 
       
 35354 			self._super(settings);
       
 35355 			self.addClass('menu');
       
 35356 		},
       
 35357 
       
 35358 		/**
       
 35359 		 * Repaints the control after a layout operation.
       
 35360 		 *
       
 35361 		 * @method repaint
       
 35362 		 */
       
 35363 		repaint: function() {
       
 35364 			this.toggleClass('menu-align', true);
       
 35365 
       
 35366 			this._super();
       
 35367 
       
 35368 			this.getEl().style.height = '';
       
 35369 			this.getEl('body').style.height = '';
       
 35370 
       
 35371 			return this;
       
 35372 		},
       
 35373 
       
 35374 		/**
       
 35375 		 * Hides/closes the menu.
       
 35376 		 *
       
 35377 		 * @method cancel
       
 35378 		 */
       
 35379 		cancel: function() {
       
 35380 			var self = this;
       
 35381 
       
 35382 			self.hideAll();
       
 35383 			self.fire('select');
       
 35384 		},
       
 35385 
       
 35386 		/**
       
 35387 		 * Hide menu and all sub menus.
       
 35388 		 *
       
 35389 		 * @method hideAll
       
 35390 		 */
       
 35391 		hideAll: function() {
       
 35392 			var self = this;
       
 35393 
       
 35394 			this.find('menuitem').exec('hideMenu');
       
 35395 
       
 35396 			return self._super();
       
 35397 		},
       
 35398 /*
       
 35399 		getContainerElm: function() {
       
 35400 			var doc = document, id = this.classPrefix + 'menucontainer';
       
 35401 
       
 35402 			var elm = doc.getElementById(id);
       
 35403 			if (!elm) {
       
 35404 				elm = doc.createElement('div');
       
 35405 				elm.id = id;
       
 35406 				elm.setAttribute('role', 'application');
       
 35407 				elm.className = this.classPrefix + '-reset';
       
 35408 				elm.style.position = 'absolute';
       
 35409 				elm.style.top = elm.style.left = '0';
       
 35410 				elm.style.overflow = 'visible';
       
 35411 				doc.body.appendChild(elm);
       
 35412 			}
       
 35413 
       
 35414 			return elm;
       
 35415 		},
       
 35416 */
       
 35417 		/**
       
 35418 		 * Invoked before the menu is rendered.
       
 35419 		 *
       
 35420 		 * @method preRender
       
 35421 		 */
       
 35422 		preRender: function() {
       
 35423 			var self = this;
       
 35424 
       
 35425 			self.items().each(function(ctrl) {
       
 35426 				var settings = ctrl.settings;
       
 35427 
       
 35428 				if (settings.icon || settings.selectable) {
       
 35429 					self._hasIcons = true;
       
 35430 					return false;
       
 35431 				}
       
 35432 			});
       
 35433 
       
 35434 			return self._super();
       
 35435 		}
       
 35436 	});
       
 35437 
       
 35438 	return Menu;
       
 35439 });
       
 35440 
       
 35441 // Included from: js/tinymce/classes/ui/Radio.js
       
 35442 
       
 35443 /**
       
 35444  * Radio.js
       
 35445  *
       
 35446  * Copyright, Moxiecode Systems AB
       
 35447  * Released under LGPL License.
       
 35448  *
       
 35449  * License: http://www.tinymce.com/license
       
 35450  * Contributing: http://www.tinymce.com/contributing
       
 35451  */
       
 35452 
       
 35453 /**
       
 35454  * Creates a new radio button.
       
 35455  *
       
 35456  * @-x-less Radio.less
       
 35457  * @class tinymce.ui.Radio
       
 35458  * @extends tinymce.ui.Checkbox
       
 35459  */
       
 35460 define("tinymce/ui/Radio", [
       
 35461 	"tinymce/ui/Checkbox"
       
 35462 ], function(Checkbox) {
       
 35463 	"use strict";
       
 35464 
       
 35465 	return Checkbox.extend({
       
 35466 		Defaults: {
       
 35467 			classes: "radio",
       
 35468 			role: "radio"
       
 35469 		}
       
 35470 	});
       
 35471 });
       
 35472 
       
 35473 // Included from: js/tinymce/classes/ui/ResizeHandle.js
       
 35474 
       
 35475 /**
       
 35476  * ResizeHandle.js
       
 35477  *
       
 35478  * Copyright, Moxiecode Systems AB
       
 35479  * Released under LGPL License.
       
 35480  *
       
 35481  * License: http://www.tinymce.com/license
       
 35482  * Contributing: http://www.tinymce.com/contributing
       
 35483  */
       
 35484 
       
 35485 /**
       
 35486  * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events.
       
 35487  *
       
 35488  * @-x-less ResizeHandle.less
       
 35489  * @class tinymce.ui.ResizeHandle
       
 35490  * @extends tinymce.ui.Widget
       
 35491  */
       
 35492 define("tinymce/ui/ResizeHandle", [
       
 35493 	"tinymce/ui/Widget",
       
 35494 	"tinymce/ui/DragHelper"
       
 35495 ], function(Widget, DragHelper) {
       
 35496 	"use strict";
       
 35497 
       
 35498 	return Widget.extend({
       
 35499 		/**
       
 35500 		 * Renders the control as a HTML string.
       
 35501 		 *
       
 35502 		 * @method renderHtml
       
 35503 		 * @return {String} HTML representing the control.
       
 35504 		 */
       
 35505 		renderHtml: function() {
       
 35506 			var self = this, prefix = self.classPrefix;
       
 35507 
       
 35508 			self.addClass('resizehandle');
       
 35509 
       
 35510 			if (self.settings.direction == "both") {
       
 35511 				self.addClass('resizehandle-both');
       
 35512 			}
       
 35513 
       
 35514 			self.canFocus = false;
       
 35515 
       
 35516 			return (
       
 35517 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
       
 35518 					'<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' +
       
 35519 				'</div>'
       
 35520 			);
       
 35521 		},
       
 35522 
       
 35523 		/**
       
 35524 		 * Called after the control has been rendered.
       
 35525 		 *
       
 35526 		 * @method postRender
       
 35527 		 */
       
 35528 		postRender: function() {
       
 35529 			var self = this;
       
 35530 
       
 35531 			self._super();
       
 35532 
       
 35533 			self.resizeDragHelper = new DragHelper(this._id, {
       
 35534 				start: function() {
       
 35535 					self.fire('ResizeStart');
       
 35536 				},
       
 35537 
       
 35538 				drag: function(e) {
       
 35539 					if (self.settings.direction != "both") {
       
 35540 						e.deltaX = 0;
       
 35541 					}
       
 35542 
       
 35543 					self.fire('Resize', e);
       
 35544 				},
       
 35545 
       
 35546 				stop: function() {
       
 35547 					self.fire('ResizeEnd');
       
 35548 				}
       
 35549 			});
       
 35550 		},
       
 35551 
       
 35552 		remove: function() {
       
 35553 			if (this.resizeDragHelper) {
       
 35554 				this.resizeDragHelper.destroy();
       
 35555 			}
       
 35556 
       
 35557 			return this._super();
       
 35558 		}
       
 35559 	});
       
 35560 });
       
 35561 
       
 35562 // Included from: js/tinymce/classes/ui/Spacer.js
       
 35563 
       
 35564 /**
       
 35565  * Spacer.js
       
 35566  *
       
 35567  * Copyright, Moxiecode Systems AB
       
 35568  * Released under LGPL License.
       
 35569  *
       
 35570  * License: http://www.tinymce.com/license
       
 35571  * Contributing: http://www.tinymce.com/contributing
       
 35572  */
       
 35573 
       
 35574 /**
       
 35575  * Creates a spacer. This control is used in flex layouts for example.
       
 35576  *
       
 35577  * @-x-less Spacer.less
       
 35578  * @class tinymce.ui.Spacer
       
 35579  * @extends tinymce.ui.Widget
       
 35580  */
       
 35581 define("tinymce/ui/Spacer", [
       
 35582 	"tinymce/ui/Widget"
       
 35583 ], function(Widget) {
       
 35584 	"use strict";
       
 35585 
       
 35586 	return Widget.extend({
       
 35587 		/**
       
 35588 		 * Renders the control as a HTML string.
       
 35589 		 *
       
 35590 		 * @method renderHtml
       
 35591 		 * @return {String} HTML representing the control.
       
 35592 		 */
       
 35593 		renderHtml: function() {
       
 35594 			var self = this;
       
 35595 
       
 35596 			self.addClass('spacer');
       
 35597 			self.canFocus = false;
       
 35598 
       
 35599 			return '<div id="' + self._id + '" class="' + self.classes() + '"></div>';
       
 35600 		}
       
 35601 	});
       
 35602 });
       
 35603 
       
 35604 // Included from: js/tinymce/classes/ui/SplitButton.js
       
 35605 
       
 35606 /**
       
 35607  * SplitButton.js
       
 35608  *
       
 35609  * Copyright, Moxiecode Systems AB
       
 35610  * Released under LGPL License.
       
 35611  *
       
 35612  * License: http://www.tinymce.com/license
       
 35613  * Contributing: http://www.tinymce.com/contributing
       
 35614  */
       
 35615 
       
 35616 /**
       
 35617  * Creates a split button.
       
 35618  *
       
 35619  * @-x-less SplitButton.less
       
 35620  * @class tinymce.ui.SplitButton
       
 35621  * @extends tinymce.ui.MenuButton
       
 35622  */
       
 35623 define("tinymce/ui/SplitButton", [
       
 35624 	"tinymce/ui/MenuButton",
       
 35625 	"tinymce/ui/DomUtils"
       
 35626 ], function(MenuButton, DomUtils) {
       
 35627 	return MenuButton.extend({
       
 35628 		Defaults: {
       
 35629 			classes: "widget btn splitbtn",
       
 35630 			role: "button"
       
 35631 		},
       
 35632 
       
 35633 		/**
       
 35634 		 * Repaints the control after a layout operation.
       
 35635 		 *
       
 35636 		 * @method repaint
       
 35637 		 */
       
 35638 		repaint: function() {
       
 35639 			var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm;
       
 35640 
       
 35641 			self._super();
       
 35642 
       
 35643 			mainButtonElm = elm.firstChild;
       
 35644 			menuButtonElm = elm.lastChild;
       
 35645 
       
 35646 			DomUtils.css(mainButtonElm, {
       
 35647 				width: rect.w - DomUtils.getSize(menuButtonElm).width,
       
 35648 				height: rect.h - 2
       
 35649 			});
       
 35650 
       
 35651 			DomUtils.css(menuButtonElm, {
       
 35652 				height: rect.h - 2
       
 35653 			});
       
 35654 
       
 35655 			return self;
       
 35656 		},
       
 35657 
       
 35658 		/**
       
 35659 		 * Sets the active menu state.
       
 35660 		 *
       
 35661 		 * @private
       
 35662 		 */
       
 35663 		activeMenu: function(state) {
       
 35664 			var self = this;
       
 35665 
       
 35666 			DomUtils.toggleClass(self.getEl().lastChild, self.classPrefix + 'active', state);
       
 35667 		},
       
 35668 
       
 35669 		/**
       
 35670 		 * Renders the control as a HTML string.
       
 35671 		 *
       
 35672 		 * @method renderHtml
       
 35673 		 * @return {String} HTML representing the control.
       
 35674 		 */
       
 35675 		renderHtml: function() {
       
 35676 			var self = this, id = self._id, prefix = self.classPrefix, image;
       
 35677 			var icon = self.settings.icon;
       
 35678 
       
 35679 			image = self.settings.image;
       
 35680 			if (image) {
       
 35681 				icon = 'none';
       
 35682 
       
 35683 				// Support for [high dpi, low dpi] image sources
       
 35684 				if (typeof image != "string") {
       
 35685 					image = window.getSelection ? image[0] : image[1];
       
 35686 				}
       
 35687 
       
 35688 				image = ' style="background-image: url(\'' + image + '\')"';
       
 35689 			} else {
       
 35690 				image = '';
       
 35691 			}
       
 35692 
       
 35693 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
       
 35694 
       
 35695 			return (
       
 35696 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1">' +
       
 35697 					'<button type="button" hidefocus="1" tabindex="-1">' +
       
 35698 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
       
 35699 						(self._text ? (icon ? ' ' : '') + self._text : '') +
       
 35700 					'</button>' +
       
 35701 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
       
 35702 						//(icon ? '<i class="' + icon + '"></i>' : '') +
       
 35703 						(self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') +
       
 35704 						' <i class="' + prefix + 'caret"></i>' +
       
 35705 					'</button>' +
       
 35706 				'</div>'
       
 35707 			);
       
 35708 		},
       
 35709 
       
 35710 		/**
       
 35711 		 * Called after the control has been rendered.
       
 35712 		 *
       
 35713 		 * @method postRender
       
 35714 		 */
       
 35715 		postRender: function() {
       
 35716 			var self = this, onClickHandler = self.settings.onclick;
       
 35717 
       
 35718 			self.on('click', function(e) {
       
 35719 				var node = e.target;
       
 35720 
       
 35721 				if (e.control == this) {
       
 35722 					// Find clicks that is on the main button
       
 35723 					while (node) {
       
 35724 						if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) {
       
 35725 							e.stopImmediatePropagation();
       
 35726 							onClickHandler.call(this, e);
       
 35727 							return;
       
 35728 						}
       
 35729 
       
 35730 						node = node.parentNode;
       
 35731 					}
       
 35732 				}
       
 35733 			});
       
 35734 
       
 35735 			delete self.settings.onclick;
       
 35736 
       
 35737 			return self._super();
       
 35738 		}
       
 35739 	});
       
 35740 });
       
 35741 
       
 35742 // Included from: js/tinymce/classes/ui/StackLayout.js
       
 35743 
       
 35744 /**
       
 35745  * StackLayout.js
       
 35746  *
       
 35747  * Copyright, Moxiecode Systems AB
       
 35748  * Released under LGPL License.
       
 35749  *
       
 35750  * License: http://www.tinymce.com/license
       
 35751  * Contributing: http://www.tinymce.com/contributing
       
 35752  */
       
 35753 
       
 35754 /**
       
 35755  * This layout uses the browsers layout when the items are blocks.
       
 35756  *
       
 35757  * @-x-less StackLayout.less
       
 35758  * @class tinymce.ui.StackLayout
       
 35759  * @extends tinymce.ui.FlowLayout
       
 35760  */
       
 35761 define("tinymce/ui/StackLayout", [
       
 35762 	"tinymce/ui/FlowLayout"
       
 35763 ], function(FlowLayout) {
       
 35764 	"use strict";
       
 35765 
       
 35766 	return FlowLayout.extend({
       
 35767 		Defaults: {
       
 35768 			containerClass: 'stack-layout',
       
 35769 			controlClass: 'stack-layout-item',
       
 35770 			endClass: 'break'
       
 35771 		}
       
 35772 	});
       
 35773 });
       
 35774 
       
 35775 // Included from: js/tinymce/classes/ui/TabPanel.js
       
 35776 
       
 35777 /**
       
 35778  * TabPanel.js
       
 35779  *
       
 35780  * Copyright, Moxiecode Systems AB
       
 35781  * Released under LGPL License.
       
 35782  *
       
 35783  * License: http://www.tinymce.com/license
       
 35784  * Contributing: http://www.tinymce.com/contributing
       
 35785  */
       
 35786 
       
 35787 /**
       
 35788  * Creates a tab panel control.
       
 35789  *
       
 35790  * @-x-less TabPanel.less
       
 35791  * @class tinymce.ui.TabPanel
       
 35792  * @extends tinymce.ui.Panel
       
 35793  *
       
 35794  * @setting {Number} activeTab Active tab index.
       
 35795  */
       
 35796 define("tinymce/ui/TabPanel", [
       
 35797 	"tinymce/ui/Panel",
       
 35798 	"tinymce/ui/DomUtils"
       
 35799 ], function(Panel, DomUtils) {
       
 35800 	"use strict";
       
 35801 
       
 35802 	return Panel.extend({
       
 35803 		Defaults: {
       
 35804 			layout: 'absolute',
       
 35805 			defaults: {
       
 35806 				type: 'panel'
       
 35807 			}
       
 35808 		},
       
 35809 
       
 35810 		/**
       
 35811 		 * Activates the specified tab by index.
       
 35812 		 *
       
 35813 		 * @method activateTab
       
 35814 		 * @param {Number} idx Index of the tab to activate.
       
 35815 		 */
       
 35816 		activateTab: function(idx) {
       
 35817 			var activeTabElm;
       
 35818 
       
 35819 			if (this.activeTabId) {
       
 35820 				activeTabElm = this.getEl(this.activeTabId);
       
 35821 				DomUtils.removeClass(activeTabElm, this.classPrefix + 'active');
       
 35822 				activeTabElm.setAttribute('aria-selected', "false");
       
 35823 			}
       
 35824 
       
 35825 			this.activeTabId = 't' + idx;
       
 35826 
       
 35827 			activeTabElm = this.getEl('t' + idx);
       
 35828 			activeTabElm.setAttribute('aria-selected', "true");
       
 35829 			DomUtils.addClass(activeTabElm, this.classPrefix + 'active');
       
 35830 
       
 35831 			this.items()[idx].show().fire('showtab');
       
 35832 			this.reflow();
       
 35833 
       
 35834 			this.items().each(function(item, i) {
       
 35835 				if (idx != i) {
       
 35836 					item.hide();
       
 35837 				}
       
 35838 			});
       
 35839 		},
       
 35840 
       
 35841 		/**
       
 35842 		 * Renders the control as a HTML string.
       
 35843 		 *
       
 35844 		 * @method renderHtml
       
 35845 		 * @return {String} HTML representing the control.
       
 35846 		 */
       
 35847 		renderHtml: function() {
       
 35848 			var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix;
       
 35849 
       
 35850 			self.preRender();
       
 35851 			layout.preRender(self);
       
 35852 
       
 35853 			self.items().each(function(ctrl, i) {
       
 35854 				var id = self._id + '-t' + i;
       
 35855 
       
 35856 				ctrl.aria('role', 'tabpanel');
       
 35857 				ctrl.aria('labelledby', id);
       
 35858 
       
 35859 				tabsHtml += (
       
 35860 					'<div id="' + id + '" class="' + prefix + 'tab" ' +
       
 35861 						'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' +
       
 35862 						self.encode(ctrl.settings.title) +
       
 35863 					'</div>'
       
 35864 				);
       
 35865 			});
       
 35866 
       
 35867 			return (
       
 35868 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
       
 35869 					'<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' +
       
 35870 						tabsHtml +
       
 35871 					'</div>' +
       
 35872 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
       
 35873 						layout.renderHtml(self) +
       
 35874 					'</div>' +
       
 35875 				'</div>'
       
 35876 			);
       
 35877 		},
       
 35878 
       
 35879 		/**
       
 35880 		 * Called after the control has been rendered.
       
 35881 		 *
       
 35882 		 * @method postRender
       
 35883 		 */
       
 35884 		postRender: function() {
       
 35885 			var self = this;
       
 35886 
       
 35887 			self._super();
       
 35888 
       
 35889 			self.settings.activeTab = self.settings.activeTab || 0;
       
 35890 			self.activateTab(self.settings.activeTab);
       
 35891 
       
 35892 			this.on('click', function(e) {
       
 35893 				var targetParent = e.target.parentNode;
       
 35894 
       
 35895 				if (e.target.parentNode.id == self._id + '-head') {
       
 35896 					var i = targetParent.childNodes.length;
       
 35897 
       
 35898 					while (i--) {
       
 35899 						if (targetParent.childNodes[i] == e.target) {
       
 35900 							self.activateTab(i);
       
 35901 						}
       
 35902 					}
       
 35903 				}
       
 35904 			});
       
 35905 		},
       
 35906 
       
 35907 		/**
       
 35908 		 * Initializes the current controls layout rect.
       
 35909 		 * This will be executed by the layout managers to determine the
       
 35910 		 * default minWidth/minHeight etc.
       
 35911 		 *
       
 35912 		 * @method initLayoutRect
       
 35913 		 * @return {Object} Layout rect instance.
       
 35914 		 */
       
 35915 		initLayoutRect: function() {
       
 35916 			var self = this, rect, minW, minH;
       
 35917 
       
 35918 			minW = DomUtils.getSize(self.getEl('head')).width;
       
 35919 			minW = minW < 0 ? 0 : minW;
       
 35920 			minH = 0;
       
 35921 
       
 35922 			self.items().each(function(item) {
       
 35923 				minW = Math.max(minW, item.layoutRect().minW);
       
 35924 				minH = Math.max(minH, item.layoutRect().minH);
       
 35925 			});
       
 35926 
       
 35927 			self.items().each(function(ctrl) {
       
 35928 				ctrl.settings.x = 0;
       
 35929 				ctrl.settings.y = 0;
       
 35930 				ctrl.settings.w = minW;
       
 35931 				ctrl.settings.h = minH;
       
 35932 
       
 35933 				ctrl.layoutRect({
       
 35934 					x: 0,
       
 35935 					y: 0,
       
 35936 					w: minW,
       
 35937 					h: minH
       
 35938 				});
       
 35939 			});
       
 35940 
       
 35941 			var headH = DomUtils.getSize(self.getEl('head')).height;
       
 35942 
       
 35943 			self.settings.minWidth = minW;
       
 35944 			self.settings.minHeight = minH + headH;
       
 35945 
       
 35946 			rect = self._super();
       
 35947 			rect.deltaH += headH;
       
 35948 			rect.innerH = rect.h - rect.deltaH;
       
 35949 
       
 35950 			return rect;
       
 35951 		}
       
 35952 	});
       
 35953 });
       
 35954 
       
 35955 // Included from: js/tinymce/classes/ui/TextBox.js
       
 35956 
       
 35957 /**
       
 35958  * TextBox.js
       
 35959  *
       
 35960  * Copyright, Moxiecode Systems AB
       
 35961  * Released under LGPL License.
       
 35962  *
       
 35963  * License: http://www.tinymce.com/license
       
 35964  * Contributing: http://www.tinymce.com/contributing
       
 35965  */
       
 35966 
       
 35967 /**
       
 35968  * Creates a new textbox.
       
 35969  *
       
 35970  * @-x-less TextBox.less
       
 35971  * @class tinymce.ui.TextBox
       
 35972  * @extends tinymce.ui.Widget
       
 35973  */
       
 35974 define("tinymce/ui/TextBox", [
       
 35975 	"tinymce/ui/Widget",
       
 35976 	"tinymce/ui/DomUtils"
       
 35977 ], function(Widget, DomUtils) {
       
 35978 	"use strict";
       
 35979 
       
 35980 	return Widget.extend({
       
 35981 		/**
       
 35982 		 * Constructs a instance with the specified settings.
       
 35983 		 *
       
 35984 		 * @constructor
       
 35985 		 * @param {Object} settings Name/value object with settings.
       
 35986 		 * @setting {Boolean} multiline True if the textbox is a multiline control.
       
 35987 		 * @setting {Number} maxLength Max length for the textbox.
       
 35988 		 * @setting {Number} size Size of the textbox in characters.
       
 35989 		 */
       
 35990 		init: function(settings) {
       
 35991 			var self = this;
       
 35992 
       
 35993 			self._super(settings);
       
 35994 
       
 35995 			self._value = settings.value || '';
       
 35996 			self.addClass('textbox');
       
 35997 
       
 35998 			if (settings.multiline) {
       
 35999 				self.addClass('multiline');
       
 36000 			} else {
       
 36001 				// TODO: Rework this
       
 36002 				self.on('keydown', function(e) {
       
 36003 					if (e.keyCode == 13) {
       
 36004 						self.parents().reverse().each(function(ctrl) {
       
 36005 							e.preventDefault();
       
 36006 
       
 36007 							if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
       
 36008 								ctrl.fire('submit', {data: ctrl.toJSON()});
       
 36009 								return false;
       
 36010 							}
       
 36011 						});
       
 36012 					}
       
 36013 				});
       
 36014 			}
       
 36015 		},
       
 36016 
       
 36017 		/**
       
 36018 		 * Getter/setter function for the disabled state.
       
 36019 		 *
       
 36020 		 * @method value
       
 36021 		 * @param {Boolean} [state] State to be set.
       
 36022 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
       
 36023 		 */
       
 36024 		disabled: function(state) {
       
 36025 			var self = this;
       
 36026 
       
 36027 			if (self._rendered && typeof state != 'undefined') {
       
 36028 				self.getEl().disabled = state;
       
 36029 			}
       
 36030 
       
 36031 			return self._super(state);
       
 36032 		},
       
 36033 
       
 36034 		/**
       
 36035 		 * Getter/setter function for the control value.
       
 36036 		 *
       
 36037 		 * @method value
       
 36038 		 * @param {String} [value] Value to be set.
       
 36039 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
       
 36040 		 */
       
 36041 		value: function(value) {
       
 36042 			var self = this;
       
 36043 
       
 36044 			if (typeof value != "undefined") {
       
 36045 				self._value = value;
       
 36046 
       
 36047 				if (self._rendered) {
       
 36048 					self.getEl().value = value;
       
 36049 				}
       
 36050 
       
 36051 				return self;
       
 36052 			}
       
 36053 
       
 36054 			if (self._rendered) {
       
 36055 				return self.getEl().value;
       
 36056 			}
       
 36057 
       
 36058 			return self._value;
       
 36059 		},
       
 36060 
       
 36061 		/**
       
 36062 		 * Repaints the control after a layout operation.
       
 36063 		 *
       
 36064 		 * @method repaint
       
 36065 		 */
       
 36066 		repaint: function() {
       
 36067 			var self = this, style, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
       
 36068 
       
 36069 			style = self.getEl().style;
       
 36070 			rect = self._layoutRect;
       
 36071 			lastRepaintRect = self._lastRepaintRect || {};
       
 36072 
       
 36073 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
       
 36074 			var doc = document;
       
 36075 			if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
       
 36076 				style.lineHeight = (rect.h - borderH) + 'px';
       
 36077 			}
       
 36078 
       
 36079 			borderBox = self._borderBox;
       
 36080 			borderW = borderBox.left + borderBox.right + 8;
       
 36081 			borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0);
       
 36082 
       
 36083 			if (rect.x !== lastRepaintRect.x) {
       
 36084 				style.left = rect.x + 'px';
       
 36085 				lastRepaintRect.x = rect.x;
       
 36086 			}
       
 36087 
       
 36088 			if (rect.y !== lastRepaintRect.y) {
       
 36089 				style.top = rect.y + 'px';
       
 36090 				lastRepaintRect.y = rect.y;
       
 36091 			}
       
 36092 
       
 36093 			if (rect.w !== lastRepaintRect.w) {
       
 36094 				style.width = (rect.w - borderW) + 'px';
       
 36095 				lastRepaintRect.w = rect.w;
       
 36096 			}
       
 36097 
       
 36098 			if (rect.h !== lastRepaintRect.h) {
       
 36099 				style.height = (rect.h - borderH) + 'px';
       
 36100 				lastRepaintRect.h = rect.h;
       
 36101 			}
       
 36102 
       
 36103 			self._lastRepaintRect = lastRepaintRect;
       
 36104 			self.fire('repaint', {}, false);
       
 36105 
       
 36106 			return self;
       
 36107 		},
       
 36108 
       
 36109 		/**
       
 36110 		 * Renders the control as a HTML string.
       
 36111 		 *
       
 36112 		 * @method renderHtml
       
 36113 		 * @return {String} HTML representing the control.
       
 36114 		 */
       
 36115 		renderHtml: function() {
       
 36116 			var self = this, id = self._id, settings = self.settings, value = self.encode(self._value, false), extraAttrs = '';
       
 36117 
       
 36118 			if ("spellcheck" in settings) {
       
 36119 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
       
 36120 			}
       
 36121 
       
 36122 			if (settings.maxLength) {
       
 36123 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
       
 36124 			}
       
 36125 
       
 36126 			if (settings.size) {
       
 36127 				extraAttrs += ' size="' + settings.size + '"';
       
 36128 			}
       
 36129 
       
 36130 			if (settings.subtype) {
       
 36131 				extraAttrs += ' type="' + settings.subtype + '"';
       
 36132 			}
       
 36133 
       
 36134 			if (self.disabled()) {
       
 36135 				extraAttrs += ' disabled="disabled"';
       
 36136 			}
       
 36137 
       
 36138 			if (settings.multiline) {
       
 36139 				return (
       
 36140 					'<textarea id="' + id + '" class="' + self.classes() + '" ' +
       
 36141 					(settings.rows ? ' rows="' + settings.rows + '"' : '') +
       
 36142 					' hidefocus="1"' + extraAttrs + '>' + value +
       
 36143 					'</textarea>'
       
 36144 				);
       
 36145 			}
       
 36146 
       
 36147 			return '<input id="' + id + '" class="' + self.classes() + '" value="' + value + '" hidefocus="1"' + extraAttrs + ' />';
       
 36148 		},
       
 36149 
       
 36150 		/**
       
 36151 		 * Called after the control has been rendered.
       
 36152 		 *
       
 36153 		 * @method postRender
       
 36154 		 */
       
 36155 		postRender: function() {
       
 36156 			var self = this;
       
 36157 
       
 36158 			DomUtils.on(self.getEl(), 'change', function(e) {
       
 36159 				self.fire('change', e);
       
 36160 			});
       
 36161 
       
 36162 			return self._super();
       
 36163 		},
       
 36164 
       
 36165 		remove: function() {
       
 36166 			DomUtils.off(this.getEl());
       
 36167 			this._super();
       
 36168 		}
       
 36169 	});
       
 36170 });
       
 36171 
       
 36172 // Included from: js/tinymce/classes/ui/Throbber.js
       
 36173 
       
 36174 /**
       
 36175  * Throbber.js
       
 36176  *
       
 36177  * Copyright, Moxiecode Systems AB
       
 36178  * Released under LGPL License.
       
 36179  *
       
 36180  * License: http://www.tinymce.com/license
       
 36181  * Contributing: http://www.tinymce.com/contributing
       
 36182  */
       
 36183 
       
 36184 /**
       
 36185  * This class enables you to display a Throbber for any element.
       
 36186  *
       
 36187  * @-x-less Throbber.less
       
 36188  * @class tinymce.ui.Throbber
       
 36189  */
       
 36190 define("tinymce/ui/Throbber", [
       
 36191 	"tinymce/ui/DomUtils",
       
 36192 	"tinymce/ui/Control"
       
 36193 ], function(DomUtils, Control) {
       
 36194 	"use strict";
       
 36195 
       
 36196 	/**
       
 36197 	 * Constructs a new throbber.
       
 36198 	 *
       
 36199 	 * @constructor
       
 36200 	 * @param {Element} elm DOM Html element to display throbber in.
       
 36201 	 * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll.
       
 36202 	 */
       
 36203 	return function(elm, inline) {
       
 36204 		var self = this, state, classPrefix = Control.classPrefix;
       
 36205 
       
 36206 		/**
       
 36207 		 * Shows the throbber.
       
 36208 		 *
       
 36209 		 * @method show
       
 36210 		 * @param {Number} [time] Time to wait before showing.
       
 36211 		 * @return {tinymce.ui.Throbber} Current throbber instance.
       
 36212 		 */
       
 36213 		self.show = function(time) {
       
 36214 			self.hide();
       
 36215 
       
 36216 			state = true;
       
 36217 
       
 36218 			window.setTimeout(function() {
       
 36219 				if (state) {
       
 36220 					elm.appendChild(DomUtils.createFragment(
       
 36221 						'<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>'
       
 36222 					));
       
 36223 				}
       
 36224 			}, time || 0);
       
 36225 
       
 36226 			return self;
       
 36227 		};
       
 36228 
       
 36229 		/**
       
 36230 		 * Hides the throbber.
       
 36231 		 *
       
 36232 		 * @method hide
       
 36233 		 * @return {tinymce.ui.Throbber} Current throbber instance.
       
 36234 		 */
       
 36235 		self.hide = function() {
       
 36236 			var child = elm.lastChild;
       
 36237 
       
 36238 			if (child && child.className.indexOf('throbber') != -1) {
       
 36239 				child.parentNode.removeChild(child);
       
 36240 			}
       
 36241 
       
 36242 			state = false;
       
 36243 
       
 36244 			return self;
       
 36245 		};
       
 36246 	};
       
 36247 });
       
 36248 
       
 36249 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"]);
       
 36250 })(this);