PageSourceSearch

https://correspondant.lamanchelibre.fr/wpublic/js/tinymce/tinymce.js?20260922084155

js lamanchelibre.fr collected 2026-09-24 20:37:41 UTC 912,465 bytes, 34,160 lines download raw bytes

1// 4.0.12 (2013-12-18)
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
99define("tinymce/dom/EventUtils", [], function() {
100	"use strict";
101
102	var eventExpandoPrefix = "mce-data-";
103	var mouseEventRe = /^(?:mouse|contextmenu)|click/;
104	var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1};
105
106	/**
107	 * Binds a native event to a callback on the speified target.
108	 */
109	function addEvent(target, name, callback, capture) {
110		if (target.addEventListener) {
111			target.addEventListener(name, callback, capture || false);
112		} else if (target.attachEvent) {
113			target.attachEvent('on' + name, callback);
114		}
115	}
116
117	/**
118	 * Unbinds a native event callback on the specified target.
119	 */
120	function removeEvent(target, name, callback, capture) {
121		if (target.removeEventListener) {
122			target.removeEventListener(name, callback, capture || false);
123		} else if (target.detachEvent) {
124			target.detachEvent('on' + name, callback);
125		}
126	}
127
128	/**
129	 * Normalizes a native event object or just adds the event specific methods on a custom event.
130	 */
131	function fix(originalEvent, data) {
132		var name, event = data || {}, undef;
133
134		// Dummy function that gets replaced on the delegation state functions
135		function returnFalse() {
136			return false;
137		}
138
139		// Dummy function that gets replaced on the delegation state functions
140		function returnTrue() {
141			return true;
142		}
143
144		// Copy all properties from the original event
145		for (name in originalEvent) {
146			// layerX/layerY is deprecated in Chrome and produces a warning
147			if (!deprecated[name]) {
148				event[name] = originalEvent[name];
149			}
150		}
151
152		// Normalize target IE uses srcElement
153		if (!event.target) {
154			event.target = event.srcElement || document;
155		}
156
157		// Calculate pageX/Y if missing and clientX/Y available
158		if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
159			var eventDoc = event.target.ownerDocument || document;
160			var doc = eventDoc.documentElement;
161			var body = eventDoc.body;
162
163			event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
164				( doc && doc.clientLeft || body && body.clientLeft || 0);
165
166			event.pageY = originalEvent.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
167				( doc && doc.clientTop  || body && body.clientTop  || 0);
168		}
169
170		// Add preventDefault method
171		event.preventDefault = function() {
172			event.isDefaultPrevented = returnTrue;
173
174			// Execute preventDefault on the original event object
175			if (originalEvent) {
176				if (originalEvent.preventDefault) {
177					originalEvent.preventDefault();
178				} else {
179					originalEvent.returnValue = false; // IE
180				}
181			}
182		};
183
184		// Add stopPropagation
185		event.stopPropagation = function() {
186			event.isPropagationStopped = returnTrue;
187
188			// Execute stopPropagation on the original event object
189			if (originalEvent) {
190				if (originalEvent.stopPropagation) {
191					originalEvent.stopPropagation();
192				} else {
193					originalEvent.cancelBubble = true; // IE
194				}
195			 }
196		};
197
198		// Add stopImmediatePropagation
199		event.stopImmediatePropagation = function() {
200			event.isImmediatePropagationStopped = returnTrue;
201			event.stopPropagation();
202		};
203
204		// Add event delegation states
205		if (!event.isDefaultPrevented) {
206			event.isDefaultPrevented = returnFalse;
207			event.isPropagationStopped = returnFalse;
208			event.isImmediatePropagationStopped = returnFalse;
209		}
210
211		return event;
212	}
213
214	/**
215	 * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
216	 * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
217	 */
218	function bindOnReady(win, callback, eventUtils) {
219		var doc = win.document, event = {type: 'ready'};
220
221		if (eventUtils.domLoaded) {
222			callback(event);
223			return;
224		}
225
226		// Gets called when the DOM is ready
227		function readyHandler() {
228			if (!eventUtils.domLoaded) {
229				eventUtils.domLoaded = true;
230				callback(event);
231			}
232		}
233
234		function waitForDomLoaded() {
235			if (doc.readyState === "complete") {
236				removeEvent(doc, "readystatechange", waitForDomLoaded);
237				readyHandler();
238			}
239		}
240
241		function tryScroll() {
242			try {
243				// If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
244				// http://javascript.nwbox.com/IEContentLoaded/
245				doc.documentElement.doScroll("left");
246			} catch (ex) {
247				setTimeout(tryScroll, 0);
248				return;
249			}
250
251			readyHandler();
252		}
253
254		// Use W3C method
255		if (doc.addEventListener) {
256			if (doc.readyState === "complete") {
257				readyHandler();
258			} else {
259				addEvent(win, 'DOMContentLoaded', readyHandler);
260			}
261		} else {
262			// Use IE method
263			addEvent(doc, "readystatechange", waitForDomLoaded);
264
265			// Wait until we can scroll, when we can the DOM is initialized
266			if (doc.documentElement.doScroll && win === win.top) {
267				tryScroll();
268			}
269		}
270
271		// Fallback if any of the above methods should fail for some odd reason
272		addEvent(win, 'load', readyHandler);
273	}
274
275	/**
276	 * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
277	 */
278	function EventUtils() {
279		var self = this, events = {}
279, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
280
281		expando = eventExpandoPrefix + (+new Date()).toString(32);
282		hasMouseEnterLeave = "onmouseenter" in document.documentElement;
283		hasFocusIn = "onfocusin" in document.documentElement;
284		mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
285		count = 1;
286
287		// State if the DOMContentLoaded was executed or not
288		self.domLoaded = false;
289		self.events = events;
290
291		/**
292		 * Executes all event handler callbacks for a specific event.
293		 *
294		 * @private
295		 * @param {Event} evt Event object.
296		 * @param {String} id Expando id value to look for.
297		 */
298		function executeHandlers(evt, id) {
299			var callbackList, i, l, callback, container = events[id];
300
301			callbackList = container && container[evt.type];
302			if (callbackList) {
303				for (i = 0, l = callbackList.length; i < l; i++) {
304					callback = callbackList[i];
305
306					// Check if callback exists might be removed if a unbind is called inside the callback
307					if (callback && callback.func.call(callback.scope, evt) === false) {
308						evt.preventDefault();
309					}
310
311					// Should we stop propagation to immediate listeners
312					if (evt.isImmediatePropagationStopped()) {
313						return;
314					}
315				}
316			}
317		}
318
319		/**
320		 * Binds a callback to an event on the specified target.
321		 *
322		 * @method bind
323		 * @param {Object} target Target node/window or custom object.
324		 * @param {String} names Name of the event to bind.
325		 * @param {function} callback Callback function to execute when the event occurs.
326		 * @param {Object} scope Scope to call the callback function on, defaults to target.
327		 * @return {function} Callback function that got bound.
328		 */
329		self.bind = function(target, names, callback, scope) {
330			var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
331
332			// Native event handler function patches the event and executes the callbacks for the expando
333			function defaultNativeHandler(evt) {
334				executeHandlers(fix(evt || win.event), id);
335			}
336
337			// Don't bind to text nodes or comments
338			if (!target || target.nodeType === 3 || target.nodeType === 8) {
339				return;
340			}
341
342			// Create or get events id for the target
343			if (!target[expando]) {
344				id = count++;
345				target[expando] = id;
346				events[id] = {};
347			} else {
348				id = target[expando];
349			}
350
351			// Setup the specified scope or use the target as a default
352			scope = scope || target;
353
354			// Split names and bind each event, enables you to bind multiple events with one call
355			names = names.split(' ');
356			i = names.length;
357			while (i--) {
358				name = names[i];
359				nativeHandler = defaultNativeHandler;
360				fakeName = capture = false;
361
362				// Use ready instead of DOMContentLoaded
363				if (name === "DOMContentLoaded") {
364					name = "ready";
365				}
366
367				// DOM is already ready
368				if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
369					callback.call(scope, fix({type: name}));
370					continue;
371				}
372
373				// Handle mouseenter/mouseleaver
374				if (!hasMouseEnterLeave) {
375					fakeName = mouseEnterLeave[name];
376
377					if (fakeName) {
378						nativeHandler = function(evt) {
379							var current, related;
380
381							current = evt.currentTarget;
382							related = evt.relatedTarget;
383
384							// Check if related is inside the current target if it's not then the event should
385							// be ignored since it's a mouseover/mouseout inside the element
386							if (related && current.contains) {
387								// Use contains for performance
388								related = current.contains(related);
389							} else {
390								while (related && related !== current) {
391									related = related.parentNode;
392								}
393							}
394
395							// Fire fake event
396							if (!related) {
397								evt = fix(evt || win.event);
398								evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
399								evt.target = current;
400								executeHandlers(evt, id);
401							}
402						};
403					}
404				}
405
406				// Fake bubbeling of focusin/focusout
407				if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
408					capture = true;
409					fakeName = name === "focusin" ? "focus" : "blur";
410					nativeHandler = function(evt) {
411						evt = fix(evt || win.event);
412						evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
413						executeHandlers(evt, id);
414					};
415				}
416
417				// Setup callback list and bind native event
418				callbackList = events[id][name];
419				if (!callbackList) {
420					events[id][name] = callbackList = [{func: callback, scope: scope}];
421					callbackList.fakeName = fakeName;
422					callbackList.capture = capture;
423
424					// Add the nativeHandler to the callback list so that we can later unbind it
425					callbackList.nativeHandler = nativeHandler;
426
427					// Check if the target has native events support
428
429					if (name === "ready") {
430						bindOnReady(target, nativeHandler, self);
431					} else {
432						addEvent(target, fakeName || name, nativeHandler, capture);
433					}
434				} else {
435					if (name === "ready" && self.domLoaded) {
436						callback({type: name});
437					} else {
438						// If it already has an native handler then just push the callback
439						callbackList.push({func: callback, scope: scope});
440					}
441				}
442			}
443
444			target = callbackList = 0; // Clean memory for IE
445
446			return callback;
447		};
448
449		/**
450		 * Unbinds the specified event by name, name and callback or all events on the target.
451		 *
452		 * @method unbind
453		 * @param {Object} target Target node/window or custom object.
454		 * @param {String} names Optional event name to unbind.
455		 * @param {function} callback Optional callback function to unbind.
456		 * @return {EventUtils} Event utils instance.
457		 */
458		self.unbind = function(target, names, callback) {
459			var id, callbackList, i, ci, name, eventMap;
460
461			// Don't bind to text nodes or comments
462			if (!target || target.nodeType === 3 || target.nodeType === 8) {
463				return self;
464			}
465
466			// Unbind event or events if the target has the expando
467			id = target[expando];
468			if (id) {
469				eventMap = events[id];
470
471				// Specific callback
472				if (names) {
473					names = names.split(' ');
474					i = names.length;
475					while (i--) {
476						name = names[i];
477						callbackList = eventMap[name];
478
479						// Unbind the event if it exists in the map
480						if (callbackList) {
481							// Remove specified callback
482							if (callback) {
483								ci = callbackList.length;
484								while (ci--) {
485									if (callbackList[ci].func === callback) {
486										var nativeHandler = callbackList.nativeHandler;
487
488										// Clone callbackList since unbind inside a callback would otherwise break the handlers loop
489										callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
490										callbackList.nativeHandler = nativeHandler;
491
492										eventMap[name] = callbackList;
493									}
494								}
495							}
496
497							// Remove all callbacks if there isn't a specified callback or there is no callbacks left
498							if (!callback || callbackList.length === 0) {
499								delete eventMap[name];
500								removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
501							}
502						}
503					}
504				} else {
505					// All events for a specific element
506					for (name in eventMap) {
507						callbackList = eventMap[name];
508						removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
509					}
510
511					eventMap = {};
512				}
513
514				// Check if object is empty, if it isn't then we won't remove the expando map
515				for (name in eventMap) {
516					return self;
517				}
518
519				// Delete event object
520				delete events[id];
521
522				// Remove expando from target
523				try {
524					// IE will fail here since it can't delete properties from window
525					delete target[expando];
526				} catch (ex) {
527					// IE will set it to null
528					target[expando] = null;
529				}
530			}
531
532			return self;
533		};
534
535		/**
536		 * Fires the specified event on the specified target.
537		 *
538		 * @method fire
539		 * @param {Object} target Target node/window or custom object.
540		 * @param {String} name Event name to fire.
541		 * @param {Object} args Optional arguments to send to the observers.
542		 * @return {EventUtils} Event utils instance.
543		 */
544		self.fire = function(target, name, args) {
545			var id;
546
547			// Don't bind to text nodes or comments
548			if (!target || target.nodeType === 3 || target.nodeType === 8) {
549				return self;
550			}
551
552			// Build event object by patching the args
553			args = fix(null, args);
554			args.type = name;
555			args.target = target;
556
557			do {
558				// Found an expando that means there is listeners to execute
559				id = target[expando];
560				if (id) {
561					executeHandlers(args, id);
562				}
563
564				// Walk up the DOM
565				target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
566			} while (target && !args.isPropagationStopped());
567
568			return self;
569		};
570
571		/**
572		 * Removes all bound event listeners for the specified target. This will also remove any bound
573		 * listeners to child nodes within that target.
574		 *
575		 * @method clean
576		 * @param {Object} target Target node/window object.
577		 * @return {EventUtils} Event utils instance.
578		 */
579		self.clean = function(target) {
580			var i, children, unbind = self.unbind;
581
582			// Don't bind to text nodes or comments
583			if (!target || target.nodeType === 3 || target.nodeType === 8) {
584				return self;
585			}
586
587			// Unbind any element on the specificed target
588			if (target[expando]) {
589				unbind(target);
590			}
591
592			// Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
593			if (!target.getElementsByTagName) {
594				target = target.document;
595			}
596
597			// Remove events from each child element
598			if (target && target.getElementsByTagName) {
599				unbind(target);
600
601				children = target.getElementsByTagName('*');
602				i = children.length;
603				while (i--) {
604					target = children[i];
605
606					if (target[expando]) {
607						unbind(target);
608					}
609				}
610			}
611
612			return self;
613		};
614
615		/**
616		 * Destroys the event object. Call this on IE to remove memory leaks.
617		 */
618		self.destroy = function() {
619			events = {};
620		};
621
622		// Legacy function for canceling events
623		self.cancel = function(e) {
624			if (e) {
625				e.preventDefault();
626				e.stopImmediatePropagation();
627			}
628
629			return false;
630		};
631	}
632
633	EventUtils.Event = new EventUtils();
634	EventUtils.Event.bind(window, 'ready', function() {});
635
636	return EventUtils;
637});
638
639// Included from: js/tinymce/classes/dom/Sizzle.js
640
641/**
642 * Sizzle.js
643 *
644 * Copyright, Moxiecode Systems AB
645 * Released under LGPL License.
646 *
647 * License: http://www.tinymce.com/license
648 * Contributing: http://www.tinymce.com/contributing
649 *
650 * @ignore-file
651 */
652
653/*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */
654
655/*
656 * Sizzle CSS Selector Engine
657 *  Copyright, The Dojo Foundation
658 *  Released under the MIT, BSD, and GPL Licenses.
659 *  More information: http://sizzlejs.com/
660 */
661define("tinymce/dom/Sizzle", [], function() {
662var i,
663	cachedruns,
664	Expr,
665	getText,
666	isXML,
667	compile,
668	outermostContext,
669	recompare,
670	sortInput,
671
672	// Local document vars
673	setDocument,
674	document,
675	docElem,
676	documentIsHTML,
677	rbuggyQSA,
678	rbuggyMatches,
679	matches,
680	contains,
681
682	// Instance-specific data
683	expando = "sizzle" + -(new Date()),
684	preferredDoc = window.document,
685	support = {},
686	dirruns = 0,
687	done = 0,
688	classCache = createCache(),
689	tokenCache = createCache(),
690	compilerCache = createCache(),
691	hasDuplicate = false,
692	sortOrder = function() { return 0; },
693
694	// General-purpose constants
695	strundefined = typeof undefined,
696	MAX_NEGATIVE = 1 << 31,
697
698	// Array methods
699	arr = [],
700	pop = arr.pop,
701	push_native = arr.push,
702	push = arr.push,
703	slice = arr.slice,
704	// Use a stripped-down indexOf if we can't use a native one
705	indexOf = arr.indexOf || function( elem ) {
706		var i = 0,
707			len = this.length;
708		for ( ; i < len; i++ ) {
709			if ( this[i] === elem ) {
710				return i;
711			}
712		}
713		return -1;
714	},
715
716
717	// Regular expressions
718
719	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
720	whitespace = "[\\x20\\t\\r\\n\\f]",
721	// http://www.w3.org/TR/css3-syntax/#characters
722	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
723
724	// Loosely modeled on CSS identifier characters
725	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
726	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
727	identifier = characterEncoding.replace( "w", "w#" ),
728
729	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
730	operators = "([*^$|!~]?=)",
731	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
732		"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
733
734	// Prefer arguments quoted,
735	//   then not containing pseudos/brackets,
736	//   then attribute selectors/non-parenthetical expressions,
737	//   then anything else
738	// These preferences are here to reduce the number of selectors
739	//   needing tokenize in the PSEUDO preFilter
740	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
741
742	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
743	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
744
745	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
746	rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
747	rpseudo = new RegExp( pseudos ),
748	ridentifier = new RegExp( "^" + identifier + "$" ),
749
750	matchExpr = {
751		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
752		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
753		"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
754		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
755		"ATTR": new RegExp( "^" + attributes ),
756		"PSEUDO": new RegExp( "^" + pseudos ),
757		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
758			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
759			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
760		// For use in libraries implementing .is()
761		// We use this for POS matching in `select`
762		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
763			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
764	},
765
766	rsibling = /[\x20\t\r\n\f]*[+~]/,
767
768	rnative = /^[^{]+\{\s*\[native code/,
769
770	// Easily-parseable/retrievable ID or TAG or CLASS selectors
771	rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
772
773	rinputs = /^(?:input|select|textarea|button)$/i,
774	rheader = /^h\d$/i,
775
776	rescape = /'|\\/g,
777	rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
778
779	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
780	runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
781	funescape = function( _, escaped ) {
782		var high = "0x" + escaped - 0x10000;
783		// NaN means non-codepoint
784		return high !== high ?
785			escaped :
786			// BMP codepoint
787			high < 0 ?
788				String.fromCharCode( high + 0x10000 ) :
789				// Supplemental Plane codepoint (surrogate pair)
790				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
791	};
792
793// Optimize for push.apply( _, NodeList )
794try {
795	push.apply(
796		(arr = slice.call( preferredDoc.childNodes )),
797		preferredDoc.childNodes
798	);
799	// Support: Android<4.0
800	// Detect silently failing push.apply
801	arr[ preferredDoc.childNodes.length ].nodeType;
802} catch ( e ) {
803	push = { apply: arr.length ?
804
805		// Leverage slice if possible
806		function( target, els ) {
807			push_native.apply( target, slice.call(els) );
808		} :
809
810		// Support: IE<9
811		// Otherwise append directly
812		function( target, els ) {
813			var j = target.length,
814				i = 0;
815			// Can't trust NodeList.length
816			while ( (target[j++] = els[i++]) ) {}
817			target.length = j - 1;
818		}
819	};
820}
821
822/**
823 * For feature detection
824 * @param {Function} fn The function to test for native support
825 */
826function isNative( fn ) {
827	return rnative.test( fn + "" );
828}
829
830/**
831 * Create key-value caches of limited size
832 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
833 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
834 *	deleting the oldest entry
835 */
836function createCache() {
837	var cache,
838		keys = [];
839
840	cache = function( key, value ) {
841		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
842		if ( keys.push( key += " " ) > Expr.cacheLength ) {
843			// Only keep the most recent entries
844			delete cache[ keys.shift() ];
845		}
846		cache[ key ] = value;
847		return value;
848	};
849
850	return cache;
851}
852
853/**
854 * Mark a function for special use by Sizzle
855 * @param {Function} fn The function to mark
856 */
857function markFunction( fn ) {
858	fn[ expando ] = true;
859	return fn;
860}
861
862/**
863 * Support testing using an element
864 * @param {Function} fn Passed the created div and expects a boolean result
865 */
866function assert( fn ) {
867	var div = document.createElement("div");
868
869	try {
870		return !!fn( div );
871	} catch (e) {
872		return false;
873	} finally {
874		// release memory in IE
875		div = null;
876	}
877}
878
879function Sizzle( selector, context, results, seed ) {
880	var match, elem, m, nodeType,
881		// QSA vars
882		i, groups, old, nid, newContext, newSelector;
883
884	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
885		setDocument( context );
886	}
887
888	context = context || document;
889	results = results || [];
890
891	if ( !selector || typeof selector !== "string" ) {
892		return results;
893	}
894
895	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
896		return [];
897	}
898
899	if ( documentIsHTML && !seed ) {
900
901		// Shortcuts
902		if ( (match = rquickExpr.exec( selector )) ) {
903			// Speed-up: Sizzle("#ID")
904			if ( (m = match[1]) ) {
905				if ( nodeType === 9 ) {
906					elem = context.getElementById( m );
907					// Check parentNode to catch when Blackberry 4.6 returns
908					// nodes that are no longer in the document #6963
909					if ( elem && elem.parentNode ) {
910						// Handle the case where IE, Opera, and Webkit return items
911						// by name instead of ID
912						if ( elem.id === m ) {
913							results.push( elem );
914							return results;
915						}
916					} else {
917						return results;
918					}
919				} else {
920					// Context is not a document
921					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
922						contains( context, elem ) && elem.id === m ) {
923						results.push( elem );
924						return results;
925					}
926				}
927
928			// Speed-up: Sizzle("TAG")
929			} else if ( match[2] ) {
930				push.apply( results, context.getElementsByTagName( selector ) );
931				return results;
932
933			// Speed-up: Sizzle(".CLASS")
934			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
935				push.apply( results, context.getElementsByClassName( m ) );
936				return results;
937			}
938		}
939
940		// QSA path
941		if ( support.qsa && !rbuggyQSA.test(selector) ) {
942			old = true;
943			nid = expando;
944			newContext = context;
945			newSelector = nodeType === 9 && selector;
946
947			// qSA works strangely on Element-rooted queries
948			// We can work around this by specifying an extra ID on the root
949			// and working up from there (Thanks to Andrew Dupont for the technique)
950			// IE 8 doesn't work on object elements
951			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
952				groups = tokenize( selector );
953
954				if ( (old = context.getAttribute("id")) ) {
955					nid = old.replace( rescape, "\\$&" );
956				} else {
957					context.setAttribute( "id", nid );
958				}
959				nid = "[id='" + nid + "'] ";
960
961				i = groups.length;
962				while ( i-- ) {
963					groups[i] = nid + toSelector( groups[i] );
964				}
965				newContext = rsibling.test( selector ) && context.parentNode || context;
966				newSelector = groups.join(",");
967			}
968
969			if ( newSelector ) {
970				try {
971					push.apply( results,
972						newContext.querySelectorAll( newSelector )
973					);
974					return results;
975				} catch(qsaError) {
976				} finally {
977					if ( !old ) {
978						context.removeAttribute("id");
979					}
980				}
981			}
982		}
983	}
984
985	// All others
986	return select( selector.replace( rtrim, "$1" ), context, results, seed );
987}
988
989/**
990 * Detect xml
991 * @param {Element|Object} elem An element or a document
992 */
993isXML = Sizzle.isXML = function( elem ) {
994	// documentElement is verified for cases where it doesn't yet exist
995	// (such as loading iframes in IE - #4833)
996	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
997	return documentElement ? documentElement.nodeName !== "HTML" : false;
998};
999
1000/**
1001 * Sets document-related variables once based on the current document
1002 * @param {Element|Object} [doc] An element or document object to use to set the document
1003 * @returns {Object} Returns the current document
1004 */
1005setDocument = Sizzle.setDocument = function( node ) {
1006	var doc = node ? node.ownerDocument || node : preferredDoc;
1007
1008	// If no document and documentElement is available, return
1009	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
1010		return document;
1011	}
1012
1013	// Set our document
1014	document = doc;
1015	docElem = doc.documentElement;
1016
1017	// Support tests
1018	documentIsHTML = !isXML( doc );
1019
1020	// Check if getElementsByTagName("*") returns only elements
1021	support.getElementsByTagName = assert(function( div ) {
1022		div.appendChild( doc.createComment("") );
1023		return !div.getElementsByTagName("*").length;
1024	});
1025
1026	// Check if attributes should be retrieved by attribute nodes
1027	support.attributes = assert(function( div ) {
1028		div.innerHTML = "<select></select>";
1029		var type = typeof div.lastChild.getAttribute("multiple");
1030		// IE8 returns a string for some attributes even when not present
1031		return type !== "boolean" && type !== "string";
1032	});
1033
1034	// Check if getElementsByClassName can be trusted
1035	support.getElementsByClassName = assert(function( div ) {
1036		// Opera can't find a second classname (in 9.6)
1037		div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
1038		if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
1039			return false;
1040		}
1041
1042		// Safari 3.2 caches class attributes and doesn't catch changes
1043		div.lastChild.className = "e";
1044		return div.getElementsByClassName("e").length === 2;
1045	});
1046
1047	// Check if getElementsByName privileges form controls or returns elements by ID
1048	// If so, assume (for broader support) that getElementById returns elements by name
1049	support.getByName = assert(function( div ) {
1050		// Inject content
1051		div.id = expando + 0;
1052		// Support: Windows 8 Native Apps
1053		// Assigning innerHTML with "name" attributes throws uncatchable exceptions
1054		// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx
1055		div.appendChild( document.createElement("a") ).setAttribute( "name", expando );
1056		div.appendChild( document.createElement("i") ).setAttribute( "name", expando );
1057		docElem.appendChild( div );
1058
1059		// Test
1060		var pass = doc.getElementsByName &&
1061			// buggy browsers will return fewer than the correct 2
1062			doc.getElementsByName( expando ).length === 2 +
1063			// buggy browsers will return more than the correct 0
1064			doc.getElementsByName( expando + 0 ).length;
1065
1066		// Cleanup
1067		docElem.removeChild( div );
1068
1069		return pass;
1070	});
1071
1072	// Support: Webkit<537.32
1073	// Detached nodes confoundingly follow *each other*
1074	support.sortDetached = assert(function( div1 ) {
1075		return div1.compareDocumentPosition &&
1076			// Should return 1, but Webkit returns 4 (following)
1077			(div1.compareDocumentPosition( document.createElement("div") ) & 1);
1078	});
1079
1080	// IE6/7 return modified attributes
1081	Expr.attrHandle = assert(function( div ) {
1082		div.innerHTML = "<a href='#'></a>";
1083		return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
1084			div.firstChild.getAttribute("href") === "#";
1085	}) ?
1086		{} :
1087		{
1088			"href": function( elem ) {
1089				return elem.getAttribute( "href", 2 );
1090			},
1091			"type": function( elem ) {
1092				return elem.getAttribute("type");
1093			}
1094		};
1095
1096	// ID find and filter
1097	if ( support.getByName ) {
1098		Expr.find["ID"] = function( id, context ) {
1099			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1100				var m = context.getElementById( id );
1101				// Check parentNode to catch when Blackberry 4.6 returns
1102				// nodes that are no longer in the document #6963
1103				return m && m.parentNode ? [m] : [];
1104			}
1105		};
1106		Expr.filter["ID"] = function( id ) {
1107			var attrId = id.replace( runescape, funescape );
1108			return function( elem ) {
1109				return elem.getAttribute("id") === attrId;
1110			};
1111		};
1112	} else {
1113		Expr.find["ID"] = function( id, context ) {
1114			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1115				var m = context.getElementById( id );
1116
1117				return m ?
1118					m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
1119						[m] :
1120						undefined :
1121					[];
1122			}
1123		};
1124		Expr.filter["ID"] =  function( id ) {
1125			var attrId = id.replace( runescape, funescape );
1126			return function( elem ) {
1127				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
1128				return node && node.value === attrId;
1129			};
1130		};
1131	}
1132
1133	// Tag
1134	Expr.find["TAG"] = support.getElementsByTagName ?
1135		function( tag, context ) {
1136			if ( typeof context.getElementsByTagName !== strundefined ) {
1137				return context.getElementsByTagName( tag );
1138			}
1139		} :
1140		function( tag, context ) {
1141			var elem,
1142				tmp = [],
1143				i = 0,
1144				results = context.getElementsByTagName( tag );
1145
1146			// Filter out possible comments
1147			if ( tag === "*" ) {
1148				while ( (elem = results[i++]) ) {
1149					if ( elem.nodeType === 1 ) {
1150						tmp.push( elem );
1151					}
1152				}
1153
1154				return tmp;
1155			}
1156			return results;
1157		};
1158
1159	// Name
1160	Expr.find["NAME"] = support.getByName && function( tag, context ) {
1161		if ( typeof context.getElementsByName !== strundefined ) {
1162			return context.getElementsByName( name );
1163		}
1164	};
1165
1166	// Class
1167	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
1168		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
1169			return context.getElementsByClassName( className );
1170		}
1171	};
1172
1173	// QSA and matchesSelector support
1174
1175	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
1176	rbuggyMatches = [];
1177
1178	// qSa(:focus) reports false when true (Chrome 21),
1179	// no need to also add to buggyMatches since matches checks buggyQSA
1180	// A support test would require too much code (would include document ready)
1181	rbuggyQSA = [ ":focus" ];
1182
1183	if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
1184		// Build QSA regex
1185		// Regex strategy adopted from Diego Perini
1186		assert(function( div ) {
1187			// Select is set to empty string on purpose
1188			// This is to test IE's treatment of not explicitly
1189			// setting a boolean content attribute,
1190			// since its presence should be enough
1191			// http://bugs.jquery.com/ticket/12359
1192			div.innerHTML = "<select><option selected=''></option></select>";
1193
1194			// IE8 - Some boolean attributes are not treated correctly
1195			if ( !div.querySelectorAll("[selected]").length ) {
1196				rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
1197			}
1198
1199			// Webkit/Opera - :checked should return selected option elements
1200			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1201			// IE8 throws error here and will not see later tests
1202			if ( !div.querySelectorAll(":checked").length ) {
1203				rbuggyQSA.push(":checked");
1204			}
1205		});
1206
1207		assert(function( div ) {
1208
1209			// Opera 10-12/IE8 - ^= $= *= and empty values
1210			// Should not select anything
1211			div.innerHTML = "<input type='hidden' i=''/>";
1212			if ( div.querySelectorAll("[i^='']").length ) {
1213				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
1214			}
1215
1216			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
1217			// IE8 throws error here and will not see later tests
1218			if ( !div.querySelectorAll(":enabled").length ) {
1219				rbuggyQSA.push( ":enabled", ":disabled" );
1220			}
1221
1222			// Opera 10-11 does not throw on post-comma invalid pseudos
1223			div.querySelectorAll("*,:x");
1224			rbuggyQSA.push(",.*:");
1225		});
1226	}
1227
1228	if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
1229		docElem.mozMatchesSelector ||
1230		docElem.webkitMatchesSelector ||
1231		docElem.oMatchesSelector ||
1232		docElem.msMatchesSelector) )) ) {
1233
1234		assert(function( div ) {
1235			// Check to see if it's possible to do matchesSelector
1236			// on a disconnected node (IE 9)
1237			support.disconnectedMatch = matches.call( div, "div" );
1238
1239			// This should fail with an exception
1240			// Gecko does not error, returns false instead
1241			matches.call( div, "[s!='']:x" );
1242			rbuggyMatches.push( "!=", pseudos );
1243		});
1244	}
1245
1246	rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
1247	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
1248
1249	// Element contains another
1250	// Purposefully does not implement inclusive descendant
1251	// As in, an element does not contain itself
1252	contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
1253		function( a, b ) {
1254			var adown = a.nodeType === 9 ? a.documentElement : a,
1255				bup = b && b.parentNode;
1256			return a === bup || !!( bup && bup.nodeType === 1 && (
1257				adown.contains ?
1258					adown.contains( bup ) :
1259					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1260			));
1261		} :
1262		function( a, b ) {
1263			if ( b ) {
1264				while ( (b = b.parentNode) ) {
1265					if ( b === a ) {
1266						return true;
1267					}
1268				}
1269			}
1270			return false;
1271		};
1272
1273	// Document order sorting
1274	sortOrder = docElem.compareDocumentPosition ?
1275	function( a, b ) {
1276
1277		// Flag for duplicate removal
1278		if ( a === b ) {
1279			hasDuplicate = true;
1280			return 0;
1281		}
1282
1283		var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
1284
1285		if ( compare ) {
1286			// Disconnected nodes
1287			if ( compare & 1 ||
1288				(recompare && b.compareDocumentPosition( a ) === compare) ) {
1289
1290				// Choose the first element that is related to our preferred document
1291				if ( a === doc || contains(preferredDoc, a) ) {
1292					return -1;
1293				}
1294				if ( b === doc || contains(preferredDoc, b) ) {
1295					return 1;
1296				}
1297
1298				// Maintain original order
1299				return sortInput ?
1300					( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1301					0;
1302			}
1303
1304			return compare & 4 ? -1 : 1;
1305		}
1306
1307		// Not directly comparable, sort on existence of method
1308		return a.compareDocumentPosition ? -1 : 1;
1309	} :
1310	function( a, b ) {
1311		var cur,
1312			i = 0,
1313			aup = a.parentNode,
1314			bup = b.parentNode,
1315			ap = [ a ],
1316			bp = [ b ];
1317
1318		// Exit early if the nodes are identical
1319		if ( a === b ) {
1320			hasDuplicate = true;
1321			return 0;
1322
1323		// Parentless nodes are either documents or disconnected
1324		} else if ( !aup || !bup ) {
1325			return a === doc ? -1 :
1326				b === doc ? 1 :
1327				aup ? -1 :
1328				bup ? 1 :
1329				0;
1330
1331		// If the nodes are siblings, we can do a quick check
1332		} else if ( aup === bup ) {
1333			return siblingCheck( a, b );
1334		}
1335
1336		// Otherwise we need full lists of their ancestors for comparison
1337		cur = a;
1338		while ( (cur = cur.parentNode) ) {
1339			ap.unshift( cur );
1340		}
1341		cur = b;
1342		while ( (cur = cur.parentNode) ) {
1343			bp.unshift( cur );
1344		}
1345
1346		// Walk down the tree looking for a discrepancy
1347		while ( ap[i] === bp[i] ) {
1348			i++;
1349		}
1350
1351		return i ?
1352			// Do a sibling check if the nodes have a common ancestor
1353			siblingCheck( ap[i], bp[i] ) :
1354
1355			// Otherwise nodes in our document sort first
1356			ap[i] === preferredDoc ? -1 :
1357			bp[i] === preferredDoc ? 1 :
1358			0;
1359	};
1360
1361	return document;
1362};
1363
1364Sizzle.matches = function( expr, elements ) {
1365	return Sizzle( expr, null, null, elements );
1366};
1367
1368Sizzle.matchesSelector = function( elem, expr ) {
1369	// Set document vars if needed
1370	if ( ( elem.ownerDocument || elem ) !== document ) {
1371		setDocument( elem );
1372	}
1373
1374	// Make sure that attribute selectors are quoted
1375	expr = expr.replace( rattributeQuotes, "='$1']" );
1376
1377	// rbuggyQSA always contains :focus, so no need for an existence check
1378	if ( support.matchesSelector && documentIsHTML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
1379		try {
1380			var ret = matches.call( elem, expr );
1381
1382			// IE 9's matchesSelector returns false on disconnected nodes
1383			if ( ret || support.disconnectedMatch ||
1384					// As well, disconnected nodes are said to be in a document
1385					// fragment in IE 9
1386					elem.document && elem.document.nodeType !== 11 ) {
1387				return ret;
1388			}
1389		} catch(e) {}
1390	}
1391
1392	return Sizzle( expr, document, null, [elem] ).length > 0;
1393};
1394
1395Sizzle.contains = function( context, elem ) {
1396	// Set document vars if needed
1397	if ( ( context.ownerDocument || context ) !== document ) {
1398		setDocument( context );
1399	}
1400	return contains( context, elem );
1401};
1402
1403Sizzle.attr = function( elem, name ) {
1404	var val;
1405
1406	// Set document vars if needed
1407	if ( ( elem.ownerDocument || elem ) !== document ) {
1408		setDocument( elem );
1409	}
1410
1411	if ( documentIsHTML ) {
1412		name = name.toLowerCase();
1413	}
1414	if ( (val = Expr.attrHandle[ name ]) ) {
1415		return val( elem );
1416	}
1417	if ( !documentIsHTML || support.attributes ) {
1418		return elem.getAttribute( name );
1419	}
1420	return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
1421		name :
1422		val && val.specified ? val.value : null;
1423};
1424
1425Sizzle.error = function( msg ) {
1426	throw new Error( "Syntax error, unrecognized expression: " + msg );
1427};
1428
1429// Document sorting and removing duplicates
1430Sizzle.uniqueSort = function( results ) {
1431	var elem,
1432		duplicates = [],
1433		j = 0,
1434		i = 0;
1435
1436	// Unless we *know* we can detect duplicates, assume their presence
1437	hasDuplicate = !support.detectDuplicates;
1438	// Compensate for sort limitations
1439	recompare = !support.sortDetached;
1440	sortInput = !support.sortStable && results.slice( 0 );
1441	results.sort( sortOrder );
1442
1443	if ( hasDuplicate ) {
1444		while ( (elem = results[i++]) ) {
1445			if ( elem === results[ i ] ) {
1446				j = duplicates.push( i );
1447			}
1448		}
1449		while ( j-- ) {
1450			results.splice( duplicates[ j ], 1 );
1451		}
1452	}
1453
1454	return results;
1455};
1456
1457/**
1458 * Checks document order of two siblings
1459 * @param {Element} a
1460 * @param {Element} b
1461 * @returns Returns -1 if a precedes b, 1 if a follows b
1462 */
1463function siblingCheck( a, b ) {
1464	var cur = b && a,
1465		diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
1466
1467	// Use IE sourceIndex if available on both nodes
1468	if ( diff ) {
1469		return diff;
1470	}
1471
1472	// Check if b follows a
1473	if ( cur ) {
1474		while ( (cur = cur.nextSibling) ) {
1475			if ( cur === b ) {
1476				return -1;
1477			}
1478		}
1479	}
1480
1481	return a ? 1 : -1;
1482}
1483
1484// Returns a function to use in pseudos for input types
1485function createInputPseudo( type ) {
1486	return function( elem ) {
1487		var name = elem.nodeName.toLowerCase();
1488		return name === "input" && elem.type === type;
1489	};
1490}
1491
1492// Returns a function to use in pseudos for buttons
1493function createButtonPseudo( type ) {
1494	return function( elem ) {
1495		var name = elem.nodeName.toLowerCase();
1496		return (name === "input" || name === "button") && elem.type === type;
1497	};
1498}
1499
1500// Returns a function to use in pseudos for positionals
1501function createPositionalPseudo( fn ) {
1502	return markFunction(function( argument ) {
1503		argument = +argument;
1504		return markFunction(function( seed, matches ) {
1505			var j,
1506				matchIndexes = fn( [], seed.length, argument ),
1507				i = matchIndexes.length;
1508
1509			// Match elements found at the specified indexes
1510			while ( i-- ) {
1511				if ( seed[ (j = matchIndexes[i]) ] ) {
1512					seed[j] = !(matches[j] = seed[j]);
1513				}
1514			}
1515		});
1516	});
1517}
1518
1519/**
1520 * Utility function for retrieving the text value of an array of DOM nodes
1521 * @param {Array|Element} elem
1522 */
1523getText = Sizzle.getText = function( elem ) {
1524	var node,
1525		ret = "",
1526		i = 0,
1527		nodeType = elem.nodeType;
1528
1529	if ( !nodeType ) {
1530		// If no nodeType, this is expected to be an array
1531		for ( ; (node = elem[i]); i++ ) {
1532			// Do not traverse comment nodes
1533			ret += getText( node );
1534		}
1535	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
1536		// Use textContent for elements
1537		// innerText usage removed for consistency of new lines (see #11153)
1538		if ( typeof elem.textContent === "string" ) {
1539			return elem.textContent;
1540		} else {
1541			// Traverse its children
1542			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1543				ret += getText( elem );
1544			}
1545		}
1546	} else if ( nodeType === 3 || nodeType === 4 ) {
1547		return elem.nodeValue;
1548	}
1549	// Do not include comment or processing instruction nodes
1550
1551	return ret;
1552};
1553
1554Expr = Sizzle.selectors = {
1555
1556	// Can be adjusted by the user
1557	cacheLength: 50,
1558
1559	createPseudo: markFunction,
1560
1561	match: matchExpr,
1562
1563	find: {},
1564
1565	relative: {
1566		">": { dir: "parentNode", first: true },
1567		" ": { dir: "parentNode" },
1568		"+": { dir: "previousSibling", first: true },
1569		"~": { dir: "previousSibling" }
1570	},
1571
1572	preFilter: {
1573		"ATTR": function( match ) {
1574			match[1] = match[1].replace( runescape, funescape );
1575
1576			// Move the given value to match[3] whether quoted or unquoted
1577			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
1578
1579			if ( match[2] === "~=" ) {
1580				match[3] = " " + match[3] + " ";
1581			}
1582
1583			return match.slice( 0, 4 );
1584		},
1585
1586		"CHILD": function( match ) {
1587			/* matches from matchExpr["CHILD"]
1588				1 type (only|nth|...)
1589				2 what (child|of-type)
1590				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
1591				4 xn-component of xn+y argument ([+-]?\d*n|)
1592				5 sign of xn-component
1593				6 x of xn-component
1594				7 sign of y-component
1595				8 y of y-component
1596			*/
1597			match[1] = match[1].toLowerCase();
1598
1599			if ( match[1].slice( 0, 3 ) === "nth" ) {
1600				// nth-* requires argument
1601				if ( !match[3] ) {
1602					Sizzle.error( match[0] );
1603				}
1604
1605				// numeric x and y parameters for Expr.filter.CHILD
1606				// remember that false/true cast respectively to 0/1
1607				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
1608				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
1609
1610			// other types prohibit arguments
1611			} else if ( match[3] ) {
1612				Sizzle.error( match[0] );
1613			}
1614
1615			return match;
1616		},
1617
1618		"PSEUDO": function( match ) {
1619			var excess,
1620				unquoted = !match[5] && match[2];
1621
1622			if ( matchExpr["CHILD"].test( match[0] ) ) {
1623				return null;
1624			}
1625
1626			// Accept quoted arguments as-is
1627			if ( match[4] ) {
1628				match[2] = match[4];
1629
1630			// Strip excess characters from unquoted arguments
1631			} else if ( unquoted && rpseudo.test( unquoted ) &&
1632				// Get excess from tokenize (recursively)
1633				(excess = tokenize( unquoted, true )) &&
1634				// advance to the next closing parenthesis
1635				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
1636
1637				// excess is a negative index
1638				match[0] = match[0].slice( 0, excess );
1639				match[2] = unquoted.slice( 0, excess );
1640			}
1641
1642			// Return only captures needed by the pseudo filter method (type and argument)
1643			return match.slice( 0, 3 );
1644		}
1645	},
1646
1647	filter: {
1648
1649		"TAG": function( nodeName ) {
1650			if ( nodeName === "*" ) {
1651				return function() { return true; };
1652			}
1653
1654			nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
1655			return function( elem ) {
1656				return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
1657			};
1658		},
1659
1660		"CLASS": function( className ) {
1661			var pattern = classCache[ className + " " ];
1662
1663			return pattern ||
1664				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
1665				classCache( className, function( elem ) {
1666					return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
1667				});
1668		},
1669
1670		"ATTR": function( name, operator, check ) {
1671			return function( elem ) {
1672				var result = Sizzle.attr( elem, name );
1673
1674				if ( result == null ) {
1675					return operator === "!=";
1676				}
1677				if ( !operator ) {
1678					return true;
1679				}
1680
1681				result += "";
1682
1683				return operator === "=" ? result === check :
1684					operator === "!=" ? result !== check :
1685					operator === "^=" ? check && result.indexOf( check ) === 0 :
1686					operator === "*=" ? check && result.indexOf( check ) > -1 :
1687					operator === "$=" ? check && result.slice( -check.length ) === check :
1688					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
1689					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
1690					false;
1691			};
1692		},
1693
1694		"CHILD": function( type, what, argument, first, last ) {
1695			var simple = type.slice( 0, 3 ) !== "nth",
1696				forward = type.slice( -4 ) !== "last",
1697				ofType = what === "of-type";
1698
1699			return first === 1 && last === 0 ?
1700
1701				// Shortcut for :nth-*(n)
1702				function( elem ) {
1703					return !!elem.parentNode;
1704				} :
1705
1706				function( elem, context, xml ) {
1707					var cache, outerCache, node, diff, nodeIndex, start,
1708						dir = simple !== forward ? "nextSibling" : "previousSibling",
1709						parent = elem.parentNode,
1710						name = ofType && elem.nodeName.toLowerCase(),
1711						useCache = !xml && !ofType;
1712
1713					if ( parent ) {
1714
1715						// :(first|last|only)-(child|of-type)
1716						if ( simple ) {
1717							while ( dir ) {
1718								node = elem;
1719								while ( (node = node[ dir ]) ) {
1720									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
1721										return false;
1722									}
1723								}
1724								// Reverse direction for :only-* (if we haven't yet done so)
1725								start = dir = type === "only" && !start && "nextSibling";
1726							}
1727							return true;
1728						}
1729
1730						start = [ forward ? parent.firstChild : parent.lastChild ];
1731
1732						// non-xml :nth-child(...) stores cache data on `parent`
1733						if ( forward && useCache ) {
1734							// Seek `elem` from a previously-cached index
1735							outerCache = parent[ expando ] || (parent[ expando ] = {});
1736							cache = outerCache[ type ] || [];
1737							nodeIndex = cache[0] === dirruns && cache[1];
1738							diff = cache[0] === dirruns && cache[2];
1739							node = nodeIndex && parent.childNodes[ nodeIndex ];
1740
1741							while ( (node = ++nodeIndex && node && node[ dir ] ||
1742
1743								// Fallback to seeking `elem` from the start
1744								(diff = nodeIndex = 0) || start.pop()) ) {
1745
1746								// When found, cache indexes on `parent` and break
1747								if ( node.nodeType === 1 && ++diff && node === elem ) {
1748									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
1749									break;
1750								}
1751							}
1752
1753						// Use previously-cached element index if available
1754						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
1755							diff = cache[1];
1756
1757						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
1758						} else {
1759							// Use the same loop as above to seek `elem` from the start
1760							while ( (node = ++nodeIndex && node && node[ dir ] ||
1761								(diff = nodeIndex = 0) || start.pop()) ) {
1762
1763								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
1764									// Cache the index of each encountered element
1765									if ( useCache ) {
1766										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
1767									}
1768
1769									if ( node === elem ) {
1770										break;
1771									}
1772								}
1773							}
1774						}
1775
1776						// Incorporate the offset, then check against cycle size
1777						diff -= last;
1778						return diff === first || ( diff % first === 0 && diff / first >= 0 );
1779					}
1780				};
1781		},
1782
1783		"PSEUDO": function( pseudo, argument ) {
1784			// pseudo-class names are case-insensitive
1785			// http://www.w3.org/TR/selectors/#pseudo-classes
1786			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
1787			// Remember that setFilters inherits from pseudos
1788			var args,
1789				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
1790					Sizzle.error( "unsupported pseudo: " + pseudo );
1791
1792			// The user may use createPseudo to indicate that
1793			// arguments are needed to create the filter function
1794			// just as Sizzle does
1795			if ( fn[ expando ] ) {
1796				return fn( argument );
1797			}
1798
1799			// But maintain support for old signatures
1800			if ( fn.length > 1 ) {
1801				args = [ pseudo, pseudo, "", argument ];
1802				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
1803					markFunction(function( seed, matches ) {
1804						var idx,
1805							matched = fn( seed, argument ),
1806							i = matched.length;
1807						while ( i-- ) {
1808							idx = indexOf.call( seed, matched[i] );
1809							seed[ idx ] = !( matches[ idx ] = matched[i] );
1810						}
1811					}) :
1812					function( elem ) {
1813						return fn( elem, 0, args );
1814					};
1815			}
1816
1817			return fn;
1818		}
1819	},
1820
1821	pseudos: {
1822		// Potentially complex pseudos
1823		"not": markFunction(function( selector ) {
1824			// Trim the selector passed to compile
1825			// to avoid treating leading and trailing
1826			// spaces as combinators
1827			var input = [],
1828				results = [],
1829				matcher = compile( selector.replace( rtrim, "$1" ) );
1830
1831			return matcher[ expando ] ?
1832				markFunction(function( seed, matches, context, xml ) {
1833					var elem,
1834						unmatched = matcher( seed, null, xml, [] ),
1835						i = seed.length;
1836
1837					// Match elements unmatched by `matcher`
1838					while ( i-- ) {
1839						if ( (elem = unmatched[i]) ) {
1840							seed[i] = !(matches[i] = elem);
1841						}
1842					}
1843				}) :
1844				function( elem, context, xml ) {
1845					input[0] = elem;
1846					matcher( input, null, xml, results );
1847					return !results.pop();
1848				};
1849		}),
1850
1851		"has": markFunction(function( selector ) {
1852			return function( elem ) {
1853				return Sizzle( selector, elem ).length > 0;
1854			};
1855		}),
1856
1857		"contains": markFunction(function( text ) {
1858			return function( elem ) {
1859				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
1860			};
1861		}),
1862
1863		// "Whether an element is represented by a :lang() selector
1864		// is based solely on the element's language value
1865		// being equal to the identifier C,
1866		// or beginning with the identifier C immediately followed by "-".
1867		// The matching of C against the element's language value is performed case-insensitively.
1868		// The identifier C does not have to be a valid language name."
1869		// http://www.w3.org/TR/selectors/#lang-pseudo
1870		"lang": markFunction( function( lang ) {
1871			// lang value must be a valid identifier
1872			if ( !ridentifier.test(lang || "") ) {
1873				Sizzle.error( "unsupported lang: " + lang );
1874			}
1875			lang = lang.replace( runescape, funescape ).toLowerCase();
1876			return function( elem ) {
1877				var elemLang;
1878				do {
1879					if ( (elemLang = documentIsHTML ?
1880						elem.lang :
1881						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
1882
1883						elemLang = elemLang.toLowerCase();
1884						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
1885					}
1886				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
1887				return false;
1888			};
1889		}),
1890
1891		// Miscellaneous
1892		"target": function( elem ) {
1893			var hash = window.location && window.location.hash;
1894			return hash && hash.slice( 1 ) === elem.id;
1895		},
1896
1897		"root": function( elem ) {
1898			return elem === docElem;
1899		},
1900
1901		"focus": function( elem ) {
1902			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
1903		},
1904
1905		// Boolean properties
1906		"enabled": function( elem ) {
1907			return elem.disabled === false;
1908		},
1909
1910		"disabled": function( elem ) {
1911			return elem.disabled === true;
1912		},
1913
1914		"checked": function( elem ) {
1915			// In CSS3, :checked should return both checked and selected elements
1916			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1917			var nodeName = elem.nodeName.toLowerCase();
1918			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
1919		},
1920
1921		"selected": function( elem ) {
1922			// Accessing this property makes selected-by-default
1923			// options in Safari work properly
1924			if ( elem.parentNode ) {
1925				elem.parentNode.selectedIndex;
1926			}
1927
1928			return elem.selected === true;
1929		},
1930
1931		// Contents
1932		"empty": function( elem ) {
1933			// http://www.w3.org/TR/selectors/#empty-pseudo
1934			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
1935			//   not comment, processing instructions, or others
1936			// Thanks to Diego Perini for the nodeName shortcut
1937			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
1938			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1939				if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
1940					return false;
1941				}
1942			}
1943			return true;
1944		},
1945
1946		"parent": function( elem ) {
1947			return !Expr.pseudos["empty"]( elem );
1948		},
1949
1950		// Element/input types
1951		"header": function( elem ) {
1952			return rheader.test( elem.nodeName );
1953		},
1954
1955		"input": function( elem ) {
1956			return rinputs.test( elem.nodeName );
1957		},
1958
1959		"button": function( elem ) {
1960			var name = elem.nodeName.toLowerCase();
1961			return name === "input" && elem.type === "button" || name === "button";
1962		},
1963
1964		"text": function( elem ) {
1965			var attr;
1966			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
1967			// use getAttribute instead to test this case
1968			return elem.nodeName.toLowerCase() === "input" &&
1969				elem.type === "text" &&
1970				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
1971		},
1972
1973		// Position-in-collection
1974		"first": createPositionalPseudo(function() {
1975			return [ 0 ];
1976		}),
1977
1978		"last": createPositionalPseudo(function( matchIndexes, length ) {
1979			return [ length - 1 ];
1980		}),
1981
1982		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
1983			return [ argument < 0 ? argument + length : argument ];
1984		}),
1985
1986		"even": createPositionalPseudo(function( matchIndexes, length ) {
1987			var i = 0;
1988			for ( ; i < length; i += 2 ) {
1989				matchIndexes.push( i );
1990			}
1991			return matchIndexes;
1992		}),
1993
1994		"odd": createPositionalPseudo(function( matchIndexes, length ) {
1995			var i = 1;
1996			for ( ; i < length; i += 2 ) {
1997				matchIndexes.push( i );
1998			}
1999			return matchIndexes;
2000		}),
2001
2002		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
2003			var i = argument < 0 ? argument + length : argument;
2004			for ( ; --i >= 0; ) {
2005				matchIndexes.push( i );
2006			}
2007			return matchIndexes;
2008		}),
2009
2010		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
2011			var i = argument < 0 ? argument + length : argument;
2012			for ( ; ++i < length; ) {
2013				matchIndexes.push( i );
2014			}
2015			return matchIndexes;
2016		})
2017	}
2018};
2019
2020// Add button/input type pseudos
2021for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
2022	Expr.pseudos[ i ] = createInputPseudo( i );
2023}
2024for ( i in { submit: true, reset: true } ) {
2025	Expr.pseudos[ i ] = createButtonPseudo( i );
2026}
2027
2028function tokenize( selector, parseOnly ) {
2029	var matched, match, tokens, type,
2030		soFar, groups, preFilters,
2031		cached = tokenCache[ selector + " " ];
2032
2033	if ( cached ) {
2034		return parseOnly ? 0 : cached.slice( 0 );
2035	}
2036
2037	soFar = selector;
2038	groups = [];
2039	preFilters = Expr.preFilter;
2040
2041	while ( soFar ) {
2042
2043		// Comma and first run
2044		if ( !matched || (match = rcomma.exec( soFar )) ) {
2045			if ( match ) {
2046				// Don't consume trailing commas as valid
2047				soFar = soFar.slice( match[0].length ) || soFar;
2048			}
2049			groups.push( tokens = [] );
2050		}
2051
2052		matched = false;
2053
2054		// Combinators
2055		if ( (match = rcombinators.exec( soFar )) ) {
2056			matched = match.shift();
2057			tokens.push( {
2058				value: matched,
2059				// Cast descendant combinators to space
2060				type: match[0].replace( rtrim, " " )
2061			} );
2062			soFar = soFar.slice( matched.length );
2063		}
2064
2065		// Filters
2066		for ( type in Expr.filter ) {
2067			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
2068				(match = preFilters[ type ]( match ))) ) {
2069				matched = match.shift();
2070				tokens.push( {
2071					value: matched,
2072					type: type,
2073					matches: match
2074				} );
2075				soFar = soFar.slice( matched.length );
2076			}
2077		}
2078
2079		if ( !matched ) {
2080			break;
2081		}
2082	}
2083
2084	// Return the length of the invalid excess
2085	// if we're just parsing
2086	// Otherwise, throw an error or return tokens
2087	return parseOnly ?
2088		soFar.length :
2089		soFar ?
2090			Sizzle.error( selector ) :
2091			// Cache the tokens
2092			tokenCache( selector, groups ).slice( 0 );
2093}
2094
2095function toSelector( tokens ) {
2096	var i = 0,
2097		len = tokens.length,
2098		selector = "";
2099	for ( ; i < len; i++ ) {
2100		selector += tokens[i].value;
2101	}
2102	return selector;
2103}
2104
2105function addCombinator( matcher, combinator, base ) {
2106	var dir = combinator.dir,
2107		checkNonElements = base && dir === "parentNode",
2108		doneName = done++;
2109
2110	return combinator.first ?
2111		// Check against closest ancestor/preceding element
2112		function( elem, context, xml ) {
2113			while ( (elem = elem[ dir ]) ) {
2114				if ( elem.nodeType === 1 || checkNonElements ) {
2115					return matcher( elem, context, xml );
2116				}
2117			}
2118		} :
2119
2120		// Check against all ancestor/preceding elements
2121		function( elem, context, xml ) {
2122			var data, cache, outerCache,
2123				dirkey = dirruns + " " + doneName;
2124
2125			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
2126			if ( xml ) {
2127				while ( (elem = elem[ dir ]) ) {
2128					if ( elem.nodeType === 1 || checkNonElements ) {
2129						if ( matcher( elem, context, xml ) ) {
2130							return true;
2131						}
2132					}
2133				}
2134			} else {
2135				while ( (elem = elem[ dir ]) ) {
2136					if ( elem.nodeType === 1 || checkNonElements ) {
2137						outerCache = elem[ expando ] || (elem[ expando ] = {});
2138						if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
2139							if ( (data = cache[1]) === true || data === cachedruns ) {
2140								return data === true;
2141							}
2142						} else {
2143							cache = outerCache[ dir ] = [ dirkey ];
2144							cache[1] = matcher( elem, context, xml ) || cachedruns;
2145							if ( cache[1] === true ) {
2146								return true;
2147							}
2148						}
2149					}
2150				}
2151			}
2152		};
2153}
2154
2155function elementMatcher( matchers ) {
2156	return matchers.length > 1 ?
2157		function( elem, context, xml ) {
2158			var i = matchers.length;
2159			while ( i-- ) {
2160				if ( !matchers[i]( elem, context, xml ) ) {
2161					return false;
2162				}
2163			}
2164			return true;
2165		} :
2166		matchers[0];
2167}
2168
2169function condense( unmatched, map, filter, context, xml ) {
2170	var elem,
2171		newUnmatched = [],
2172		i = 0,
2173		len = unmatched.length,
2174		mapped = map != null;
2175
2176	for ( ; i < len; i++ ) {
2177		if ( (elem = unmatched[i]) ) {
2178			if ( !filter || filter( elem, context, xml ) ) {
2179				newUnmatched.push( elem );
2180				if ( mapped ) {
2181					map.push( i );
2182				}
2183			}
2184		}
2185	}
2186
2187	return newUnmatched;
2188}
2189
2190function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
2191	if ( postFilter && !postFilter[ expando ] ) {
2192		postFilter = setMatcher( postFilter );
2193	}
2194	if ( postFinder && !postFinder[ expando ] ) {
2195		postFinder = setMatcher( postFinder, postSelector );
2196	}
2197	return markFunction(function( seed, results, context, xml ) {
2198		var temp, i, elem,
2199			preMap = [],
2200			postMap = [],
2201			preexisting = results.length,
2202
2203			// Get initial elements from seed or context
2204			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
2205
2206			// Prefilter to get matcher input, preserving a map for seed-results synchronization
2207			matcherIn = preFilter && ( seed || !selector ) ?
2208				condense( elems, preMap, preFilter, context, xml ) :
2209				elems,
2210
2211			matcherOut = matcher ?
2212				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
2213				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
2214
2215					// ...intermediate processing is necessary
2216					[] :
2217
2218					// ...otherwise use results directly
2219					results :
2220				matcherIn;
2221
2222		// Find primary matches
2223		if ( matcher ) {
2224			matcher( matcherIn, matcherOut, context, xml );
2225		}
2226
2227		// Apply postFilter
2228		if ( postFilter ) {
2229			temp = condense( matcherOut, postMap );
2230			postFilter( temp, [], context, xml );
2231
2232			// Un-match failing elements by moving them back to matcherIn
2233			i = temp.length;
2234			while ( i-- ) {
2235				if ( (elem = temp[i]) ) {
2236					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
2237				}
2238			}
2239		}
2240
2241		if ( seed ) {
2242			if ( postFinder || preFilter ) {
2243				if ( postFinder ) {
2244					// Get the final matcherOut by condensing this intermediate into postFinder contexts
2245					temp = [];
2246					i = matcherOut.length;
2247					while ( i-- ) {
2248						if ( (elem = matcherOut[i]) ) {
2249							// Restore matcherIn since elem is not yet a final match
2250							temp.push( (matcherIn[i] = elem) );
2251						}
2252					}
2253					postFinder( null, (matcherOut = []), temp, xml );
2254				}
2255
2256				// Move matched elements from seed to results to keep them synchronized
2257				i = matcherOut.length;
2258				while ( i-- ) {
2259					if ( (elem = matcherOut[i]) &&
2260						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
2261
2262						seed[temp] = !(results[temp] = elem);
2263					}
2264				}
2265			}
2266
2267		// Add elements to results, through postFinder if defined
2268		} else {
2269			matcherOut = condense(
2270				matcherOut === results ?
2271					matcherOut.splice( preexisting, matcherOut.length ) :
2272					matcherOut
2273			);
2274			if ( postFinder ) {
2275				postFinder( null, results, matcherOut, xml );
2276			} else {
2277				push.apply( results, matcherOut );
2278			}
2279		}
2280	});
2281}
2282
2283function matcherFromTokens( tokens ) {
2284	var checkContext, matcher, j,
2285		len = tokens.length,
2286		leadingRelative = Expr.relative[ tokens[0].type ],
2287		implicitRelative = leadingRelative || Expr.relative[" "],
2288		i = leadingRelative ? 1 : 0,
2289
2290		// The foundational matcher ensures that elements are reachable from top-level context(s)
2291		matchContext = addCombinator( function( elem ) {
2292			return elem === checkContext;
2293		}, implicitRelative, true ),
2294		matchAnyContext = addCombinator( function( elem ) {
2295			return indexOf.call( checkContext, elem ) > -1;
2296		}, implicitRelative, true ),
2297		matchers = [ function( elem, context, xml ) {
2298			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
2299				(checkContext = context).nodeType ?
2300					matchContext( elem, context, xml ) :
2301					matchAnyContext( elem, context, xml ) );
2302		} ];
2303
2304	for ( ; i < len; i++ ) {
2305		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
2306			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
2307		} else {
2308			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
2309
2310			// Return special upon seeing a positional matcher
2311			if ( matcher[ expando ] ) {
2312				// Find the next relative operator (if any) for proper handling
2313				j = ++i;
2314				for ( ; j < len; j++ ) {
2315					if ( Expr.relative[ tokens[j].type ] ) {
2316						break;
2317					}
2318				}
2319				return setMatcher(
2320					i > 1 && elementMatcher( matchers ),
2321					i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
2322					matcher,
2323					i < j && matcherFromTokens( tokens.slice( i, j ) ),
2324					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
2325					j < len && toSelector( tokens )
2326				);
2327			}
2328			matchers.push( matcher );
2329		}
2330	}
2331
2332	return elementMatcher( matchers );
2333}
2334
2335function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
2336	// A counter to specify which element is currently being matched
2337	var matcherCachedRuns = 0,
2338		bySet = setMatchers.length > 0,
2339		byElement = elementMatchers.length > 0,
2340		superMatcher = function( seed, context, xml, results, expandContext ) {
2341			var elem, j, matcher,
2342				setMatched = [],
2343				matchedCount = 0,
2344				i = "0",
2345				unmatched = seed && [],
2346				outermost = expandContext != null,
2347				contextBackup = outermostContext,
2348				// We must always have either seed elements or context
2349				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
2350				// Use integer dirruns iff this is the outermost matcher
2351				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
2352
2353			if ( outermost ) {
2354				outermostContext = context !== document && context;
2355				cachedruns = matcherCachedRuns;
2356			}
2357
2358			// Add elements passing elementMatchers directly to results
2359			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
2360			for ( ; (elem = elems[i]) != null; i++ ) {
2361				if ( byElement && elem ) {
2362					j = 0;
2363					while ( (matcher = elementMatchers[j++]) ) {
2364						if ( matcher( elem, context, xml ) ) {
2365							results.push( elem );
2366							break;
2367						}
2368					}
2369					if ( outermost ) {
2370						dirruns = dirrunsUnique;
2371						cachedruns = ++matcherCachedRuns;
2372					}
2373				}
2374
2375				// Track unmatched elements for set filters
2376				if ( bySet ) {
2377					// They will have gone through all possible matchers
2378					if ( (elem = !matcher && elem) ) {
2379						matchedCount--;
2380					}
2381
2382					// Lengthen the array for every element, matched or not
2383					if ( seed ) {
2384						unmatched.push( elem );
2385					}
2386				}
2387			}
2388
2389			// Apply set filters to unmatched elements
2390			matchedCount += i;
2391			if ( bySet && i !== matchedCount ) {
2392				j = 0;
2393				while ( (matcher = setMatchers[j++]) ) {
2394					matcher( unmatched, setMatched, context, xml );
2395				}
2396
2397				if ( seed ) {
2398					// Reintegrate element matches to eliminate the need for sorting
2399					if ( matchedCount > 0 ) {
2400						while ( i-- ) {
2401							if ( !(unmatched[i] || setMatched[i]) ) {
2402								setMatched[i] = pop.call( results );
2403							}
2404						}
2405					}
2406
2407					// Discard index placeholder values to get only actual matches
2408					setMatched = condense( setMatched );
2409				}
2410
2411				// Add matches to results
2412				push.apply( results, setMatched );
2413
2414				// Seedless set matches succeeding multiple successful matchers stipulate sorting
2415				if ( outermost && !seed && setMatched.length > 0 &&
2416					( matchedCount + setMatchers.length ) > 1 ) {
2417
2418					Sizzle.uniqueSort( results );
2419				}
2420			}
2421
2422			// Override manipulation of globals by nested matchers
2423			if ( outermost ) {
2424				dirruns = dirrunsUnique;
2425				outermostContext = contextBackup;
2426			}
2427
2428			return unmatched;
2429		};
2430
2431	return bySet ?
2432		markFunction( superMatcher ) :
2433		superMatcher;
2434}
2435
2436compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
2437	var i,
2438		setMatchers = [],
2439		elementMatchers = [],
2440		cached = compilerCache[ selector + " " ];
2441
2442	if ( !cached ) {
2443		// Generate a function of recursive functions that can be used to check each element
2444		if ( !group ) {
2445			group = tokenize( selector );
2446		}
2447		i = group.length;
2448		while ( i-- ) {
2449			cached = matcherFromTokens( group[i] );
2450			if ( cached[ expando ] ) {
2451				setMatchers.push( cached );
2452			} else {
2453				elementMatchers.push( cached );
2454			}
2455		}
2456
2457		// Cache the compiled function
2458		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
2459	}
2460	return cached;
2461};
2462
2463function multipleContexts( selector, contexts, results ) {
2464	var i = 0,
2465		len = contexts.length;
2466	for ( ; i < len; i++ ) {
2467		Sizzle( selector, contexts[i], results );
2468	}
2469	return results;
2470}
2471
2472function select( selector, context, results, seed ) {
2473	var i, tokens, token, type, find,
2474		match = tokenize( selector );
2475
2476	if ( !seed ) {
2477		// Try to minimize operations if there is only one group
2478		if ( match.length === 1 ) {
2479
2480			// Take a shortcut and set the context if the root selector is an ID
2481			tokens = match[0] = match[0].slice( 0 );
2482			if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
2483					context.nodeType === 9 && documentIsHTML &&
2484					Expr.relative[ tokens[1].type ] ) {
2485
2486				context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
2487				if ( !context ) {
2488					return results;
2489				}
2490
2491				selector = selector.slice( tokens.shift().value.length );
2492			}
2493
2494			// Fetch a seed set for right-to-left matching
2495			i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
2496			while ( i-- ) {
2497				token = tokens[i];
2498
2499				// Abort if we hit a combinator
2500				if ( Expr.relative[ (type = token.type) ] ) {
2501					break;
2502				}
2503				if ( (find = Expr.find[ type ]) ) {
2504					// Search, expanding context for leading sibling combinators
2505					if ( (seed = find(
2506						token.matches[0].replace( runescape, funescape ),
2507						rsibling.test( tokens[0].type ) && context.parentNode || context
2508					)) ) {
2509
2510						// If seed is empty or no tokens remain, we can return early
2511						tokens.splice( i, 1 );
2512						selector = seed.length && toSelector( tokens );
2513						if ( !selector ) {
2514							push.apply( results, seed );
2515							return results;
2516						}
2517
2518						break;
2519					}
2520				}
2521			}
2522		}
2523	}
2524
2525	// Compile and execute a filtering function
2526	// Provide `match` to avoid retokenization if we modified the selector above
2527	compile( selector, match )(
2528		seed,
2529		context,
2530		!documentIsHTML,
2531		results,
2532		rsibling.test( selector )
2533	);
2534	return results;
2535}
2536
2537// Deprecated
2538Expr.pseudos["nth"] = Expr.pseudos["eq"];
2539
2540// Easy API for creating new setFilters
2541function setFilters() {}
2542setFilters.prototype = Expr.filters = Expr.pseudos;
2543Expr.setFilters = new setFilters();
2544
2545// Check sort stability
2546support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
2547
2548// Initialize with the default document
2549setDocument();
2550
2551// Always assume the presence of duplicates if sort doesn't
2552// pass them to our comparison function (as in Google Chrome).
2553[0, 0].sort( sortOrder );
2554support.detectDuplicates = hasDuplicate;
2555
2556/*
2557// EXPOSE
2558if ( typeof define === "function" && define.amd ) {
2559	define(function() { return Sizzle; });
2560} else {
2561	window.Sizzle = Sizzle;
2562}
2563*/
2564
2565// EXPOSE
2566return Sizzle;
2567});
2568
2569// Included from: js/tinymce/classes/dom/DomQuery.js
2570
2571/**
2572 * DomQuery.js
2573 *
2574 * Copyright, Moxiecode Systems AB
2575 * Released under LGPL License.
2576 *
2577 * License: http://www.tinymce.com/license
2578 * Contributing: http://www.tinymce.com/contributing
2579 *
2580 * Some of this logic is based on jQuery code that is released under
2581 * MIT license that grants us to sublicense it under LGPL.
2582 *
2583 * @ignore-file
2584 */
2585
2586/**
2587 * @class tinymce.dom.DomQuery
2588 */
2589define("tinymce/dom/DomQuery", [
2590	"tinymce/dom/EventUtils",
2591	"tinymce/dom/Sizzle"
2592], function(EventUtils, Sizzle) {
2593	var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
2594	var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
2595	var Event = EventUtils.Event;
2596
2597	function isDefined(obj) {
2598		return typeof obj !== "undefined";
2599	}
2600
2601	function isString(obj) {
2602		return typeof obj === "string";
2603	}
2604
2605	function createFragment(html) {
2606		var frag, node, container;
2607
2608		container = doc.createElement("div");
2609		frag = doc.createDocumentFragment();
2610		container.innerHTML = html;
2611
2612		while ((node = container.firstChild)) {
2613			frag.appendChild(node);
2614		}
2615
2616		return frag;
2617	}
2618
2619	function domManipulate(targetNodes, sourceItem, callback) {
2620		var i;
2621
2622		if (typeof sourceItem === "string") {
2623			sourceItem = createFragment(sourceItem);
2624		} else if (sourceItem.length) {
2625			for (i = 0; i < sourceItem.length; i++) {
2626				domManipulate(targetNodes, sourceItem[i], callback);
2627			}
2628
2629			return targetNodes;
2630		}
2631
2632		i = targetNodes.length;
2633		while (i--) {
2634			callback.call(targetNodes[i], sourceItem.parentNode ? sourceItem : sourceItem);
2635		}
2636
2637		return targetNodes;
2638	}
2639
2640	function hasClass(node, className) {
2641		return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
2642	}
2643
2644	/**
2645	 * Makes a map object out of a string that gets separated by a delimiter.
2646	 *
2647	 * @method makeMap
2648	 * @param {String} items Item string to split.
2649	 * @param {Object} map Optional object to add items to.
2650	 * @return {Object} name/value object with items as keys.
2651	 */
2652	function makeMap(items, map) {
2653		var i;
2654
2655		items = items || [];
2656
2657		if (typeof(items) == "string") {
2658			items = items.split(' ');
2659		}
2660
2661		map = map || {};
2662
2663		i = items.length;
2664		while (i--) {
2665			map[items[i]] = {};
2666		}
2667
2668		return map;
2669	}
2670
2671	var numericCssMap = makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom');
2672
2673	function DomQuery(selector, context) {
2674		return new DomQuery.fn.init(selector, context);
2675	}
2676
2677	/**
2678	 * Extends the specified object with another object.
2679	 *
2680	 * @method extend
2681	 * @param {Object} target Object to extend.
2682	 * @param {Object..} obj Multiple objects to extend with.
2683	 * @return {Object} Same as target, the extended object.
2684	 */
2685	function extend(target) {
2686		var args = arguments, arg, i, key;
2687
2688		for (i = 1; i < args.length; i++) {
2689			arg = args[i];
2690
2691			for (key in arg) {
2692				target[key] = arg[key];
2693			}
2694		}
2695
2696		return target;
2697	}
2698
2699	/**
2700	 * Converts the specified object into a real JavaScript array.
2701	 *
2702	 * @method toArray
2703	 * @param {Object} obj Object to convert into array.
2704	 * @return {Array} Array object based in input.
2705	 */
2706	function toArray(obj) {
2707		var array = [], i, l;
2708
2709		for (i = 0, l = obj.length; i < l; i++) {
2710			array[i] = obj[i];
2711		}
2712
2713		return array;
2714	}
2715
2716	/**
2717	 * Returns the index of the specified item inside the array.
2718	 *
2719	 * @method inArray
2720	 * @param {Object} item Item to look for.
2721	 * @param {Array} array Array to look for item in.
2722	 * @return {Number} Index of the item or -1.
2723	 */
2724	function inArray(item, array) {
2725		var i;
2726
2727		if (array.indexOf) {
2728			return array.indexOf(item);
2729		}
2730
2731		i = array.length;
2732		while (i--) {
2733			if (array[i] === item) {
2734				return i;
2735			}
2736		}
2737
2738		return -1;
2739	}
2740
2741	/**
2742	 * Returns true/false if the specified object is an array.
2743	 *
2744	 * @method isArray
2745	 * @param {Object} obj Object to check if it's an array.
2746	 * @return {Boolean} true/false if the input object is array or not.
2747	 */
2748	var isArray = Array.isArray || function(obj) {
2749		return Object.prototype.toString.call(obj) === "[object Array]";
2750	};
2751
2752	var whiteSpaceRegExp = /^\s*|\s*$/g;
2753	var trim = function(str) {
2754		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
2755	};
2756
2757	/**
2758	 * Executes the callback function for each item in array/object. If you return false in the
2759	 * callback it will break the loop.
2760	 *
2761	 * @method each
2762	 * @param {Object} obj Object to iterate.
2763	 * @param {function} callback Callback function to execute for each item.
2764	 */
2765	function each(obj, callback) {
2766		var length, key, i, undef, value;
2767
2768		if (obj) {
2769			length = obj.length;
2770
2771			if (length === undef) {
2772				// Loop object items
2773				for (key in obj) {
2774					if (obj.hasOwnProperty(key)) {
2775						value = obj[key];
2776						if (callback.call(value, value, key) === false) {
2777							break;
2778						}
2779					}
2780				}
2781			} else {
2782				// Loop array items
2783				for (i = 0; i < length; i++) {
2784					value = obj[i];
2785					if (callback.call(value, value, key) === false) {
2786						break;
2787					}
2788				}
2789			}
2790		}
2791
2792		return obj;
2793	}
2794
2795	DomQuery.fn = DomQuery.prototype = {
2796		constructor: DomQuery,
2797		selector: "",
2798		length: 0,
2799
2800		init: function(selector, context) {
2801			var self = this, match, node;
2802
2803			if (!selector) {
2804				return self;
2805			}
2806
2807			if (selector.nodeType) {
2808				self.context = self[0] = selector;
2809				self.length = 1;
2810
2811				return self;
2812			}
2813
2814			if (isString(selector)) {
2815				if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
2816					match = [null, selector, null];
2817				} else {
2818					match = rquickExpr.exec(selector);
2819				}
2820
2821				if (match) {
2822					if (match[1]) {
2823						node = createFragment(selector).firstChild;
2824						while (node) {
2825							this.add(node);
2826							node = node.nextSibling;
2827						}
2828					} else {
2829						node = doc.getElementById(match[2]);
2830
2831						if (node.id !== match[2]) {
2832							return self.find(selector);
2833						}
2834
2835						self.length = 1;
2836						self[0] = node;
2837					}
2838				} else {
2839					return DomQuery(context || document).find(selector);
2840				}
2841			} else {
2842				this.add(selector);
2843			}
2844
2845			return self;
2846		},
2847
2848		toArray: function() {
2849			return toArray(this);
2850		},
2851
2852		add: function(items) {
2853			var self = this;
2854
2855			// Force single item into array
2856			if (!isArray(items)) {
2857				if (items instanceof DomQuery) {
2858					self.add(items.toArray());
2859				} else {
2860					push.call(self, items);
2861				}
2862			} else {
2863				push.apply(self, items);
2864			}
2865
2866			return self;
2867		},
2868
2869		attr: function(name, value) {
2870			var self = this;
2871
2872			if (typeof name === "object") {
2873				each(name, function(value, name) {
2874					self.attr(name, value);
2875				});
2876			} else if (isDefined(value)) {
2877				this.each(function() {
2878					if (this.nodeType === 1) {
2879						this.setAttribute(name, value);
2880					}
2881				});
2882			} else {
2883				return self[0] && self[0].nodeType === 1 ? self[0].getAttribute(name) : undefined;
2884			}
2885
2886			return self;
2887		},
2888
2889		css: function(name, value) {
2890			var self = this;
2891
2892			if (typeof name === "object") {
2893				each(name, function(value, name) {
2894					self.css(name, value);
2895				});
2896			} else {
2897				// Camelcase it, if needed
2898				name = name.replace(/-(\D)/g, function(a, b) {
2899					return b.toUpperCase();
2900				});
2901
2902				if (isDefined(value)) {
2903					// Default px suffix on these
2904					if (typeof(value) === 'number' && !numericCssMap[name]) {
2905						value += 'px';
2906					}
2907
2908					self.each(function() {
2909						var style = this.style;
2910
2911						// IE specific opacity
2912						if (name === "opacity" && this.runtimeStyle && typeof(this.runtimeStyle.opacity) === "undefined") {
2913							style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
2914						}
2915
2916						try {
2917							style[name] = value;
2918						} catch (ex) {
2919							// Ignore
2920						}
2921					});
2922				} else {
2923					return self[0] ? self[0].style[name] : undefined;
2924				}
2925			}
2926
2927			return self;
2928		},
2929
2930		remove: function() {
2931			var self = this, node, i = this.length;
2932
2933			while (i--) {
2934				node = self[i];
2935				Event.clean(node);
2936
2937				if (node.parentNode) {
2938					node.parentNode.removeChild(node);
2939				}
2940			}
2941
2942			return this;
2943		},
2944
2945		empty: function() {
2946			var self = this, node, i = this.length;
2947
2948			while (i--) {
2949				node = self[i];
2950				while (node.firstChild) {
2951					node.removeChild(node.firstChild);
2952				}
2953			}
2954
2955			return this;
2956		},
2957
2958		html: function(value) {
2959			var self = this, i;
2960
2961			if (isDefined(value)) {
2962				i = self.length;
2963				while (i--) {
2964					self[i].innerHTML = value;
2965				}
2966
2967				return self;
2968			}
2969
2970			return self[0] ? self[0].innerHTML : '';
2971		},
2972
2973		text: function(value) {
2974			var self = this, i;
2975
2976			if (isDefined(value)) {
2977				i = self.length;
2978				while (i--) {
2979					self[i].innerText = self[0].textContent = value;
2980				}
2981
2982				return self;
2983			}
2984
2985			return self[0] ? self[0].innerText || self[0].textContent : '';
2986		},
2987
2988		append: function() {
2989			return domManipulate(this, arguments, function(node) {
2990				if (this.nodeType === 1) {
2991					this.appendChild(node);
2992				}
2993			});
2994		},
2995
2996		prepend: function() {
2997			return domManipulate(this, arguments, function(node) {
2998				if (this.nodeType === 1) {
2999					this.insertBefore(node, this.firstChild);
3000				}
3001			});
3002		},
3003
3004		before: function() {
3005			var self = this;
3006
3007			if (self[0] && self[0].parentNode) {
3008				return domManipulate(self, arguments, function(node) {
3009					this.parentNode.insertBefore(node, this.nextSibling);
3010				});
3011			}
3012
3013			return self;
3014		},
3015
3016		after: function() {
3017			var self = this;
3018
3019			if (self[0] && self[0].parentNode) {
3020				return domManipulate(self, arguments, function(node) {
3021					this.parentNode.insertBefore(node, this);
3022				});
3023			}
3024
3025			return self;
3026		},
3027
3028		appendTo: function(val) {
3029			DomQuery(val).append(this);
3030
3031			return this;
3032		},
3033
3034		addClass: function(className) {
3035			return this.toggleClass(className, true);
3036		},
3037
3038		removeClass: function(className) {
3039			return this.toggleClass(className, false);
3040		},
3041
3042		toggleClass: function(className, state) {
3043			var self = this;
3044
3045			if (className.indexOf(' ') !== -1) {
3046				each(className.split(' '), function() {
3047					self.toggleClass(this, state);
3048				});
3049			} else {
3050				self.each(function() {
3051					var node = this, existingClassName;
3052
3053					if (hasClass(node, className) !== state) {
3054						existingClassName = node.className;
3055
3056						if (state) {
3057							node.className += existingClassName ? ' ' + className : className;
3058						} else {
3059							node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
3060						}
3061					}
3062				});
3063			}
3064
3065			return self;
3066		},
3067
3068		hasClass: function(className) {
3069			return hasClass(this[0], className);
3070		},
3071
3072		each: function(callback) {
3073			return each(this, callback);
3074		},
3075
3076		on: function(name, callback) {
3077			return this.each(function() {
3078				Event.bind(this, name, callback);
3079			});
3080		},
3081
3082		off: function(name, callback) {
3083			return this.each(function() {
3084				Event.unbind(this, name, callback);
3085			});
3086		},
3087
3088		show: function() {
3089			return this.css('display', '');
3090		},
3091
3092		hide: function() {
3093			return this.css('display', 'none');
3094		},
3095
3096		slice: function() {
3097			return new DomQuery(slice.apply(this, arguments));
3098		},
3099
3100		eq: function(index) {
3101			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
3102		},
3103
3104		first: function() {
3105			return this.eq(0);
3106		},
3107
3108		last: function() {
3109			return this.eq(-1);
3110		},
3111
3112		replaceWith: function(content) {
3113			var self = this;
3114
3115			if (self[0]) {
3116				self[0].parentNode.replaceChild(DomQuery(content)[0], self[0]);
3117			}
3118
3119			return self;
3120		},
3121
3122		wrap: function(wrapper) {
3123			wrapper = DomQuery(wrapper)[0];
3124
3125			return this.each(function() {
3126				var self = this, newWrapper = wrapper.cloneNode(false);
3127				self.parentNode.insertBefore(newWrapper, self);
3128				newWrapper.appendChild(self);
3129			});
3130		},
3131
3132		unwrap: function() {
3133			return this.each(function() {
3134				var self = this, node = self.firstChild, currentNode;
3135
3136				while (node) {
3137					currentNode = node;
3138					node = node.nextSibling;
3139					self.parentNode.insertBefore(currentNode, self);
3140				}
3141			});
3142		},
3143
3144		clone: function() {
3145			var result = [];
3146
3147			this.each(function() {
3148				result.push(this.cloneNode(true));
3149			});
3150
3151			return DomQuery(result);
3152		},
3153
3154		find: function(selector) {
3155			var i, l, ret = [];
3156
3157			for (i = 0, l = this.length; i < l; i++) {
3158				DomQuery.find(selector, this[i], ret);
3159			}
3160
3161			return DomQuery(ret);
3162		},
3163
3164		push: push,
3165		sort: [].sort,
3166		splice: [].splice
3167	};
3168
3169	// Static members
3170	extend(DomQuery, {
3171		extend: extend,
3172		toArray: toArray,
3173		inArray: inArray,
3174		isArray: isArray,
3175		each: each,
3176		trim: trim,
3177		makeMap: makeMap,
3178
3179		// Sizzle
3180		find: Sizzle,
3181		expr: Sizzle.selectors,
3182		unique: Sizzle.uniqueSort,
3183		text: Sizzle.getText,
3184		isXMLDoc: Sizzle.isXML,
3185		contains: Sizzle.contains,
3186		filter: function(expr, elems, not) {
3187			if (not) {
3188				expr = ":not(" + expr + ")";
3189			}
3190
3191			return elems.length === 1 ?
3192				DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [] :
3193				DomQuery.find.matches(expr, elems);
3194		}
3195	});
3196
3197	function dir(el, prop, until) {
3198		var matched = [], cur = el[prop];
3199
3200		while (cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !DomQuery(cur).is(until))) {
3201			if (cur.nodeType === 1) {
3202				matched.push(cur);
3203			}
3204
3205			cur = cur[prop];
3206		}
3207
3208		return matched;
3209	}
3210
3211	function sibling(n, el, siblingName, nodeType) {
3212		var r = [];
3213
3214		for(; n; n = n[siblingName]) {
3215			if ((!nodeType || n.nodeType === nodeType) && n !== el) {
3216				r.push(n);
3217			}
3218		}
3219
3220		return r;
3221	}
3222
3223	each({
3224		parent: function(node) {
3225			var parent = node.parentNode;
3226
3227			return parent && parent.nodeType !== 11 ? parent : null;
3228		},
3229
3230		parents: function(node) {
3231			return dir(node, "parentNode");
3232		},
3233
3234		parentsUntil: function(node, until) {
3235			return dir(node, "parentNode", until);
3236		},
3237
3238		next: function(node) {
3239			return sibling(node, 'nextSibling', 1);
3240		},
3241
3242		prev: function(node) {
3243			return sibling(node, 'previousSibling', 1);
3244		},
3245
3246		nextNodes: function(node) {
3247			return sibling(node, 'nextSibling');
3248		},
3249
3250		prevNodes: function(node) {
3251			return sibling(node, 'previousSibling');
3252		},
3253
3254		children: function(node) {
3255			return sibling(node.firstChild, 'nextSibling', 1);
3256		},
3257
3258		contents: function(node) {
3259			return toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
3260		}
3261	}, function(name, fn){
3262		DomQuery.fn[name] = function(selector) {
3263			var self = this, result;
3264
3265			if (self.length > 1) {
3266				throw new Error("DomQuery only supports traverse functions on a single node.");
3267			}
3268
3269			if (self[0]) {
3270				result = fn(self[0], selector);
3271			}
3272
3273			result = DomQuery(result);
3274
3275			if (selector && name !== "parentsUntil") {
3276				return result.filter(selector);
3277			}
3278
3279			return result;
3280		};
3281	});
3282
3283	DomQuery.fn.filter = function(selector) {
3284		return DomQuery.filter(selector);
3285	};
3286
3287	DomQuery.fn.is = function(selector) {
3288		return !!selector && this.filter(selector).length > 0;
3289	};
3290
3291	DomQuery.fn.init.prototype = DomQuery.fn;
3292
3293	return DomQuery;
3294});
3295
3296// Included from: js/tinymce/classes/html/Styles.js
3297
3298/**
3299 * Styles.js
3300 *
3301 * Copyright, Moxiecode Systems AB
3302 * Released under LGPL License.
3303 *
3304 * License: http://www.tinymce.com/license
3305 * Contributing: http://www.tinymce.com/contributing
3306 */
3307
3308/**
3309 * This class is used to parse CSS styles it also compresses styles to reduce the output size.
3310 *
3311 * @example
3312 * var Styles = new tinymce.html.Styles({
3313 *    url_converter: function(url) {
3314 *       return url;
3315 *    }
3316 * });
3317 *
3318 * styles = Styles.parse('border: 1px solid red');
3319 * styles.color = 'red';
3320 *
3321 * console.log(new tinymce.html.StyleSerializer().serialize(styles));
3322 *
3323 * @class tinymce.html.Styles
3324 * @version 3.4
3325 */
3326define("tinymce/html/Styles", [], function() {
3327	return function(settings, schema) {
3328		/*jshint maxlen:255 */
3329		var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
3330			urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
3331			styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
3332			trimRightRegExp = /\s+$/,
3333			undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF';
3334
3335		settings = settings || {};
3336
3337		encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
3338		for (i = 0; i < encodingItems.length; i++) {
3339			encodingLookup[encodingItems[i]] = invisibleChar + i;
3340			encodingLookup[invisibleChar + i] = encodingItems[i];
3341		}
3342
3343		function toHex(match, r, g, b) {
3344			function hex(val) {
3345				val = parseInt(val, 10).toString(16);
3346
3347				return val.length > 1 ? val : '0' + val; // 0 -> 00
3348			}
3349
3350			return '#' + hex(r) + hex(g) + hex(b);
3351		}
3352
3353		return {
3354			/**
3355			 * Parses the specified RGB color value and returns a hex version of that color.
3356			 *
3357			 * @method toHex
3358			 * @param {String} color RGB string value like rgb(1,2,3)
3359			 * @return {String} Hex version of that RGB value like #FF00FF.
3360			 */
3361			toHex: function(color) {
3362				return color.replace(rgbRegExp, toHex);
3363			},
3364
3365			/**
3366			 * Parses the specified style value into an object collection. This parser will also
3367			 * merge and remove any redundant items that browsers might have added. It will also convert non hex
3368			 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
3369			 *
3370			 * @method parse
3371			 * @param {String} css Style value to parse for example: border:1px solid red;.
3372			 * @return {Object} Object representation of that style like {border: '1px solid red'}
3373			 */
3374			parse: function(css) {
3375				var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
3376				var urlConverterScope = settings.url_converter_scope || this;
3377
3378				function compress(prefix, suffix, noJoin) {
3379					var top, right, bottom, left;
3380
3381					top = styles[prefix + '-top' + suffix];
3382					if (!top) {
3383						return;
3384					}
3385
3386					right = styles[prefix + '-right' + suffix];
3387					if (!right) {
3388						return;
3389					}
3390
3391					bottom = styles[prefix + '-bottom' + suffix];
3392					if (!bottom) {
3393						return;
3394					}
3395
3396					left = styles[prefix + '-left' + suffix];
3397					if (!left) {
3398						return;
3399					}
3400
3401					var box = [top, right, bottom, left];
3402					i = box.length - 1;
3403					while (i--) {
3404						if (box[i] !== box[i + 1]) {
3405							break;
3406						}
3407					}
3408
3409					if (i > -1 && noJoin) {
3410						return;
3411					}
3412
3413					styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
3414					delete styles[prefix + '-top' + suffix];
3415					delete styles[prefix + '-right' + suffix];
3416					delete styles[prefix + '-bottom' + suffix];
3417					delete styles[prefix + '-left' + suffix];
3418				}
3419
3420				/**
3421				 * Checks if the specific style can be compressed in other words if all border-width are equal.
3422				 */
3423				function canCompress(key) {
3424					var value = styles[key], i;
3425
3426					if (!value) {
3427						return;
3428					}
3429
3430					value = value.split(' ');
3431					i = value.length;
3432					while (i--) {
3433						if (value[i] !== value[0]) {
3434							return false;
3435						}
3436					}
3437
3438					styles[key] = value[0];
3439
3440					return true;
3441				}
3442
3443				/**
3444				 * Compresses multiple styles into one style.
3445				 */
3446				function compress2(target, a, b, c) {
3447					if (!canCompress(a)) {
3448						return;
3449					}
3450
3451					if (!canCompress(b)) {
3452						return;
3453					}
3454
3455					if (!canCompress(c)) {
3456						return;
3457					}
3458
3459					// Compress
3460					styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
3461					delete styles[a];
3462					delete styles[b];
3463					delete styles[c];
3464				}
3465
3466				// Encodes the specified string by replacing all \" \' ; : with _<num>
3467				function encode(str) {
3468					isEncoded = true;
3469
3470					return encodingLookup[str];
3471				}
3472
3473				// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
3474				// It will also decode the \" \' if keep_slashes is set to fale or omitted
3475				function decode(str, keep_slashes) {
3476					if (isEncoded) {
3477						str = str.replace(/\uFEFF[0-9]/g, function(str) {
3478							return encodingLookup[str];
3479						});
3480					}
3481
3482					if (!keep_slashes) {
3483						str = str.replace(/\\([\'\";:])/g, "$1");
3484					}
3485
3486					return str;
3487				}
3488
3489				function processUrl(match, url, url2, url3, str, str2) {
3490					str = str || str2;
3491
3492					if (str) {
3493						str = decode(str);
3494
3495						// Force strings into single quote format
3496						return "'" + str.replace(/\'/g, "\\'") + "'";
3497					}
3498
3499					url = decode(url || url2 || url3);
3500
3501					if (!settings.allow_script_urls && /(java|vb)script:/i.test(url.replace(/[\s\r\n]+/, ''))) {
3502						return "";
3503					}
3504
3505					// Convert the URL to relative/absolute depending on config
3506					if (urlConverter) {
3507						url = urlConverter.call(urlConverterScope, url, 'style');
3508					}
3509
3510					// Output new URL format
3511					return "url('" + url.replace(/\'/g, "\\'") + "')";
3512				}
3513
3514				if (css) {
3515					css = css.replace(/[\u0000-\u001F]/g, '');
3516
3517					// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
3518					css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
3519						return str.replace(/[;:]/g, encode);
3520					});
3521
3522					// Parse styles
3523					while ((matches = styleRegExp.exec(css))) {
3524						name = matches[1].replace(trimRightRegExp, '').toLowerCase();
3525						value = matches[2].replace(trimRightRegExp, '');
3526
3527						if (name && value.length > 0) {
3528							if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(/.test(value))) {
3529								continue;
3530							}
3531
3532							// Opera will produce 700 instead of bold in their style values
3533							if (name === 'font-weight' && value === '700') {
3534								value = 'bold';
3535							} else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
3536								value = value.toLowerCase();
3537							}
3538
3539							// Convert RGB colors to HEX
3540							value = value.replace(rgbRegExp, toHex);
3541
3542							// Convert URLs and force them into url('value') format
3543							value = value.replace(urlOrStrRegExp, processUrl);
3544							styles[name] = isEncoded ? decode(value, true) : value;
3545						}
3546
3547						styleRegExp.lastIndex = matches.index + matches[0].length;
3548					}
3549					// Compress the styles to reduce it's size for example IE will expand styles
3550					compress("border", "", true);
3551					compress("border", "-width");
3552					compress("border", "-color");
3553					compress("border", "-style");
3554					compress("padding", "");
3555					compress("margin", "");
3556					compress2('border', 'border-width', 'border-style', 'border-color');
3557
3558					// Remove pointless border, IE produces these
3559					if (styles.border === 'medium none') {
3560						delete styles.border;
3561					}
3562
3563					// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
3564					// So lets asume it shouldn't be there
3565					if (styles['border-image'] === 'none') {
3566						delete styles['border-image'];
3567					}
3568				}
3569
3570				return styles;
3571			},
3572
3573			/**
3574			 * Serializes the specified style object into a string.
3575			 *
3576			 * @method serialize
3577			 * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
3578			 * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
3579			 * @return {String} String representation of the style object for example: border: 1px solid red.
3580			 */
3581			serialize: function(styles, element_name) {
3582				var css = '', name, value;
3583
3584				function serializeStyles(name) {
3585					var styleList, i, l, value;
3586
3587					styleList = schema.styles[name];
3588					if (styleList) {
3589						for (i = 0, l = styleList.length; i < l; i++) {
3590							name = styleList[i];
3591							value = styles[name];
3592
3593							if (value !== undef && value.length > 0) {
3594								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
3595							}
3596						}
3597					}
3598				}
3599
3600				// Serialize styles according to schema
3601				if (element_name && schema && schema.styles) {
3602					// Serialize global styles and element specific styles
3603					serializeStyles('*');
3604					serializeStyles(element_name);
3605				} else {
3606					// Output the styles in the order they are inside the object
3607					for (name in styles) {
3608						value = styles[name];
3609
3610						if (value !== undef && value.length > 0) {
3611							css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
3612						}
3613					}
3614				}
3615
3616				return css;
3617			}
3618		};
3619	};
3620});
3621
3622// Included from: js/tinymce/classes/dom/TreeWalker.js
3623
3624/**
3625 * TreeWalker.js
3626 *
3627 * Copyright, Moxiecode Systems AB
3628 * Released under LGPL License.
3629 *
3630 * License: http://www.tinymce.com/license
3631 * Contributing: http://www.tinymce.com/contributing
3632 */
3633
3634/**
3635 * TreeWalker class enables you to walk the DOM in a linear manner.
3636 *
3637 * @class tinymce.dom.TreeWalker
3638 */
3639define("tinymce/dom/TreeWalker", [], function() {
3640	return function(start_node, root_node) {
3641		var node = start_node;
3642
3643		function findSibling(node, start_name, sibling_name, shallow) {
3644			var sibling, parent;
3645
3646			if (node) {
3647				// Walk into nodes if it has a start
3648				if (!shallow && node[start_name]) {
3649					return node[start_name];
3650				}
3651
3652				// Return the sibling if it has one
3653				if (node != root_node) {
3654					sibling = node[sibling_name];
3655					if (sibling) {
3656						return sibling;
3657					}
3658
3659					// Walk up the parents to look for siblings
3660					for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
3661						sibling = parent[sibling_name];
3662						if (sibling) {
3663							return sibling;
3664						}
3665					}
3666				}
3667			}
3668		}
3669
3670		/**
3671		 * Returns the current node.
3672		 *
3673		 * @method current
3674		 * @return {Node} Current node where the walker is.
3675		 */
3676		this.current = function() {
3677			return node;
3678		};
3679
3680		/**
3681		 * Walks to the next node in tree.
3682		 *
3683		 * @method next
3684		 * @return {Node}
3684 Current node where the walker is after moving to the next node.
3685		 */
3686		this.next = function(shallow) {
3687			node = findSibling(node, 'firstChild', 'nextSibling', shallow);
3688			return node;
3689		};
3690
3691		/**
3692		 * Walks to the previous node in tree.
3693		 *
3694		 * @method prev
3695		 * @return {Node} Current node where the walker is after moving to the previous node.
3696		 */
3697		this.prev = function(shallow) {
3698			node = findSibling(node, 'lastChild', 'previousSibling', shallow);
3699			return node;
3700		};
3701	};
3702});
3703
3704// Included from: js/tinymce/classes/util/Tools.js
3705
3706/**
3707 * Tools.js
3708 *
3709 * Copyright, Moxiecode Systems AB
3710 * Released under LGPL License.
3711 *
3712 * License: http://www.tinymce.com/license
3713 * Contributing: http://www.tinymce.com/contributing
3714 */
3715
3716/**
3717 * This class contains various utlity functions. These are also exposed
3718 * directly on the tinymce namespace.
3719 *
3720 * @class tinymce.util.Tools
3721 */
3722define("tinymce/util/Tools", [], function() {
3723	/**
3724	 * Removes whitespace from the beginning and end of a string.
3725	 *
3726	 * @method trim
3727	 * @param {String} s String to remove whitespace from.
3728	 * @return {String} New string with removed whitespace.
3729	 */
3730	var whiteSpaceRegExp = /^\s*|\s*$/g;
3731	var trim = function(str) {
3732		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
3733	};
3734
3735	/**
3736	 * Returns true/false if the object is an array or not.
3737	 *
3738	 * @method isArray
3739	 * @param {Object} obj Object to check.
3740	 * @return {boolean} true/false state if the object is an array or not.
3741	 */
3742	var isArray = Array.isArray || function(obj) {
3743		return Object.prototype.toString.call(obj) === "[object Array]";
3744	};
3745
3746	/**
3747	 * Checks if a object is of a specific type for example an array.
3748	 *
3749	 * @method is
3750	 * @param {Object} o Object to check type of.
3751	 * @param {string} t Optional type to check for.
3752	 * @return {Boolean} true/false if the object is of the specified type.
3753	 */
3754	function is(o, t) {
3755		if (!t) {
3756			return o !== undefined;
3757		}
3758
3759		if (t == 'array' && isArray(o)) {
3760			return true;
3761		}
3762
3763		return typeof(o) == t;
3764	}
3765
3766	/**
3767	 * Converts the specified object into a real JavaScript array.
3768	 *
3769	 * @method toArray
3770	 * @param {Object} obj Object to convert into array.
3771	 * @return {Array} Array object based in input.
3772	 */
3773	function toArray(obj) {
3774		var array = [], i, l;
3775
3776		for (i = 0, l = obj.length; i < l; i++) {
3777			array[i] = obj[i];
3778		}
3779
3780		return array;
3781	}
3782
3783	/**
3784	 * Makes a name/object map out of an array with names.
3785	 *
3786	 * @method makeMap
3787	 * @param {Array/String} items Items to make map out of.
3788	 * @param {String} delim Optional delimiter to split string by.
3789	 * @param {Object} map Optional map to add items to.
3790	 * @return {Object} Name/value map of items.
3791	 */
3792	function makeMap(items, delim, map) {
3793		var i;
3794
3795		items = items || [];
3796		delim = delim || ',';
3797
3798		if (typeof(items) == "string") {
3799			items = items.split(delim);
3800		}
3801
3802		map = map || {};
3803
3804		i = items.length;
3805		while (i--) {
3806			map[items[i]] = {};
3807		}
3808
3809		return map;
3810	}
3811
3812	/**
3813	 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
3814	 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
3815	 * The callback has the following format: cb(value, key_or_index).
3816	 *
3817	 * @method each
3818	 * @param {Object} o Collection to iterate.
3819	 * @param {function} cb Callback function to execute for each item.
3820	 * @param {Object} s Optional scope to execute the callback in.
3821	 * @example
3822	 * // Iterate an array
3823	 * tinymce.each([1,2,3], function(v, i) {
3824	 *     console.debug("Value: " + v + ", Index: " + i);
3825	 * });
3826	 *
3827	 * // Iterate an object
3828	 * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
3829	 *     console.debug("Value: " + v + ", Key: " + k);
3830	 * });
3831	 */
3832	function each(o, cb, s) {
3833		var n, l;
3834
3835		if (!o) {
3836			return 0;
3837		}
3838
3839		s = s || o;
3840
3841		if (o.length !== undefined) {
3842			// Indexed arrays, needed for Safari
3843			for (n=0, l = o.length; n < l; n++) {
3844				if (cb.call(s, o[n], n, o) === false) {
3845					return 0;
3846				}
3847			}
3848		} else {
3849			// Hashtables
3850			for (n in o) {
3851				if (o.hasOwnProperty(n)) {
3852					if (cb.call(s, o[n], n, o) === false) {
3853						return 0;
3854					}
3855				}
3856			}
3857		}
3858
3859		return 1;
3860	}
3861
3862	/**
3863	 * Creates a new array by the return value of each iteration function call. This enables you to convert
3864	 * one array list into another.
3865	 *
3866	 * @method map
3867	 * @param {Array} a Array of items to iterate.
3868	 * @param {function} f Function to call for each item. It's return value will be the new value.
3869	 * @return {Array}
3869 Array with new values based on function return values.
3870	 */
3871	function map(a, f) {
3872		var o = [];
3873
3874		each(a, function(v) {
3875			o.push(f(v));
3876		});
3877
3878		return o;
3879	}
3880
3881	/**
3882	 * Filters out items from the input array by calling the specified function for each item.
3883	 * If the function returns false the item will be excluded if it returns true it will be included.
3884	 *
3885	 * @method grep
3886	 * @param {Array} a Array of items to loop though.
3887	 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
3888	 * @return {Array} New array with values imported and filtered based in input.
3889	 * @example
3890	 * // Filter out some items, this will return an array with 4 and 5
3891	 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
3892	 */
3893	function grep(a, f) {
3894		var o = [];
3895
3896		each(a, function(v) {
3897			if (!f || f(v)) {
3898				o.push(v);
3899			}
3900		});
3901
3902		return o;
3903	}
3904
3905	/**
3906	 * Creates a class, subclass or static singleton.
3907	 * More details on this method can be found in the Wiki.
3908	 *
3909	 * @method create
3910	 * @param {String} s Class name, inheritage and prefix.
3911	 * @param {Object} p Collection of methods to add to the class.
3912	 * @param {Object} root Optional root object defaults to the global window object.
3913	 * @example
3914	 * // Creates a basic class
3915	 * tinymce.create('tinymce.somepackage.SomeClass', {
3916	 *     SomeClass: function() {
3917	 *         // Class constructor
3918	 *     },
3919	 *
3920	 *     method: function() {
3921	 *         // Some method
3922	 *     }
3923	 * });
3924	 *
3925	 * // Creates a basic subclass class
3926	 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
3927	 *     SomeSubClass: function() {
3928	 *         // Class constructor
3929	 *         this.parent(); // Call parent constructor
3930	 *     },
3931	 *
3932	 *     method: function() {
3933	 *         // Some method
3934	 *         this.parent(); // Call parent method
3935	 *     },
3936	 *
3937	 *     'static': {
3938	 *         staticMethod: function() {
3939	 *             // Static method
3940	 *         }
3941	 *     }
3942	 * });
3943	 *
3944	 * // Creates a singleton/static class
3945	 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
3946	 *     method: function() {
3947	 *         // Some method
3948	 *     }
3949	 * });
3950	 */
3951	function create(s, p, root) {
3952		var t = this, sp, ns, cn, scn, c, de = 0;
3953
3954		// Parse : <prefix> <class>:<super class>
3955		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
3956		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
3957
3958		// Create namespace for new class
3959		ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);
3960
3961		// Class already exists
3962		if (ns[cn]) {
3963			return;
3964		}
3965
3966		// Make pure static class
3967		if (s[2] == 'static') {
3968			ns[cn] = p;
3969
3970			if (this.onCreate) {
3971				this.onCreate(s[2], s[3], ns[cn]);
3972			}
3973
3974			return;
3975		}
3976
3977		// Create default constructor
3978		if (!p[cn]) {
3979			p[cn] = function() {};
3980			de = 1;
3981		}
3982
3983		// Add constructor and methods
3984		ns[cn] = p[cn];
3985		t.extend(ns[cn].prototype, p);
3986
3987		// Extend
3988		if (s[5]) {
3989			sp = t.resolve(s[5]).prototype;
3990			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
3991
3992			// Extend constructor
3993			c = ns[cn];
3994			if (de) {
3995				// Add passthrough constructor
3996				ns[cn] = function() {
3997					return sp[scn].apply(this, arguments);
3998				};
3999			} else {
4000				// Add inherit constructor
4001				ns[cn] = function() {
4002					this.parent = sp[scn];
4003					return c.apply(this, arguments);
4004				};
4005			}
4006			ns[cn].prototype[cn] = ns[cn];
4007
4008			// Add super methods
4009			t.each(sp, function(f, n) {
4010				ns[cn].prototype[n] = sp[n];
4011			});
4012
4013			// Add overridden methods
4014			t.each(p, function(f, n) {
4015				// Extend methods if needed
4016				if (sp[n]) {
4017					ns[cn].prototype[n] = function() {
4018						this.parent = sp[n];
4019						return f.apply(this, arguments);
4020					};
4021				} else {
4022					if (n != cn) {
4023						ns[cn].prototype[n] = f;
4024					}
4025				}
4026			});
4027		}
4028
4029		// Add static methods
4030		/*jshint sub:true*/
4031		t.each(p['static'], function(f, n) {
4032			ns[cn][n] = f;
4033		});
4034	}
4035
4036	/**
4037	 * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
4038	 *
4039	 * @method inArray
4040	 * @param {Array} a Array/Object to search for value in.
4041	 * @param {Object} v Value to check for inside the array.
4042	 * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
4043	 * @example
4044	 * // Get index of value in array this will alert 1 since 2 is at that index
4045	 * alert(tinymce.inArray([1,2,3], 2));
4046	 */
4047	function inArray(a, v) {
4048		var i, l;
4049
4050		if (a) {
4051			for (i = 0, l = a.length; i < l; i++) {
4052				if (a[i] === v) {
4053					return i;
4054				}
4055			}
4056		}
4057
4058		return -1;
4059	}
4060
4061	function extend(obj, ext) {
4062		var i, l, name, args = arguments, value;
4063
4064		for (i = 1, l = args.length; i < l; i++) {
4065			ext = args[i];
4066			for (name in ext) {
4067				if (ext.hasOwnProperty(name)) {
4068					value = ext[name];
4069
4070					if (value !== undefined) {
4071						obj[name] = value;
4072					}
4073				}
4074			}
4075		}
4076
4077		return obj;
4078	}
4079
4080	/**
4081	 * Executed the specified function for each item in a object tree.
4082	 *
4083	 * @method walk
4084	 * @param {Object} o Object tree to walk though.
4085	 * @param {function} f Function to call for each item.
4086	 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
4087	 * @param {String} s Optional scope to execute the function in.
4088	 */
4089	function walk(o, f, n, s) {
4090		s = s || this;
4091
4092		if (o) {
4093			if (n) {
4094				o = o[n];
4095			}
4096
4097			each(o, function(o, i) {
4098				if (f.call(s, o, i, n) === false) {
4099					return false;
4100				}
4101
4102				walk(o, f, n, s);
4103			});
4104		}
4105	}
4106
4107	/**
4108	 * Creates a namespace on a specific object.
4109	 *
4110	 * @method createNS
4111	 * @param {String} n Namespace to create for example a.b.c.d.
4112	 * @param {Object} o Optional object to add namespace to, defaults to window.
4113	 * @return {Object} New namespace object the last item in path.
4114	 * @example
4115	 * // Create some namespace
4116	 * tinymce.createNS('tinymce.somepackage.subpackage');
4117	 *
4118	 * // Add a singleton
4119	 * var tinymce.somepackage.subpackage.SomeSingleton = {
4120	 *     method: function() {
4121	 *         // Some method
4122	 *     }
4123	 * };
4124	 */
4125	function createNS(n, o) {
4126		var i, v;
4127
4128		o = o || window;
4129
4130		n = n.split('.');
4131		for (i=0; i<n.length; i++) {
4132			v = n[i];
4133
4134			if (!o[v]) {
4135				o[v] = {};
4136			}
4137
4138			o = o[v];
4139		}
4140
4141		return o;
4142	}
4143
4144	/**
4145	 * Resolves a string and returns the object from a specific structure.
4146	 *
4147	 * @method resolve
4148	 * @param {String} n Path to resolve for example a.b.c.d.
4149	 * @param {Object} o Optional object to search though, defaults to window.
4150	 * @return {Object} Last object in path or null if it couldn't be resolved.
4151	 * @example
4152	 * // Resolve a path into an object reference
4153	 * var obj = tinymce.resolve('a.b.c.d');
4154	 */
4155	function resolve(n, o) {
4156		var i, l;
4157
4158		o = o || window;
4159
4160		n = n.split('.');
4161		for (i = 0, l = n.length; i < l; i++) {
4162			o = o[n[i]];
4163
4164			if (!o) {
4165				break;
4166			}
4167		}
4168
4169		return o;
4170	}
4171
4172	/**
4173	 * Splits a string but removes the whitespace before and after each value.
4174	 *
4175	 * @method explode
4176	 * @param {string} s String to split.
4177	 * @param {string} d Delimiter to split by.
4178	 * @example
4179	 * // Split a string into an array with a,b,c
4180	 * var arr = tinymce.explode('a, b,   c');
4181	 */
4182	function explode(s, d) {
4183		if (!s || is(s, 'array')) {
4184			return s;
4185		}
4186
4187		return map(s.split(d || ','), trim);
4188	}
4189
4190	return {
4191		trim: trim,
4192		isArray: isArray,
4193		is: is,
4194		toArray: toArray,
4195		makeMap: makeMap,
4196		each: each,
4197		map: map,
4198		grep: grep,
4199		inArray: inArray,
4200		extend: extend,
4201		create: create,
4202		walk: walk,
4203		createNS: createNS,
4204		resolve: resolve,
4205		explode: explode
4206	};
4207});
4208
4209// Included from: js/tinymce/classes/dom/Range.js
4210
4211/**
4212 * Range.js
4213 *
4214 * Copyright, Moxiecode Systems AB
4215 * Released under LGPL License.
4216 *
4217 * License: http://www.tinymce.com/license
4218 * Contributing: http://www.tinymce.com/contributing
4219 */
4220
4221define("tinymce/dom/Range", [
4222	"tinymce/util/Tools"
4223], function(Tools) {
4224	// Range constructor
4225	function Range(dom) {
4226		var t = this,
4227			doc = dom.doc,
4228			EXTRACT = 0,
4229			CLONE = 1,
4230			DELETE = 2,
4231			TRUE = true,
4232			FALSE = false,
4233			START_OFFSET = 'startOffset',
4234			START_CONTAINER = 'startContainer',
4235			END_CONTAINER = 'endContainer',
4236			END_OFFSET = 'endOffset',
4237			extend = Tools.extend,
4238			nodeIndex = dom.nodeIndex;
4239
4240		function createDocumentFragment() {
4241			return doc.createDocumentFragment();
4242		}
4243
4244		function setStart(n, o) {
4245			_setEndPoint(TRUE, n, o);
4246		}
4247
4248		function setEnd(n, o) {
4249			_setEndPoint(FALSE, n, o);
4250		}
4251
4252		function setStartBefore(n) {
4253			setStart(n.parentNode, nodeIndex(n));
4254		}
4255
4256		function setStartAfter(n) {
4257			setStart(n.parentNode, nodeIndex(n) + 1);
4258		}
4259
4260		function setEndBefore(n) {
4261			setEnd(n.parentNode, nodeIndex(n));
4262		}
4263
4264		function setEndAfter(n) {
4265			setEnd(n.parentNode, nodeIndex(n) + 1);
4266		}
4267
4268		function collapse(ts) {
4269			if (ts) {
4270				t[END_CONTAINER] = t[START_CONTAINER];
4271				t[END_OFFSET] = t[START_OFFSET];
4272			} else {
4273				t[START_CONTAINER] = t[END_CONTAINER];
4274				t[START_OFFSET] = t[END_OFFSET];
4275			}
4276
4277			t.collapsed = TRUE;
4278		}
4279
4280		function selectNode(n) {
4281			setStartBefore(n);
4282			setEndAfter(n);
4283		}
4284
4285		function selectNodeContents(n) {
4286			setStart(n, 0);
4287			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
4288		}
4289
4290		function compareBoundaryPoints(h, r) {
4291			var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],
4292			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
4293
4294			// Check START_TO_START
4295			if (h === 0) {
4296				return _compareBoundaryPoints(sc, so, rsc, rso);
4297			}
4298
4299			// Check START_TO_END
4300			if (h === 1) {
4301				return _compareBoundaryPoints(ec, eo, rsc, rso);
4302			}
4303
4304			// Check END_TO_END
4305			if (h === 2) {
4306				return _compareBoundaryPoints(ec, eo, rec, reo);
4307			}
4308
4309			// Check END_TO_START
4310			if (h === 3) {
4311				return _compareBoundaryPoints(sc, so, rec, reo);
4312			}
4313		}
4314
4315		function deleteContents() {
4316			_traverse(DELETE);
4317		}
4318
4319		function extractContents() {
4320			return _traverse(EXTRACT);
4321		}
4322
4323		function cloneContents() {
4324			return _traverse(CLONE);
4325		}
4326
4327		function insertNode(n) {
4328			var startContainer = this[START_CONTAINER],
4329				startOffset = this[START_OFFSET], nn, o;
4330
4331			// Node is TEXT_NODE or CDATA
4332			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
4333				if (!startOffset) {
4334					// At the start of text
4335					startContainer.parentNode.insertBefore(n, startContainer);
4336				} else if (startOffset >= startContainer.nodeValue.length) {
4337					// At the end of text
4338					dom.insertAfter(n, startContainer);
4339				} else {
4340					// Middle, need to split
4341					nn = startContainer.splitText(startOffset);
4342					startContainer.parentNode.insertBefore(n, nn);
4343				}
4344			} else {
4345				// Insert element node
4346				if (startContainer.childNodes.length > 0) {
4347					o = startContainer.childNodes[startOffset];
4348				}
4349
4350				if (o) {
4351					startContainer.insertBefore(n, o);
4352				} else {
4353					if (startContainer.nodeType == 3) {
4354						dom.insertAfter(n, startContainer);
4355					} else {
4356						startContainer.appendChild(n);
4357					}
4358				}
4359			}
4360		}
4361
4362		function surroundContents(n) {
4363			var f = t.extractContents();
4364
4365			t.insertNode(n);
4366			n.appendChild(f);
4367			t.selectNode(n);
4368		}
4369
4370		function cloneRange() {
4371			return extend(new Range(dom), {
4372				startContainer: t[START_CONTAINER],
4373				startOffset: t[START_OFFSET],
4374				endContainer: t[END_CONTAINER],
4375				endOffset: t[END_OFFSET],
4376				collapsed: t.collapsed,
4377				commonAncestorContainer: t.commonAncestorContainer
4378			});
4379		}
4380
4381		// Private methods
4382
4383		function _getSelectedNode(container, offset) {
4384			var child;
4385
4386			if (container.nodeType == 3 /* TEXT_NODE */) {
4387				return container;
4388			}
4389
4390			if (offset < 0) {
4391				return container;
4392			}
4393
4394			child = container.firstChild;
4395			while (child && offset > 0) {
4396				--offset;
4397				child = child.nextSibling;
4398			}
4399
4400			if (child) {
4401				return child;
4402			}
4403
4404			return container;
4405		}
4406
4407		function _isCollapsed() {
4408			return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);
4409		}
4410
4411		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
4412			var c, offsetC, n, cmnRoot, childA, childB;
4413
4414			// In the first case the boundary-points have the same container. A is before B
4415			// if its offset is less than the offset of B, A is equal to B if its offset is
4416			// equal to the offset of B, and A is after B if its offset is greater than the
4417			// offset of B.
4418			if (containerA == containerB) {
4419				if (offsetA == offsetB) {
4420					return 0; // equal
4421				}
4422
4423				if (offsetA < offsetB) {
4424					return -1; // before
4425				}
4426
4427				return 1; // after
4428			}
4429
4430			// In the second case a child node C of the container of A is an ancestor
4431			// container of B. In this case, A is before B if the offset of A is less than or
4432			// equal to the index of the child node C and A is after B otherwise.
4433			c = containerB;
4434			while (c && c.parentNode != containerA) {
4435				c = c.parentNode;
4436			}
4437
4438			if (c) {
4439				offsetC = 0;
4440				n = containerA.firstChild;
4441
4442				while (n != c && offsetC < offsetA) {
4443					offsetC++;
4444					n = n.nextSibling;
4445				}
4446
4447				if (offsetA <= offsetC) {
4448					return -1; // before
4449				}
4450
4451				return 1; // after
4452			}
4453
4454			// In the third case a child node C of the container of B is an ancestor container
4455			// of A. In this case, A is before B if the index of the child node C is less than
4456			// the offset of B and A is after B otherwise.
4457			c = containerA;
4458			while (c && c.parentNode != containerB) {
4459				c = c.parentNode;
4460			}
4461
4462			if (c) {
4463				offsetC = 0;
4464				n = containerB.firstChild;
4465
4466				while (n != c && offsetC < offsetB) {
4467					offsetC++;
4468					n = n.nextSibling;
4469				}
4470
4471				if (offsetC < offsetB) {
4472					return -1; // before
4473				}
4474
4475				return 1; // after
4476			}
4477
4478			// In the fourth case, none of three other cases hold: the containers of A and B
4479			// are siblings or descendants of sibling nodes. In this case, A is before B if
4480			// the container of A is before the container of B in a pre-order traversal of the
4481			// Ranges' context tree and A is after B otherwise.
4482			cmnRoot = dom.findCommonAncestor(containerA, containerB);
4483			childA = containerA;
4484
4485			while (childA && childA.parentNode != cmnRoot) {
4486				childA = childA.parentNode;
4487			}
4488
4489			if (!childA) {
4490				childA = cmnRoot;
4491			}
4492
4493			childB = containerB;
4494			while (childB && childB.parentNode != cmnRoot) {
4495				childB = childB.parentNode;
4496			}
4497
4498			if (!childB) {
4499				childB = cmnRoot;
4500			}
4501
4502			if (childA == childB) {
4503				return 0; // equal
4504			}
4505
4506			n = cmnRoot.firstChild;
4507			while (n) {
4508				if (n == childA) {
4509					return -1; // before
4510				}
4511
4512				if (n == childB) {
4513					return 1; // after
4514				}
4515
4516				n = n.nextSibling;
4517			}
4518		}
4519
4520		function _setEndPoint(st, n, o) {
4521			var ec, sc;
4522
4523			if (st) {
4524				t[START_CONTAINER] = n;
4525				t[START_OFFSET] = o;
4526			} else {
4527				t[END_CONTAINER] = n;
4528				t[END_OFFSET] = o;
4529			}
4530
4531			// If one boundary-point of a Range is set to have a root container
4532			// other than the current one for the Range, the Range is collapsed to
4533			// the new position. This enforces the restriction that both boundary-
4534			// points of a Range must have the same root container.
4535			ec = t[END_CONTAINER];
4536			while (ec.parentNode) {
4537				ec = ec.parentNode;
4538			}
4539
4540			sc = t[START_CONTAINER];
4541			while (sc.parentNode) {
4542				sc = sc.parentNode;
4543			}
4544
4545			if (sc == ec) {
4546				// The start position of a Range is guaranteed to never be after the
4547				// end position. To enforce this restriction, if the start is set to
4548				// be at a position after the end, the Range is collapsed to that
4549				// position.
4550				if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0) {
4551					t.collapse(st);
4552				}
4553			} else {
4554				t.collapse(st);
4555			}
4556
4557			t.collapsed = _isCollapsed();
4558			t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);
4559		}
4560
4561		function _traverse(how) {
4562			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
4563
4564			if (t[START_CONTAINER] == t[END_CONTAINER]) {
4565				return _traverseSameContainer(how);
4566			}
4567
4568			for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
4569				if (p == t[START_CONTAINER]) {
4570					return _traverseCommonStartContainer(c, how);
4571				}
4572
4573				++endContainerDepth;
4574			}
4575
4576			for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
4577				if (p == t[END_CONTAINER]) {
4578					return _traverseCommonEndContainer(c, how);
4579				}
4580
4581				++startContainerDepth;
4582			}
4583
4584			depthDiff = startContainerDepth - endContainerDepth;
4585
4586			startNode = t[START_CONTAINER];
4587			while (depthDiff > 0) {
4588				startNode = startNode.parentNode;
4589				depthDiff--;
4590			}
4591
4592			endNode = t[END_CONTAINER];
4593			while (depthDiff < 0) {
4594				endNode = endNode.parentNode;
4595				depthDiff++;
4596			}
4597
4598			// ascend the ancestor hierarchy until we have a common parent.
4599			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
4600				startNode = sp;
4601				endNode = ep;
4602			}
4603
4604			return _traverseCommonAncestors(startNode, endNode, how);
4605		}
4606
4607		 function _traverseSameContainer(how) {
4608			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
4609
4610			if (how != DELETE) {
4611				frag = createDocumentFragment();
4612			}
4613
4614			// If selection is empty, just return the fragment
4615			if (t[START_OFFSET] == t[END_OFFSET]) {
4616				return frag;
4617			}
4618
4619			// Text node needs special case handling
4620			if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
4621				// get the substring
4622				s = t[START_CONTAINER].nodeValue;
4623				sub = s.substring(t[START_OFFSET], t[END_OFFSET]);
4624
4625				// set the original text node to its new value
4626				if (how != CLONE) {
4627					n = t[START_CONTAINER];
4628					start = t[START_OFFSET];
4629					len = t[END_OFFSET] - t[START_OFFSET];
4630
4631					if (start === 0 && len >= n.nodeValue.length - 1) {
4632						n.parentNode.removeChild(n);
4633					} else {
4634						n.deleteData(start, len);
4635					}
4636
4637					// Nothing is partially selected, so collapse to start point
4638					t.collapse(TRUE);
4639				}
4640
4641				if (how == DELETE) {
4642					return;
4643				}
4644
4645				if (sub.length > 0) {
4646					frag.appendChild(doc.createTextNode(sub));
4647				}
4648
4649				return frag;
4650			}
4651
4652			// Copy nodes between the start/end offsets.
4653			n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);
4654			cnt = t[END_OFFSET] - t[START_OFFSET];
4655
4656			while (n && cnt > 0) {
4657				sibling = n.nextSibling;
4658				xferNode = _traverseFullySelected(n, how);
4659
4660				if (frag) {
4661					frag.appendChild(xferNode);
4662				}
4663
4664				--cnt;
4665				n = sibling;
4666			}
4667
4668			// Nothing is partially selected, so collapse to start point
4669			if (how != CLONE) {
4670				t.collapse(TRUE);
4671			}
4672
4673			return frag;
4674		}
4675
4676		function _traverseCommonStartContainer(endAncestor, how) {
4677			var frag, n, endIdx, cnt, sibling, xferNode;
4678
4679			if (how != DELETE) {
4680				frag = createDocumentFragment();
4681			}
4682
4683			n = _traverseRightBoundary(endAncestor, how);
4684
4685			if (frag) {
4686				frag.appendChild(n);
4687			}
4688
4689			endIdx = nodeIndex(endAncestor);
4690			cnt = endIdx - t[START_OFFSET];
4691
4692			if (cnt <= 0) {
4693				// Collapse to just before the endAncestor, which
4694				// is partially selected.
4695				if (how != CLONE) {
4696					t.setEndBefore(endAncestor);
4697					t.collapse(FALSE);
4698				}
4699
4700				return frag;
4701			}
4702
4703			n = endAncestor.previousSibling;
4704			while (cnt > 0) {
4705				sibling = n.previousSibling;
4706				xferNode = _traverseFullySelected(n, how);
4707
4708				if (frag) {
4709					frag.insertBefore(xferNode, frag.firstChild);
4710				}
4711
4712				--cnt;
4713				n = sibling;
4714			}
4715
4716			// Collapse to just before the endAncestor, which
4717			// is partially selected.
4718			if (how != CLONE) {
4719				t.setEndBefore(endAncestor);
4720				t.collapse(FALSE);
4721			}
4722
4723			return frag;
4724		}
4725
4726		function _traverseCommonEndContainer(startAncestor, how) {
4727			var frag, startIdx, n, cnt, sibling, xferNode;
4728
4729			if (how != DELETE) {
4730				frag = createDocumentFragment();
4731			}
4732
4733			n = _traverseLeftBoundary(startAncestor, how);
4734			if (frag) {
4735				frag.appendChild(n);
4736			}
4737
4738			startIdx = nodeIndex(startAncestor);
4739			++startIdx; // Because we already traversed it
4740
4741			cnt = t[END_OFFSET] - startIdx;
4742			n = startAncestor.nextSibling;
4743			while (n && cnt > 0) {
4744				sibling = n.nextSibling;
4745				xferNode = _traverseFullySelected(n, how);
4746
4747				if (frag) {
4748					frag.appendChild(xferNode);
4749				}
4750
4751				--cnt;
4752				n = sibling;
4753			}
4754
4755			if (how != CLONE) {
4756				t.setStartAfter(startAncestor);
4757				t.collapse(TRUE);
4758			}
4759
4760			return frag;
4761		}
4762
4763		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
4764			var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
4765
4766			if (how != DELETE) {
4767				frag = createDocumentFragment();
4768			}
4769
4770			n = _traverseLeftBoundary(startAncestor, how);
4771			if (frag) {
4772				frag.appendChild(n);
4773			}
4774
4775			commonParent = startAncestor.parentNode;
4776			startOffset = nodeIndex(startAncestor);
4777			endOffset = nodeIndex(endAncestor);
4778			++startOffset;
4779
4780			cnt = endOffset - startOffset;
4781			sibling = startAncestor.nextSibling;
4782
4783			while (cnt > 0) {
4784				nextSibling = sibling.nextSibling;
4785				n = _traverseFullySelected(sibling, how);
4786
4787				if (frag) {
4788					frag.appendChild(n);
4789				}
4790
4791				sibling = nextSibling;
4792				--cnt;
4793			}
4794
4795			n = _traverseRightBoundary(endAncestor, how);
4796
4797			if (frag) {
4798				frag.appendChild(n);
4799			}
4800
4801			if (how != CLONE) {
4802				t.setStartAfter(startAncestor);
4803				t.collapse(TRUE);
4804			}
4805
4806			return frag;
4807		}
4808
4809		function _traverseRightBoundary(root, how) {
4810			var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent;
4811			var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];
4812
4813			if (next == root) {
4814				return _traverseNode(next, isFullySelected, FALSE, how);
4815			}
4816
4817			parent = next.parentNode;
4818			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
4819
4820			while (parent) {
4821				while (next) {
4822					prevSibling = next.previousSibling;
4823					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
4824
4825					if (how != DELETE) {
4826						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
4827					}
4828
4829					isFullySelected = TRUE;
4830					next = prevSibling;
4831				}
4832
4833				if (parent == root) {
4834					return clonedParent;
4835				}
4836
4837				next = parent.previousSibling;
4838				parent = parent.parentNode;
4839
4840				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
4841
4842				if (how != DELETE) {
4843					clonedGrandParent.appendChild(clonedParent);
4844				}
4845
4846				clonedParent = clonedGrandParent;
4847			}
4848		}
4849
4850		function _traverseLeftBoundary(root, how) {
4851			var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER];
4852			var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
4853
4854			if (next == root) {
4855				return _traverseNode(next, isFullySelected, TRUE, how);
4856			}
4857
4858			parent = next.parentNode;
4859			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
4860
4861			while (parent) {
4862				while (next) {
4863					nextSibling = next.nextSibling;
4864					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
4865
4866					if (how != DELETE) {
4867						clonedParent.appendChild(clonedChild);
4868					}
4869
4870					isFullySelected = TRUE;
4871					next = nextSibling;
4872				}
4873
4874				if (parent == root) {
4875					return clonedParent;
4876				}
4877
4878				next = parent.nextSibling;
4879				parent = parent.parentNode;
4880
4881				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
4882
4883				if (how != DELETE) {
4884					clonedGrandParent.appendChild(clonedParent);
4885				}
4886
4887				clonedParent = clonedGrandParent;
4888			}
4889		}
4890
4891		function _traverseNode(n, isFullySelected, isLeft, how) {
4892			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
4893
4894			if (isFullySelected) {
4895				return _traverseFullySelected(n, how);
4896			}
4897
4898			if (n.nodeType == 3 /* TEXT_NODE */) {
4899				txtValue = n.nodeValue;
4900
4901				if (isLeft) {
4902					offset = t[START_OFFSET];
4903					newNodeValue = txtValue.substring(offset);
4904					oldNodeValue = txtValue.substring(0, offset);
4905				} else {
4906					offset = t[END_OFFSET];
4907					newNodeValue = txtValue.substring(0, offset);
4908					oldNodeValue = txtValue.substring(offset);
4909				}
4910
4911				if (how != CLONE) {
4912					n.nodeValue = oldNodeValue;
4913				}
4914
4915				if (how == DELETE) {
4916					return;
4917				}
4918
4919				newNode = dom.clone(n, FALSE);
4920				newNode.nodeValue = newNodeValue;
4921
4922				return newNode;
4923			}
4924
4925			if (how == DELETE) {
4926				return;
4927			}
4928
4929			return dom.clone(n, FALSE);
4930		}
4931
4932		function _traverseFullySelected(n, how) {
4933			if (how != DELETE) {
4934				return how == CLONE ? dom.clone(n, TRUE) : n;
4935			}
4936
4937			n.parentNode.removeChild(n);
4938		}
4939
4940		function toStringIE() {
4941			return dom.create('body', null, cloneContents()).outerText;
4942		}
4943
4944		extend(t, {
4945			// Inital states
4946			startContainer: doc,
4947			startOffset: 0,
4948			endContainer: doc,
4949			endOffset: 0,
4950			collapsed: TRUE,
4951			commonAncestorContainer: doc,
4952
4953			// Range constants
4954			START_TO_START: 0,
4955			START_TO_END: 1,
4956			END_TO_END: 2,
4957			END_TO_START: 3,
4958
4959			// Public methods
4960			setStart: setStart,
4961			setEnd: setEnd,
4962			setStartBefore: setStartBefore,
4963			setStartAfter: setStartAfter,
4964			setEndBefore: setEndBefore,
4965			setEndAfter: setEndAfter,
4966			collapse: collapse,
4967			selectNode: selectNode,
4968			selectNodeContents: selectNodeContents,
4969			compareBoundaryPoints: compareBoundaryPoints,
4970			deleteContents: deleteContents,
4971			extractContents: extractContents,
4972			cloneContents: cloneContents,
4973			insertNode: insertNode,
4974			surroundContents: surroundContents,
4975			cloneRange: cloneRange,
4976			toStringIE: toStringIE
4977		});
4978
4979		return t;
4980	}
4981
4982	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
4983	Range.prototype.toString = function() {
4984		return this.toStringIE();
4985	};
4986
4987	return Range;
4988});
4989
4990// Included from: js/tinymce/classes/html/Entities.js
4991
4992/**
4993 * Entities.js
4994 *
4995 * Copyright, Moxiecode Systems AB
4996 * Released under LGPL License.
4997 *
4998 * License: http://www.tinymce.com/license
4999 * Contributing: http://www.tinymce.com/contributing
5000 */
5001
5002/*jshint bitwise:false */
5003
5004/**
5005 * Entity encoder class.
5006 *
5007 * @class tinymce.html.Entities
5008 * @static
5009 * @version 3.4
5010 */
5011define("tinymce/html/Entities", [
5012	"tinymce/util/Tools"
5013], function(Tools) {
5014	var makeMap = Tools.makeMap;
5015
5016	var namedEntities, baseEntities, reverseEntities,
5017		attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
5018		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
5019		rawCharsRegExp = /[<>&\"\']/g,
5020		entityRegExp = /&(#x|#)?([\w]+);/g,
5021		asciiMap = {
5022			128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
5023			135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
5024			142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
5025			150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
5026			156: "\u0153", 158: "\u017E", 159: "\u0178"
5027		};
5028
5029	// Raw entities
5030	baseEntities = {
5031		'\"': '&quot;
5031', // Needs to be escaped since the YUI compressor would otherwise break the code
5032		"'": '&#39;',
5033		'<': '&lt;',
5034		'>': '&gt;',
5035		'&': '&amp;'
5036	};
5037
5038	// Reverse lookup table for raw entities
5039	reverseEntities = {
5040		'&lt;': '<',
5041		'&gt;': '>',
5042		'&amp;': '&',
5043		'&quot;': '"',
5044		'&apos;': "'"
5045	};
5046
5047	// Decodes text by using the browser
5048	function nativeDecode(text) {
5049		var elm;
5050
5051		elm = document.createElement("div");
5052		elm.innerHTML = text;
5053
5054		return elm.textContent || elm.innerText || text;
5055	}
5056
5057	// Build a two way lookup table for the entities
5058	function buildEntitiesLookup(items, radix) {
5059		var i, chr, entity, lookup = {};
5060
5061		if (items) {
5062			items = items.split(',');
5063			radix = radix || 10;
5064
5065			// Build entities lookup table
5066			for (i = 0; i < items.length; i += 2) {
5067				chr = String.fromCharCode(parseInt(items[i], radix));
5068
5069				// Only add non base entities
5070				if (!baseEntities[chr]) {
5071					entity = '&' + items[i + 1] + ';';
5072					lookup[chr] = entity;
5073					lookup[entity] = chr;
5074				}
5075			}
5076
5077			return lookup;
5078		}
5079	}
5080
5081	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
5082	namedEntities = buildEntitiesLookup(
5083		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
5084		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
5085		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
5086		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
5087		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
5088		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
5089		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
5090		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
5091		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
5092		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
5093		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
5094		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
5095		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
5096		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
5097		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
5098		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
5099		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
5100		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
5101		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
5102		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
5103		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
5104		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
5105		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
5106		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
5107		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
5108
5109	var Entities = {
5110		/**
5111		 * Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
5112		 *
5113		 * @method encodeRaw
5114		 * @param {String} text Text to encode.
5115		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
5116		 * @return {String} Entity encoded text.
5117		 */
5118		encodeRaw: function(text, attr) {
5119			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5120				return baseEntities[chr] || chr;
5121			});
5122		},
5123
5124		/**
5125		 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
5126		 * since it doesn't know if the context is within a attribute or text node. This was added for c
5126ompatibility
5127		 * and is exposed as the DOMUtils.encode function.
5128		 *
5129		 * @method encodeAllRaw
5130		 * @param {String} text Text to encode.
5131		 * @return {String} Entity encoded text.
5132		 */
5133		encodeAllRaw: function(text) {
5134			return ('' + text).replace(rawCharsRegExp, function(chr) {
5135				return baseEntities[chr] || chr;
5136			});
5137		},
5138
5139		/**
5140		 * Encodes the specified string using numeric entities. The core entities will be
5141		 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
5142		 *
5143		 * @method encodeNumeric
5144		 * @param {String} text Text to encode.
5145		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
5146		 * @return {String} Entity encoded text.
5147		 */
5148		encodeNumeric: function(text, attr) {
5149			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5150				// Multi byte sequence convert it to a single entity
5151				if (chr.length > 1) {
5152					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
5153				}
5154
5155				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
5156			});
5157		},
5158
5159		/**
5160		 * Encodes the specified string using named entities. The core entities will be encoded
5161		 * as named ones but all non lower ascii characters will be encoded into named entities.
5162		 *
5163		 * @method encodeNamed
5164		 * @param {String} text Text to encode.
5165		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
5166		 * @param {Object} entities Optional parameter with entities to use.
5167		 * @return {String} Entity encoded text.
5168		 */
5169		encodeNamed: function(text, attr, entities) {
5170			entities = entities || namedEntities;
5171
5172			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5173				return baseEntities[chr] || entities[chr] || chr;
5174			});
5175		},
5176
5177		/**
5178		 * Returns an encode function based on the name(s) and it's optional entities.
5179		 *
5180		 * @method getEncodeFunc
5181		 * @param {String} name Comma separated list of encoders for example named,numeric.
5182		 * @param {String} entities Optional parameter with entities to use instead of the built in set.
5183		 * @return {function} Encode function to be used.
5184		 */
5185		getEncodeFunc: function(name, entities) {
5186			entities = buildEntitiesLookup(entities) || namedEntities;
5187
5188			function encodeNamedAndNumeric(text, attr) {
5189				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5190					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
5191				});
5192			}
5193
5194			function encodeCustomNamed(text, attr) {
5195				return Entities.encodeNamed(text, attr, entities);
5196			}
5197
5198			// Replace + with , to be compatible with previous TinyMCE versions
5199			name = makeMap(name.replace(/\+/g, ','));
5200
5201			// Named and numeric encoder
5202			if (name.named && name.numeric) {
5203				return encodeNamedAndNumeric;
5204			}
5205
5206			// Named encoder
5207			if (name.named) {
5208				// Custom names
5209				if (entities) {
5210					return encodeCustomNamed;
5211				}
5212
5213				return Entities.encodeNamed;
5214			}
5215
5216			// Numeric
5217			if (name.numeric) {
5218				return Entities.encodeNumeric;
5219			}
5220
5221			// Raw encoder
5222			return Entities.encodeRaw;
5223		},
5224
5225		/**
5226		 * Decodes the specified string, this will replace entities with raw UTF characters.
5227		 *
5228		 * @method decode
5229		 * @param {String} text Text to entity decode.
5230		 * @return {String} Entity decoded string.
5231		 */
5232		decode: function(text) {
5233			return text.replace(entityRegExp, function(all, numeric, value) {
5234				if (numeric) {
5235					value = parseInt(value, numeric.length === 2 ? 16 : 10);
5236
5237					// Support upper UTF
5238					if (value > 0xFFFF) {
5239						value -= 0x10000;
5240
5241						return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
5242					} else {
5243						return asciiMap[value] || String.fromCharCode(value);
5244					}
5245				}
5246
5247				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
5248			});
5249		}
5250	};
5251
5252	return Entities;
5253});
5254
5255// Included from: js/tinymce/classes/Env.js
5256
5257/**
5258 * Env.js
5259 *
5260 * Copyright, Moxiecode Systems AB
5261 * Released under LGPL License.
5262 *
5263 * License: http://www.tinymce.com/license
5264 * Contributing: http://www.tinymce.com/contributing
5265 */
5266
5267/**
5268 * This class contains various environment constants like browser versions etc.
5269 * Normally you don't want to sniff specific browser versions but sometimes you have
5270 * to when it's impossible to feature detect. So use this with care.
5271 *
5272 * @class tinymce.Env
5273 * @static
5274 */
5275define("tinymce/Env", [], function() {
5276	var nav = navigator, userAgent = nav.userAgent;
5277	var opera, webkit, ie, ie11, gecko, mac, iDevice;
5278
5279	opera = window.opera && window.opera.buildNumber;
5280	webkit = /WebKit/.test(userAgent);
5281	ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
5282	ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
5283	ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
5284	ie = ie || ie11;
5285	gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
5286	mac = userAgent.indexOf('Mac') != -1;
5287	iDevice = /(iPad|iPhone)/.test(userAgent);
5288
5289	// Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
5290	// says it has contentEditable support but there is no visible caret.
5291	var contentEditable = !iDevice || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
5292
5293	return {
5294		/**
5295		 * Constant that is true if the browser is Opera.
5296		 *
5297		 * @property opera
5298		 * @type Boolean
5299		 * @final
5300		 */
5301		opera: opera,
5302
5303		/**
5304		 * Constant that is true if the browser is WebKit (Safari/Chrome).
5305		 *
5306		 * @property webKit
5307		 * @type Boolean
5308		 * @final
5309		 */
5310		webkit: webkit,
5311
5312		/**
5313		 * Constant that is more than zero if the browser is IE.
5314		 *
5315		 * @property ie
5316		 * @type Boolean
5317		 * @final
5318		 */
5319		ie: ie,
5320
5321		/**
5322		 * Constant that is true if the browser is Gecko.
5323		 *
5324		 * @property gecko
5325		 * @type Boolean
5326		 * @final
5327		 */
5328		gecko: gecko,
5329
5330		/**
5331		 * Constant that is true if the os is Mac OS.
5332		 *
5333		 * @property mac
5334		 * @type Boolean
5335		 * @final
5336		 */
5337		mac: mac,
5338
5339		/**
5340		 * Constant that is true if the os is iOS.
5341		 *
5342		 * @property iOS
5343		 * @type Boolean
5344		 * @final
5345		 */
5346		iOS: iDevice,
5347
5348		/**
5349		 * Constant that is true if the browser supports editing.
5350		 *
5351		 * @property contentEditable
5352		 * @type Boolean
5353		 * @final
5354		 */
5355		contentEditable: contentEditable,
5356
5357		/**
5358		 * Transparent image data url.
5359		 *
5360		 * @property transparentSrc
5361		 * @type Boolean
5362		 * @final
5363		 */
5364		transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
5365
5366		/**
5367		 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
5368		 *
5369		 * @property noCaretAfter
5370		 * @type Boolean
5371		 * @final
5372		 */
5373		caretAfter: ie != 8,
5374
5375		/**
5376		 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
5377		 *
5378		 * @property range
5379		 * @type Boolean
5380		 */
5381		range: window.getSelection && "Range" in window,
5382
5383		/**
5384		 * Returns the IE document mode for non IE browsers this will fake IE 10.
5385		 *
5386		 * @property documentMode
5387		 * @type Number
5388		 */
5389		documentMode: ie ? (document.documentMode || 7) : 10
5390	};
5391});
5392
5393// Included from: js/tinymce/classes/dom/DOMUtils.js
5394
5395/**
5396 * DOMUtils.js
5397 *
5398 * Copyright, Moxiecode Systems AB
5399 * Released under LGPL License.
5400 *
5401 * License: http://www.tinymce.com/license
5402 * Contributing: http://www.tinymce.com/contributing
5403 */
5404
5405/**
5406 * Utility class for various DOM manipulation and retrieval functions.
5407 *
5408 * @class tinymce.dom.DOMUtils
5409 * @example
5410 * // Add a class to an element by id in the page
5411 * tinymce.DOM.addClass('someid', 'someclass');
5412 *
5413 * // Add a class to an element by id inside the editor
5414 * tinymce.activeEditor.dom.addClass('someid', 'someclass');
5415 */
5416define("tinymce/dom/DOMUtils", [
5417	"tinymce/dom/Sizzle",
5418	"tinymce/html/Styles",
5419	"tinymce/dom/EventUtils",
5420	"tinymce/dom/TreeWalker",
5421	"tinymce/dom/Range",
5422	"tinymce/html/Entities",
5423	"tinymce/Env",
5424	"tinymce/util/Tools"
5425], function(Sizzle, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools) {
5426	// Shorten names
5427	var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim, extend = Tools.extend;
5428	var isWebKit = Env.webkit, isIE = Env.ie;
5429	var simpleSelectorRe = /^([a-z0-9],?)+$/i;
5430	var whiteSpaceRegExp = /^[ \t\r\n]*$/;
5431	var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
5432
5433	/**
5434	 * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
5435	 *
5436	 * @constructor
5437	 * @method DOMUtils
5438	 * @param {Document} d Document reference to bind the utility class to.
5439	 * @param {settings} s Optional settings collection.
5440	 */
5441	function DOMUtils(doc, settings) {
5442		var self = this, blockElementsMap;
5443
5444		self.doc = doc;
5445		self.win = window;
5446		self.files = {};
5447		self.counter = 0;
5448		self.stdMode = !isIE || doc.documentMode >= 8;
5449		self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
5450		self.hasOuterHTML = "outerHTML" in doc.createElement("a");
5451		this.boundEvents = [];
5452
5453		self.settings = settings = extend({
5454			keep_values: false,
5455			hex_colors: 1
5456		}, settings);
5457
5458		self.schema = settings.schema;
5459		self.styles = new Styles({
5460			url_converter: settings.url_converter,
5461			url_converter_scope: settings.url_converter_scope
5462		}, settings.schema);
5463
5464		self.fixDoc(doc);
5465		self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
5466		blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
5467
5468		/**
5469		 * Returns true/false if the specified element is a block element or not.
5470		 *
5471		 * @method isBlock
5472		 * @param {Node/String} node Element/Node to check.
5473		 * @return {Boolean} True/False state if the node is a block element or not.
5474		 */
5475		self.isBlock = function(node) {
5476			// Fix for #5446
5477			if (!node) {
5478				return false;
5479			}
5480
5481			// This function is called in module pattern style since it might be executed with the wrong this scope
5482			var type = node.nodeType;
5483
5484			// If it's a node then check the type and use the nodeName
5485			if (type) {
5486				return !!(type === 1 && blockElementsMap[node.nodeName]);
5487			}
5488
5489			return !!blockElementsMap[node];
5490		};
5491	}
5492
5493	DOMUtils.prototype = {
5494		root: null,
5495		props: {
5496			"for": "htmlFor",
5497			"class": "className",
5498			className: "className",
5499			checked: "checked",
5500			disabled: "disabled",
5501			maxlength: "maxLength",
5502			readonly: "readOnly",
5503			selected: "selected",
5504			value: "value",
5505			id: "id",
5506			name: "name",
5507			type: "type"
5508		},
5509
5510		fixDoc: function(doc) {
5511			var settings = this.settings, name;
5512
5513			if (isIE && settings.schema) {
5514				// Add missing HTML 4/5 elements to IE
5515				('abbr article aside audio canvas ' +
5516				'details figcaption figure footer ' +
5517				'header hgroup mark menu meter nav ' +
5518				'output progress section summary ' +
5519				'time video').replace(/\w+/g, function(name) {
5520					doc.createElement(name);
5521				});
5522
5523				// Create all custom elements
5524				for (name in settings.schema.getCustomElements()) {
5525					doc.createElement(name);
5526				}
5527			}
5528		},
5529
5530		clone: function(node, deep) {
5531			var self = this, clone, doc;
5532
5533			// TODO: Add feature detection here in the future
5534			if (!isIE || node.nodeType !== 1 || deep) {
5535				return node.cloneNode(deep);
5536			}
5537
5538			doc = self.doc;
5539
5540			// Make a HTML5 safe shallow copy
5541			if (!deep) {
5542				clone = doc.createElement(node.nodeName);
5543
5544				// Copy attribs
5545				each(self.getAttribs(node), function(attr) {
5546					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
5547				});
5548
5549				return clone;
5550			}
5551/*
5552			// Setup HTML5 patched document fragment
5553			if (!self.frag) {
5554				self.frag = doc.createDocumentFragment();
5555				self.fixDoc(self.frag);
5556			}
5557
5558			// Make a deep copy by adding it to the document fragment then removing it this removed the :section
5559			clone = doc.createElement('div');
5560			self.frag.appendChild(clone);
5561			clone.innerHTML = node.outerHTML;
5562			self.frag.removeChild(clone);
5563*/
5564			return clone.firstChild;
5565		},
5566
5567		/**
5568		 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
5569		 * go above the point of this root node.
5570		 *
5571		 * @method getRoot
5572		 * @return {Element} Root element for the utility class.
5573		 */
5574		getRoot: function() {
5575			var self = this;
5576
5577			return self.get(self.settings.root_element) || self.doc.body;
5578		},
5579
5580		/**
5581		 * Returns the viewport of the window.
5582		 *
5583		 * @method getViewPort
5584		 * @param {Window} win Optional window to get viewport of.
5585		 * @return {Object} Viewport object with fields x, y, w and h.
5586		 */
5587		getViewPort: function(win) {
5588			var doc, rootElm;
5589
5590			win = !win ? this.win : win;
5591			doc = win.document;
5592			rootElm = this.boxModel ? doc.documentElement : doc.body;
5593
5594			// Returns viewport size excluding scrollbars
5595			return {
5596				x: win.pageXOffset || rootElm.scrollLeft,
5597				y: win.pageYOffset || rootElm.scrollTop,
5598				w: win.innerWidth || rootElm.clientWidth,
5599				h: win.innerHeight || rootElm.clientHeight
5600			};
5601		},
5602
5603		/**
5604		 * Returns the rectangle for a specific element.
5605		 *
5606		 * @method getRect
5607		 * @param {Element/String} elm Element object or element ID to get rectangle from.
5608		 * @return {object} Rectangle for specified element object with x, y, w, h fields.
5609		 */
5610		getRect: function(elm) {
5611			var self = this, pos, size;
5612
5613			elm = self.get(elm);
5614			pos = self.getPos(elm);
5615			size = self.getSize(elm);
5616
5617			return {
5618				x: pos.x, y: pos.y,
5619				w: size.w, h: size.h
5620			};
5621		},
5622
5623		/**
5624		 * Returns the size dimensions of the specified element.
5625		 *
5626		 * @method getSize
5627		 * @param {Element/String} elm Element object or element ID to get rectangle from.
5628		 * @return {object} Rectangle for specified element object with w, h fields.
5629		 */
5630		getSize: function(elm) {
5631			var self = this, w, h;
5632
5633			elm = self.get(elm);
5634			w = self.getStyle(elm, 'width');
5635			h = self.getStyle(elm, 'height');
5636
5637			// Non pixel value, then force offset/clientWidth
5638			if (w.indexOf('px') === -1) {
5639				w = 0;
5640			}
5641
5642			// Non pixel value, then force offset/clientWidth
5643			if (h.indexOf('px') === -1) {
5644				h = 0;
5645			}
5646
5647			return {
5648				w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
5649				h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
5650			};
5651		},
5652
5653		/**
5654		 * Returns a node by the specified selector function. This function will
5655		 * loop through all parent nodes and call the specified function for each node.
5656		 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
5657		 * and the node it found will be returned.
5658		 *
5659		 * @method getParent
5660		 * @param {Node/String}
5660 node DOM node to search parents on or ID string.
5661		 * @param {function} selector Selection function or CSS selector to execute on each node.
5662		 * @param {Node} root Optional root element, never go below this point.
5663		 * @return {Node} DOM Node or null if it wasn't found.
5664		 */
5665		getParent: function(node, selector, root) {
5666			return this.getParents(node, selector, root, false);
5667		},
5668
5669		/**
5670		 * Returns a node list of all parents matching the specified selector function or pattern.
5671		 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
5672		 *
5673		 * @method getParents
5674		 * @param {Node/String} node DOM node to search parents on or ID string.
5675		 * @param {function} selector Selection function to execute on each node or CSS pattern.
5676		 * @param {Node} root Optional root element, never go below this point.
5677		 * @return {Array} Array of nodes or null if it wasn't found.
5678		 */
5679		getParents: function(node, selector, root, collect) {
5680			var self = this, selectorVal, result = [];
5681
5682			node = self.get(node);
5683			collect = collect === undefined;
5684
5685			// Default root on inline mode
5686			root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
5687
5688			// Wrap node name as func
5689			if (is(selector, 'string')) {
5690				selectorVal = selector;
5691
5692				if (selector === '*') {
5693					selector = function(node) {return node.nodeType == 1;};
5694				} else {
5695					selector = function(node) {
5696						return self.is(node, selectorVal);
5697					};
5698				}
5699			}
5700
5701			while (node) {
5702				if (node == root || !node.nodeType || node.nodeType === 9) {
5703					break;
5704				}
5705
5706				if (!selector || selector(node)) {
5707					if (collect) {
5708						result.push(node);
5709					} else {
5710						return node;
5711					}
5712				}
5713
5714				node = node.parentNode;
5715			}
5716
5717			return collect ? result : null;
5718		},
5719
5720		/**
5721		 * Returns the specified element by ID or the input element if it isn't a string.
5722		 *
5723		 * @method get
5724		 * @param {String/Element} n Element id to look for or element to just pass though.
5725		 * @return {Element} Element matching the specified id or null if it wasn't found.
5726		 */
5727		get: function(elm) {
5728			var name;
5729
5730			if (elm && this.doc && typeof(elm) == 'string') {
5731				name = elm;
5732				elm = this.doc.getElementById(elm);
5733
5734				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
5735				if (elm && elm.id !== name) {
5736					return this.doc.getElementsByName(name)[1];
5737				}
5738			}
5739
5740			return elm;
5741		},
5742
5743		/**
5744		 * Returns the next node that matches selector or function
5745		 *
5746		 * @method getNext
5747		 * @param {Node} node Node to find siblings from.
5748		 * @param {String/function} selector Selector CSS expression or function.
5749		 * @return {Node} Next node item matching the selector or null if it wasn't found.
5750		 */
5751		getNext: function(node, selector) {
5752			return this._findSib(node, selector, 'nextSibling');
5753		},
5754
5755		/**
5756		 * Returns the previous node that matches selector or function
5757		 *
5758		 * @method getPrev
5759		 * @param {Node} node Node to find siblings from.
5760		 * @param {String/function} selector Selector CSS expression or function.
5761		 * @return {Node} Previous node item matching the selector or null if it wasn't found.
5762		 */
5763		getPrev: function(node, selector) {
5764			return this._findSib(node, selector, 'previousSibling');
5765		},
5766
5767		// #ifndef jquery
5768
5769		/**
5770		 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
5771		 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
5772		 * on more complex patterns.
5773		 *
5774		 * @method select
5775		 * @param {String} selector CSS level 3 pattern to select/find elements by.
5776		 * @param {Object} scope Optional root element/scope element to search in.
5777		 * @return {Array} Array with all matched elements.
5778		 * @example
5779		 * // Adds a class to all paragraphs in the currently active editor
5780		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
5781		 *
5782		 * // Adds a class to all spans that have the test class in the currently active editor
5783		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
5784		 */
5785		select: function(selector, scope) {
5786			var self = this;
5787
5788			//Sizzle.selectors.cacheLength = 0;
5789			return Sizzle(selector, self.get(scope) || self.get(self.settings.root_element) || self.doc, []);
5790		},
5791
5792		/**
5793		 * Returns true/false if the specified element matches the specified css pattern.
5794		 *
5795		 * @method is
5796		 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
5797		 * @param {String} selector CSS pattern to match the element against.
5798		 */
5799		is: function(elm, selector) {
5800			var i;
5801
5802			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
5803			if (elm.length === undefined) {
5804				// Simple all selector
5805				if (selector === '*') {
5806					return elm.nodeType == 1;
5807				}
5808
5809				// Simple selector just elements
5810				if (simpleSelectorRe.test(selector)) {
5811					selector = selector.toLowerCase().split(/,/);
5812					elm = elm.nodeName.toLowerCase();
5813
5814					for (i = selector.length - 1; i >= 0; i--) {
5815						if (selector[i] == elm) {
5816							return true;
5817						}
5818					}
5819
5820					return false;
5821				}
5822			}
5823
5824			// Is non element
5825			if (elm.nodeType && elm.nodeType != 1) {
5826				return false;
5827			}
5828
5829			return Sizzle.matches(selector, elm.nodeType ? [elm] : elm).length > 0;
5830		},
5831
5832		// #endif
5833
5834		/**
5835		 * Adds the specified element to another element or elements.
5836		 *
5837		 * @method add
5838		 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
5839		 * @param {String/Element} name Name of new element to add or existing element to add.
5840		 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
5841		 * @param {String} html Optional inner HTML contents to add for each element.
5842		 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
5843		 * were passed in.
5844		 * @example
5845		 * // Adds a new paragraph to the end of the active editor
5846		 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
5847		 */
5848		add: function(parentElm, name, attrs, html, create) {
5849			var self = this;
5850
5851			return this.run(parentElm, function(parentElm) {
5852				var newElm;
5853
5854				newElm = is(name, 'string') ? self.doc.createElement(name) : name;
5855				self.setAttribs(newElm, attrs);
5856
5857				if (html) {
5858					if (html.nodeType) {
5859						newElm.appendChild(html);
5860					} else {
5861						self.setHTML(newElm, html);
5862					}
5863				}
5864
5865				return !create ? parentElm.appendChild(newElm) : newElm;
5866			});
5867		},
5868
5869		/**
5870		 * Creates a new element.
5871		 *
5872		 * @method create
5873		 * @param {String} name Name of new element.
5874		 * @param {Object} attrs Optional object name/value collection with element attributes.
5875		 * @param {String} html Optional HTML string to set as inner HTML of the element.
5876		 * @return {Element} HTML DOM node element that got created.
5877		 * @example
5878		 * // Adds an element where the caret/selection is in the active editor
5879		 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
5880		 * tinymce.activeEditor.selection.setNode(el);
5881		 */
5882		create: function(name, attrs, html) {
5883			return this.add(this.doc.createElement(name), name, attrs, html, 1);
5884		},
5885
5886		/**
5887		 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
5888		 *
5889		 * @method createHTML
5890		 * @param {String} name Name of new element.
5891		 * @param {Object} attrs Optional object name/value collection with element attributes.
5892		 * @param {String} html Optional HTML string to set as inner HTML of the element.
5893		 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
5894		 * @example
5895		 * // Creates a html chunk and inserts it at the current selection/caret location
5896		 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
5897		 */
5898		createHTML: function(name, attrs, html) {
5899			var outHtml = '', key;
5900
5901			outHtml += '<' + name;
5902
5903			for (key in attrs) {
5904				if (attrs.hasOwnProperty(key) && attrs[key] !== null) {
5905					outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
5906				}
5907			}
5908
5909			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
5910			if (typeof(html) != "undefined") {
5911				return outHtml + '>' + html + '</' + name + '>';
5912			}
5913
5914			return outHtml + ' />';
5915		},
5916
5917		/**
5918		 * Creates a document fragment out of the specified HTML string.
5919		 *
5920		 * @method createFragment
5921		 * @param {String} html Html string to create fragment from.
5922		 * @return {DocumentFragment} Document fragment node.
5923		 */
5924		createFragment: function(html) {
5925			var frag, node, doc = this.doc, container;
5926
5927			container = doc.createElement("div");
5928			frag = doc.createDocumentFragment();
5929
5930			if (html) {
5931				container.innerHTML = html;
5932			}
5933
5934			while ((node = container.firstChild)) {
5935				frag.appendChild(node);
5936			}
5937
5938			return frag;
5939		},
5940
5941		/**
5942		 * Removes/deletes the specified element(s) from the DOM.
5943		 *
5944		 * @method remove
5945		 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
5946		 * @param {Boolean} keep_children Optional state to keep children or not. If set to true all children will be
5947		 * placed at the location of the removed element.
5948		 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
5949		 * were passed in.
5950		 * @example
5951		 * // Removes all paragraphs in the active editor
5952		 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
5953		 *
5954		 * // Removes an element by id in the document
5955		 * tinymce.DOM.remove('mydiv');
5956		 */
5957		remove: function(node, keep_children) {
5958			return this.run(node, function(node) {
5959				var child, parent = node.parentNode;
5960
5961				if (!parent) {
5962					return null;
5963				}
5964
5965				if (keep_children) {
5966					while ((child = node.firstChild)) {
5967						// IE 8 will crash if you don't remove completely empty text nodes
5968						if (!isIE || child.nodeType !== 3 || child.nodeValue) {
5969							parent.insertBefore(child, node);
5970						} else {
5971							node.removeChild(child);
5972						}
5973					}
5974				}
5975
5976				return parent.removeChild(node);
5977			});
5978		},
5979
5980		/**
5981		 * Sets the CSS style value on a HTML element. The name can be a camelcase string
5982		 * or the CSS style name like background-color.
5983		 *
5984		 * @method setStyle
5985		 * @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
5986		 * @param {String} na Name of the style value to set.
5987		 * @param {String} v Value to set on the style.
5988		 * @example
5989		 * // Sets a style value on all paragraphs in the currently active editor
5990		 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
5991		 *
5992		 * // Sets a style value to an element by id in the current document
5993		 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
5994		 */
5995		setStyle: function(elm, name, value) {
5996			return this.run(elm, function(elm) {
5997				var self = this, style, key;
5998
5999				if (name) {
6000					if (typeof(name) === 'string') {
6001						style = elm.style;
6002
6003						// Camelcase it, if needed
6004						name = name.replace(/-(\D)/g, function(a, b) {
6005							return b.toUpperCase();
6006						});
6007
6008						// Default px suffix on these
6009						if (typeof(value) === 'number' && !numericCssMap[name]) {
6010							value += 'px';
6011						}
6012
6013						// IE specific opacity
6014						if (name === "opacity" && elm.runtimeStyle && typeof(elm.runtimeStyle.opacity) === "undefined") {
6015							style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
6016						}
6017
6018						if (name == "float") {
6019							// Old IE vs modern browsers
6020							name = "cssFloat" in elm.style ? "cssFloat" : "styleFloat";
6021						}
6022
6023						try {
6024							style[name] = value;
6025						} catch (ex) {
6026							// Ignore IE errors
6027						}
6028
6029						// Force update of the style data
6030						if (self.settings.update_styles) {
6031							elm.removeAttribute('data-mce-style');
6032						}
6033					} else {
6034						for (key in name) {
6035							self.setStyle(elm, key, name[key]);
6036						}
6037					}
6038				}
6039			});
6040		},
6041
6042		/**
6043		 * Returns the current style or runtime/computed value of an element.
6044		 *
6045		 * @method getStyle
6046		 * @param {String/Element} elm HTML element or element id string to get style from.
6047		 * @param {String} name Style name to return.
6048		 * @param {Boolean} computed Computed style.
6049		 * @return {String} Current style or computed style value of an element.
6050		 */
6051		getStyle: function(elm, name, computed) {
6052			elm = this.get(elm);
6053
6054			if (!elm) {
6055				return;
6056			}
6057
6058			// W3C
6059			if (this.doc.defaultView && computed) {
6060				// Remove camelcase
6061				name = name.replace(/[A-Z]/g, function(a){
6062					return '-' + a;
6063				});
6064
6065				try {
6066					return this.doc.defaultView.getComputedStyle(elm, null).getPropertyValue(name);
6067				} catch (ex) {
6068					// Old safari might fail
6069					return null;
6070				}
6071			}
6072
6073			// Camelcase it, if needed
6074			name = name.replace(/-(\D)/g, function(a, b) {
6075				return b.toUpperCase();
6076			});
6077
6078			if (name == 'float') {
6079				name = isIE ? 'styleFloat' : 'cssFloat';
6080			}
6081
6082			// IE & Opera
6083			if (elm.currentStyle && computed) {
6084				return elm.currentStyle[name];
6085			}
6086
6087			return elm.style ? elm.style[name] : undefined;
6088		},
6089
6090		/**
6091		 * Sets multiple styles on the specified element(s).
6092		 *
6093		 * @method setStyles
6094		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set styles on.
6095		 * @param {Object} o Name/Value collection of style items to add to the element(s).
6096		 * @example
6097		 * // Sets styles on all paragraphs in the currently active editor
6098		 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
6099		 *
6100		 * // Sets styles to an element by id in the current document
6101		 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
6102		 */
6103		setStyles: function(elm, styles) {
6104			this.setStyle(elm, styles);
6105		},
6106
6107		css: function(elm, name, value) {
6108			this.setStyle(elm, name, value);
6109		},
6110
6111		/**
6112		 * Removes all attributes from an element or elements.
6113		 *
6114		 * @method removeAllAttribs
6115		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
6116		 */
6117		removeAllAttribs: function(e) {
6118			return this.run(e, function(e) {
6119				var i, attrs = e.attributes;
6120				for (i = attrs.length - 1; i >= 0; i--) {
6121					e.removeAttributeNode(attrs.item(i));
6122				}
6123			});
6124		},
6125
6126		/**
6127		 * Sets the specified attribute of an element or elements.
6128		 *
6129		 * @method setAttrib
6130		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set attribute on.
6131		 * @param {String} n Name of attribute to set.
6132		 * @param {String} v Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove the attribute instead.
6133		 * @example
6134		 * // Sets class attribute on all paragraphs in the active editor
6135		 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
6136		 *
6137		 * // Sets class attribute on a specific element in the current page
6138		 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
6139		 */
6140		setAttrib: function(e, n, v) {
6141			var t = this;
6142
6143			// What's the point
6144			if (!e || !n) {
6145				return;
6146			}
6147
6148			return this.run(e, function(e) {
6149				var s = t.settings;
6150				var originalValue = e.getAttribute(n);
6151				if (v !== null) {
6152					switch (n) {
6153						case "style":
6154							if (!is(v, 'string')) {
6155								each(v, function(v, n) {
6156									t.setStyle(e, n, v);
6157								});
6158
6159								return;
6160							}
6161
6162							// No mce_style for elements with these since they might get resized by the user
6163							if (s.keep_values) {
6164								if (v) {
6165									e.setAttribute('data-mce-style', v, 2);
6166								} else {
6167									e.removeAttribute('data-mce-style', 2);
6168								}
6169							}
6170
6171							e.style.cssText = v;
6172							break;
6173
6174						case "class":
6175							e.className = v || ''; // Fix IE null bug
6176							break;
6177
6178						case "src":
6179						case "href":
6180							if (s.keep_values) {
6181								if (s.url_converter) {
6182									v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
6183								}
6184
6185								t.setAttrib(e, 'data-mce-' + n, v, 2);
6186							}
6187
6188							break;
6189
6190						case "shape":
6191							e.setAttribute('data-mce-style', v);
6192							break;
6193					}
6194				}
6195				if (is(v) && v !== null && v.length !== 0) {
6196					e.setAttribute(n, '' + v, 2);
6197				} else {
6198					e.removeAttribute(n, 2);
6199				}
6200
6201				// fire onChangeAttrib event for attributes that have changed
6202				if (originalValue != v && s.onSetAttrib) {
6203					s.onSetAttrib({attrElm: e, attrName: n, attrValue: v});
6204				}
6205			});
6206		},
6207
6208		/**
6209		 * Sets two or more specified attributes of an element or elements.
6210		 *
6211		 * @method setAttribs
6212		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
6213		 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
6214		 * @example
6215		 * // Sets class and title attributes on all paragraphs in the active editor
6216		 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
6217		 *
6218		 * // Sets class and title attributes on a specific element in the current page
6219		 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
6220		 */
6221		setAttribs: function(elm, attrs) {
6222			var self = this;
6223
6224			return this.run(elm, function(elm) {
6225				each(attrs, function(value, name) {
6226					self.setAttrib(elm, name, value);
6227				});
6228			});
6229		},
6230
6231		/**
6232		 * Returns the specified attribute by name.
6233		 *
6234		 * @method getAttrib
6235		 * @param {String/Element} elm Element string id or DOM element to get attribute from.
6236		 * @param {String} name Name of attribute to get.
6237		 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
6238		 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
6239		 */
6240		getAttrib: function(elm, name, defaultVal) {
6241			var value, self = this, undef;
6242
6243			elm = self.get(elm);
6244
6245			if (!elm || elm.nodeType !== 1) {
6246				return defaultVal === undef ? false : defaultVal;
6247			}
6248
6249			if (!is(defaultVal)) {
6250				defaultVal = '';
6251			}
6252
6253			// Try the mce variant for these
6254			if (/^(src|href|style|coords|shape)$/.test(name)) {
6255				value = elm.getAttribute("data-mce-" + name);
6256
6257				if (value) {
6258					return value;
6259				}
6260			}
6261
6262			if (isIE && self.props[name]) {
6263				value = elm[self.props[name]];
6264				value = value && value.nodeValue ? value.nodeValue : value;
6265			}
6266
6267			if (!value) {
6268				value = elm.getAttribute(name, 2);
6269			}
6270
6271			// Check boolean attribs
6272			if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(name)) {
6273				if (elm[self.props[name]] === true && value === '') {
6274					return name;
6275				}
6276
6277				return value ? name : '';
6278			}
6279
6280			// Inner input elements will override attributes on form elements
6281			if (elm.nodeName === "FORM" && elm.getAttributeNode(name)) {
6282				return elm.getAttributeNode(name).nodeValue;
6283			}
6284
6285			if (name === 'style') {
6286				value = value || elm.style.cssText;
6287
6288				if (value) {
6289					value = self.serializeStyle(self.parseStyle(value), elm.nodeName);
6290
6291					if (self.settings.keep_values) {
6292						elm.setAttribute('data-mce-style', value);
6293					}
6294				}
6295			}
6296
6297			// Remove Apple and WebKit stuff
6298			if (isWebKit && name === "class" && value) {
6299				value = value.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
6300			}
6301
6302			// Handle IE issues
6303			if (isIE) {
6304				switch (name) {
6305					case 'rowspan':
6306					case 'colspan':
6307						// IE returns 1 as default value
6308						if (value === 1) {
6309							value = '';
6310						}
6311
6312						break;
6313
6314					case 'size':
6315						// IE returns +0 as default value for size
6316						if (value === '+0' || value === 20 || value === 0) {
6317							value = '';
6318						}
6319
6320						break;
6321
6322					case 'width':
6323					case 'height':
6324					case 'vspace':
6325					case 'checked':
6326					case 'disabled':
6327					case 'readonly':
6328						if (value === 0) {
6329							value = '';
6330						}
6331
6332						break;
6333
6334					case 'hspace':
6335						// IE returns -1 as default value
6336						if (value === -1) {
6337							value = '';
6338						}
6339
6340						break;
6341
6342					case 'maxlength':
6343					case 'tabindex':
6344						// IE returns default value
6345						if (value === 32768 || value === 2147483647 || value === '32768') {
6346							value = '';
6347						}
6348
6349						break;
6350
6351					case 'multiple':
6352					case 'compact':
6353					case 'noshade':
6354					case 'nowrap':
6355						if (value === 65535) {
6356							return name;
6357						}
6358
6359						return defaultVal;
6360
6361					case 'shape':
6362						value = value.toLowerCase();
6363						break;
6364
6365					default:
6366						// IE has odd anonymous function for event attributes
6367						if (name.indexOf('on') === 0 && value) {
6368							value = ('' + value).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');
6369						}
6370				}
6371			}
6372
6373			return (value !== undef && value !== null && value !== '') ? '' + value : defaultVal;
6374		},
6375
6376		/**
6377		 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
6378		 *
6379		 * @method getPos
6380		 * @param {Element/String} elm HTML element or element id to get x, y position from.
6381		 * @param {Element} rootElm Optional root element to stop calculations at.
6382		 * @return {object} Absolute position of the specified element object with x, y fields.
6383		 */
6384		getPos: function(elm, rootElm) {
6385			var self = this, x = 0, y = 0, offsetParent, doc = self.doc, pos;
6386
6387			elm = self.get(elm);
6388			rootElm = rootElm || doc.body;
6389
6390			if (elm) {
6391				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
6392				if (rootElm === doc.body && elm.getBoundingClientRect) {
6393					pos = elm.getBoundingClientRect();
6394					rootElm = self.boxModel ? doc.documentElement : doc.body;
6395
6396					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
6397					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
6398					x = pos.left + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - rootElm.clientTop;
6399					y = pos.top + (doc.documentElement.scrollTop || doc.body.scrollTop) - rootElm.clientLeft;
6400
6401					return {x: x, y: y};
6402				}
6403
6404				offsetParent = elm;
6405				while (offsetParent && offsetParent != rootElm && offsetParent.
6405nodeType) {
6406					x += offsetParent.offsetLeft || 0;
6407					y += offsetParent.offsetTop || 0;
6408					offsetParent = offsetParent.offsetParent;
6409				}
6410
6411				offsetParent = elm.parentNode;
6412				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
6413					x -= offsetParent.scrollLeft || 0;
6414					y -= offsetParent.scrollTop || 0;
6415					offsetParent = offsetParent.parentNode;
6416				}
6417			}
6418
6419			return {x: x, y: y};
6420		},
6421
6422		/**
6423		 * Parses the specified style value into an object collection. This parser will also
6424		 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
6425		 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
6426		 *
6427		 * @method parseStyle
6428		 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
6429		 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
6430		 */
6431		parseStyle: function(cssText) {
6432			return this.styles.parse(cssText);
6433		},
6434
6435		/**
6436		 * Serializes the specified style object into a string.
6437		 *
6438		 * @method serializeStyle
6439		 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
6440		 * @param {String} name Optional element name.
6441		 * @return {String} String representation of the style object, for example: border: 1px solid red.
6442		 */
6443		serializeStyle: function(styles, name) {
6444			return this.styles.serialize(styles, name);
6445		},
6446
6447		/**
6448		 * Adds a style element at the top of the document with the specified cssText content.
6449		 *
6450		 * @method addStyle
6451		 * @param {String} cssText CSS Text style to add to top of head of document.
6452		 */
6453		addStyle: function(cssText) {
6454			var self = this, doc = self.doc, head, styleElm;
6455
6456			// Prevent inline from loading the same styles twice
6457			if (self !== DOMUtils.DOM && doc === document) {
6458				var addedStyles = DOMUtils.DOM.addedStyles;
6459
6460				addedStyles = addedStyles || [];
6461				if (addedStyles[cssText]) {
6462					return;
6463				}
6464
6465				addedStyles[cssText] = true;
6466				DOMUtils.DOM.addedStyles = addedStyles;
6467			}
6468
6469			// Create style element if needed
6470			styleElm = doc.getElementById('mceDefaultStyles');
6471			if (!styleElm) {
6472				styleElm = doc.createElement('style');
6473				styleElm.id = 'mceDefaultStyles';
6474				styleElm.type = 'text/css';
6475
6476				head = doc.getElementsByTagName('head')[0];
6477				if (head.firstChild) {
6478					head.insertBefore(styleElm, head.firstChild);
6479				} else {
6480					head.appendChild(styleElm);
6481				}
6482			}
6483
6484			// Append style data to old or new style element
6485			if (styleElm.styleSheet) {
6486				styleElm.styleSheet.cssText += cssText;
6487			} else {
6488				styleElm.appendChild(doc.createTextNode(cssText));
6489			}
6490		},
6491
6492		/**
6493		 * Imports/loads the specified CSS file into the document bound to the class.
6494		 *
6495		 * @method loadCSS
6496		 * @param {String} u URL to CSS file to load.
6497		 * @example
6498		 * // Loads a CSS file dynamically into the current document
6499		 * tinymce.DOM.loadCSS('somepath/some.css');
6500		 *
6501		 * // Loads a CSS file into the currently active editor instance
6502		 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
6503		 *
6504		 * // Loads a CSS file into an editor instance by id
6505		 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
6506		 *
6507		 * // Loads multiple CSS files into the current document
6508		 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
6509		 */
6510		loadCSS: function(url) {
6511			var self = this, doc = self.doc, head;
6512
6513			// Prevent inline from loading the same CSS file twice
6514			if (self !== DOMUtils.DOM && doc === document) {
6515				DOMUtils.DOM.loadCSS(url);
6516				return;
6517			}
6518
6519			if (!url) {
6520				url = '';
6521			}
6522
6523			head = doc.getElementsByTagName('head')[0];
6524
6525			each(url.split(','), function(url) {
6526				var link;
6527
6528				if (self.files[url]) {
6529					return;
6530				}
6531
6532				self.files[url] = true;
6533				link = self.create('link', {rel: 'stylesheet', href: url});
6534
6535				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
6536				// This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
6537				// It's ugly but it seems to work fine.
6538				if (isIE && doc.documentMode && doc.recalc) {
6539					link.onload = function() {
6540						if (doc.recalc) {
6541							doc.recalc();
6542						}
6543
6544						link.onload = null;
6545					};
6546				}
6547
6548				head.appendChild(link);
6549			});
6550		},
6551
6552		/**
6553		 * Adds a class to the specified element or elements.
6554		 *
6555		 * @method addClass
6556		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
6557		 * @param {String} cls Class name to add to each element.
6558		 * @return {String/Array}
6558 String with new class value or array with new class values for all elements.
6559		 * @example
6560		 * // Adds a class to all paragraphs in the active editor
6561		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
6562		 *
6563		 * // Adds a class to a specific element in the current page
6564		 * tinymce.DOM.addClass('mydiv', 'myclass');
6565		 */
6566		addClass: function(elm, cls) {
6567			return this.run(elm, function(elm) {
6568				var clsVal;
6569
6570				if (!cls) {
6571					return 0;
6572				}
6573
6574				if (this.hasClass(elm, cls)) {
6575					return elm.className;
6576				}
6577
6578				clsVal = this.removeClass(elm, cls);
6579				elm.className = clsVal = (clsVal !== '' ? (clsVal + ' ') : '') + cls;
6580
6581				return clsVal;
6582			});
6583		},
6584
6585		/**
6586		 * Removes a class from the specified element or elements.
6587		 *
6588		 * @method removeClass
6589		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
6590		 * @param {String} cls Class name to remove from each element.
6591		 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
6592		 * were passed in.
6593		 * @example
6594		 * // Removes a class from all paragraphs in the active editor
6595		 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
6596		 *
6597		 * // Removes a class from a specific element in the current page
6598		 * tinymce.DOM.removeClass('mydiv', 'myclass');
6599		 */
6600		removeClass: function(elm, cls) {
6601			var self = this, re;
6602
6603			return self.run(elm, function(elm) {
6604				var val;
6605
6606				if (self.hasClass(elm, cls)) {
6607					if (!re) {
6608						re = new RegExp("(^|\\s+)" + cls + "(\\s+|$)", "g");
6609					}
6610
6611					val = elm.className.replace(re, ' ');
6612					val = trim(val != ' ' ? val : '');
6613
6614					elm.className = val;
6615
6616					// Empty class attr
6617					if (!val) {
6618						elm.removeAttribute('class');
6619						elm.removeAttribute('className');
6620					}
6621
6622					return val;
6623				}
6624
6625				return elm.className;
6626			});
6627		},
6628
6629		/**
6630		 * Returns true if the specified element has the specified class.
6631		 *
6632		 * @method hasClass
6633		 * @param {String/Element} n HTML element or element id string to check CSS class on.
6634		 * @param {String} c CSS class to check for.
6635		 * @return {Boolean} true/false if the specified element has the specified class.
6636		 */
6637		hasClass: function(elm, cls) {
6638			elm = this.get(elm);
6639
6640			if (!elm || !cls) {
6641				return false;
6642			}
6643
6644			return (' ' + elm.className + ' ').indexOf(' ' + cls + ' ') !== -1;
6645		},
6646
6647		/**
6648		 * Toggles the specified class on/off.
6649		 *
6650		 * @method toggleClass
6651		 * @param {Element} elm Element to toggle class on.
6652		 * @param {[type]} cls Class to toggle on/off.
6653		 * @param {[type]} state Optional state to set.
6654		 */
6655		toggleClass: function(elm, cls, state) {
6656			state = state === undefined ? !this.hasClass(elm, cls) : state;
6657
6658			if (this.hasClass(elm, cls) !== state) {
6659				if (state) {
6660					this.addClass(elm, cls);
6661				} else {
6662					this.removeClass(elm, cls);
6663				}
6664			}
6665		},
6666
6667		/**
6668		 * Shows the specified element(s) by ID by setting the "display" style.
6669		 *
6670		 * @method show
6671		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
6672		 */
6673		show: function(elm) {
6674			return this.setStyle(elm, 'display', 'block');
6675		},
6676
6677		/**
6678		 * Hides the specified element(s) by ID by setting the "display" style.
6679		 *
6680		 * @method hide
6681		 * @param {String/Element/Array} e ID of DOM element or DOM element or array with elements or IDs to hide.
6682		 * @example
6683		 * // Hides an element by id in the document
6684		 * tinymce.DOM.hide('myid');
6685		 */
6686		hide: function(elm) {
6687			return this.setStyle(elm, 'display', 'none');
6688		},
6689
6690		/**
6691		 * Returns true/false if the element is hidden or not by checking the "display" style.
6692		 *
6693		 * @method isHidden
6694		 * @param {String/Element} e Id or element to check display state on.
6695		 * @return {Boolean} true/false if the element is hidden or not.
6696		 */
6697		isHidden: function(elm) {
6698			elm = this.get(elm);
6699
6700			return !elm || elm.style.display == 'none' || this.getStyle(elm, 'display') == 'none';
6701		},
6702
6703		/**
6704		 * Returns a unique id. This can be useful when generating elements on the fly.
6705		 * This method will not check if the element already exists.
6706		 *
6707		 * @method uniqueId
6708		 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
6709		 * @return {String} Unique id.
6710		 */
6711		uniqueId: function(prefix) {
6712			return (!prefix ? 'mce_' : prefix) + (this.counter++);
6713		},
6714
6715		/**
6716		 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
6717		 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
6718		 *
6719		 * @method setHTML
6720		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set HTML inside of.
6721		 * @param {String} h HTML content to set as inner HTML of the element.
6722		 * @example
6723		 * // Sets the inner HTML of all paragraphs in the active editor
6724		 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
6725		 *
6726		 * // Sets the inner HTML of an element by id in the document
6727		 * tinymce.DOM.setHTML('mydiv', 'some inner html');
6728		 */
6729		setHTML: function(element, html) {
6730			var self = this;
6731
6732			return self.run(element, function(element) {
6733				if (isIE) {
6734					// Remove all child nodes, IE keeps empty text nodes in DOM
6735					while (element.firstChild) {
6736						element.removeChild(element.firstChild);
6737					}
6738
6739					try {
6740						// IE will remove comments from the beginning
6741						// unless you padd the contents with something
6742						element.innerHTML = '<br />' + html;
6743						element.removeChild(element.firstChild);
6744					} catch (ex) {
6745						// IE sometimes produces an unknown runtime error on innerHTML if it's a block element
6746						// within a block element for example a div inside a p
6747						// This seems to fix this problem
6748
6749						// Create new div with HTML contents and a BR in front to keep comments
6750						var newElement = self.create('div');
6751						newElement.innerHTML = '<br />' + html;
6752
6753						// Add all children from div to target
6754						each (grep(newElement.childNodes), function(node, i) {
6755							// Skip br element
6756							if (i && element.canHaveHTML) {
6757								element.appendChild(node);
6758							}
6759						});
6760					}
6761				} else {
6762					element.innerHTML = html;
6763				}
6764
6765				return html;
6766			});
6767		},
6768
6769		/**
6770		 * Returns the outer HTML of an element.
6771		 *
6772		 * @method getOuterHTML
6773		 * @param {String/Element} elm Element ID or element object to get outer HTML from.
6774		 * @return {String} Outer HTML string.
6775		 * @example
6776		 * tinymce.DOM.getOuterHTML(editorElement);
6777		 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
6778		 */
6779		getOuterHTML: function(elm) {
6780			var doc, self = this;
6781
6782			elm = self.get(elm);
6783
6784			if (!elm) {
6785				return null;
6786			}
6787
6788			if (elm.nodeType === 1 && self.hasOuterHTML) {
6789				return elm.outerHTML;
6790			}
6791
6792			doc = (elm.ownerDocument || self.doc).createElement("body");
6793			doc.appendChild(elm.cloneNode(true));
6794
6795			return doc.innerHTML;
6796		},
6797
6798		/**
6799		 * Sets the specified outer HTML on an element or elements.
6800		 *
6801		 * @method setOuterHTML
6802		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
6803		 * @param {Object} html HTML code to set as outer value for the element.
6804		 * @param {Document} doc Optional document scope to use in this process - defaults to the document of the DOM class.
6805		 * @example
6806		 * // Sets the outer HTML of all paragraphs in the active editor
6807		 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
6808		 *
6809		 * // Sets the outer HTML of an element by id in the document
6810		 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
6811		 */
6812		setOuterHTML: function(elm, html, doc) {
6813			var self = this;
6814
6815			return self.run(elm, function(elm) {
6816				function set() {
6817					var node, tempElm;
6818
6819					tempElm = doc.createElement("body");
6820					tempElm.innerHTML = html;
6821
6822					node = tempElm.lastChild;
6823					while (node) {
6824						self.insertAfter(node.cloneNode(true), elm);
6825						node = node.previousSibling;
6826					}
6827
6828					self.remove(elm);
6829				}
6830
6831				// Only set HTML on elements
6832				if (elm.nodeType == 1) {
6833					doc = doc || elm.ownerDocument || self.doc;
6834
6835					if (isIE) {
6836						try {
6837							// Try outerHTML for IE it sometimes produces an unknown runtime error
6838							if (elm.nodeType == 1 && self.hasOuterHTML) {
6839								elm.outerHTML = html;
6840							} else {
6841								set();
6842							}
6843						} catch (ex) {
6844							// Fix for unknown runtime error
6845							set();
6846						}
6847					} else {
6848						set();
6849					}
6850				}
6851			});
6852		},
6853
6854		/**
6855		 * Entity decodes a string. This method decodes any HTML entities, such as &aring;.
6856		 *
6857		 * @method decode
6858		 * @param {String} s String to decode entities on.
6859		 * @return {String} Entity decoded string.
6860		 */
6861		decode: Entities.decode,
6862
6863		/**
6864		 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
6865		 *
6866		 * @method encode
6867		 * @param {String} text String to encode with entities.
6868		 * @return {String} Entity encoded string.
6869		 */
6870		encode: Entities.encodeAllRaw,
6871
6872		/**
6873		 * Inserts an element after the reference element.
6874		 *
6875		 * @method insertAfter
6876		 * @param {Element} node Element to insert after the reference.
6877		 * @param {Element/String/Array} reference_node Reference element, element id or array of elements to insert after.
6878		 * @return {Element/Array} Element that got added or an array with elements.
6879		 */
6880		insertAfter: function(node, reference_node) {
6881			reference_node = this.get(reference_node);
6882
6883			return this.run(node, function(node) {
6884				var parent, nextSibling;
6885
6886				parent = reference_node.parentNode;
6887				nextSibling = reference_node.nextSibling;
6888
6889				if (nextSibling) {
6890					parent.insertBefore(node, nextSibling);
6891				} else {
6892					parent.appendChild(node);
6893				}
6894
6895				return node;
6896			});
6897		},
6898
6899		/**
6900		 * Replaces the specified element or elements with the new element specified. The new element will
6901		 * be cloned if multiple input elements are passed in.
6902		 *
6903		 * @method replace
6904		 * @param {Element} newElm New element to replace old ones with.
6905		 * @param {Element/String/Array} oldELm Element DOM node, element id or array of elements or ids to replace.
6906		 * @param {Boolean} k Optional keep children state, if set to true child nodes from the old object will be added to new ones.
6907		 */
6908		replace: function(newElm, oldElm, keepChildren) {
6909			var self = this;
6910
6911			return self.run(oldElm, function(oldElm) {
6912				if (is(oldElm, 'array')) {
6913					newElm = newElm.cloneNode(true);
6914				}
6915
6916				if (keepChildren) {
6917					each(grep(oldElm.childNodes), function(node) {
6918						newElm.appendChild(node);
6919					});
6920				}
6921
6922				return oldElm.parentNode.replaceChild(newElm, oldElm);
6923			});
6924		},
6925
6926		/**
6927		 * Renames the specified element and keeps its attributes and children.
6928		 *
6929		 * @method rename
6930		 * @param {Element} elm Element to rename.
6931		 * @param {String} name Name of the new element.
6932		 * @return {Element} New element or the old element if it needed renaming.
6933		 */
6934		rename: function(elm, name) {
6935			var self = this, newElm;
6936
6937			if (elm.nodeName != name.toUpperCase()) {
6938				// Rename block element
6939				newElm = self.create(name);
6940
6941				// Copy attribs to new block
6942				each(self.getAttribs(elm), function(attr_node) {
6943					self.setAttrib(newElm, attr_node.nodeName, self.getAttrib(elm, attr_node.nodeName));
6944				});
6945
6946				// Replace block
6947				self.replace(newElm, elm, 1);
6948			}
6949
6950			return newElm || elm;
6951		},
6952
6953		/**
6954		 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
6955		 *
6956		 * @method findCommonAncestor
6957		 * @param {Element} a Element to find common ancestor of.
6958		 * @param {Element} b Element to find common ancestor of.
6959		 * @return {Element} Common ancestor element of the two input elements.
6960		 */
6961		findCommonAncestor: function(a, b) {
6962			var ps = a, pe;
6963
6964			while (ps) {
6965				pe = b;
6966
6967				while (pe && ps != pe) {
6968					pe = pe.parentNode;
6969				}
6970
6971				if (ps == pe) {
6972					break;
6973				}
6974
6975				ps = ps.parentNode;
6976			}
6977
6978			if (!ps && a.ownerDocument) {
6979				return a.ownerDocument.documentElement;
6980			}
6981
6982			return ps;
6983		},
6984
6985		/**
6986		 * Parses the specified RGB color value and returns a hex version of that color.
6987		 *
6988		 * @method toHex
6989		 * @param {String} rgbVal RGB string value like rgb(1,2,3)
6990		 * @return {String} Hex version of that RGB value like #FF00FF.
6991		 */
6992		toHex: function(rgbVal) {
6993			return this.styles.toHex(Tools.trim(rgbVal));
6994		},
6995
6996		/**
6997		 * Executes the specified function on the element by id or dom element node or array of elements/id.
6998		 *
6999		 * @method run
7000		 * @param {String/Element/Array} Element ID or DOM element object or array with ids or elements.
7001		 * @param {function} f Function to execute for each item.
7002		 * @param {Object} s Optional scope to execute the function in.
7003		 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
7004		 */
7005		run: function(elm, func, scope) {
7006			var self = this, result;
7007
7008			if (typeof(elm) === 'string') {
7009				elm = self.get(elm);
7010			}
7011
7012			if (!elm) {
7013				return false;
7014			}
7015
7016			scope = scope || this;
7017			if (!elm.nodeType && (elm.length || elm.length === 0)) {
7018				result = [];
7019
7020				each(elm, function(elm, i) {
7021					if (elm) {
7022						if (typeof(elm) == 'string') {
7023							elm = self.get(elm);
7024						}
7025
7026						result.push(func.call(scope, elm, i));
7027					}
7028				});
7029
7030				return result;
7031			}
7032
7033			return func.call(scope, elm);
7034		},
7035
7036		/**
7037		 * Returns a NodeList with attributes for the element.
7038		 *
7039		 * @method getAttribs
7040		 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
7041		 * @return {NodeList} NodeList with attributes.
7042		 */
7043		getAttribs: function(elm) {
7044			var attrs;
7045
7046			elm = this.get(elm);
7047
7048			if (!elm) {
7049				return [];
7050			}
7051
7052			if (isIE) {
7053				attrs = [];
7054
7055				// Object will throw exception in IE
7056				if (elm.nodeName == 'OBJECT') {
7057					return elm.attributes;
7058				}
7059
7060				// IE doesn't keep the selected attribute if you clone option elements
7061				if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
7062					attrs.push({specified: 1, nodeName: 'selected'});
7063				}
7064
7065				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
7066				var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
7067				elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
7068					attrs.push({specified: 1, nodeName: a});
7069				});
7070
7071				return attrs;
7072			}
7073
7074			return elm.attributes;
7075		},
7076
7077		/**
7078		 * Returns true/false if the specified node is to be considered empty or not.
7079		 *
7080		 * @example
7081		 * tinymce.DOM.isEmpty(node, {img: true});
7082		 * @method isEmpty
7083		 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
7084		 * @return {Boolean} true/false if the node is empty or not.
7085		 */
7086		isEmpty: function(node, elements) {
7087			var self = this, i, attributes, type, walker, name, brCount = 0;
7088
7089			node = node.firstChild;
7090			if (node) {
7091				walker = new TreeWalker(node, node.parentNode);
7092				elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
7093
7094				do {
7095					type = node.nodeType;
7096
7097					if (type === 1) {
7098						// Ignore bogus elements
7099						if (node.getAttribute('data-mce-bogus')) {
7100							continue;
7101						}
7102
7103						// Keep empty elements like <img />
7104						name = node.nodeName.toLowerCase();
7105						if (elements && elements[name]) {
7106							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
7107							if (name === 'br') {
7108								brCount++;
7109								continue;
7110							}
7111
7112							return false;
7113						}
7114
7115						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
7116						attributes = self.getAttribs(node);
7117						i = node.attributes.length;
7118						while (i--) {
7119							name = node.attributes[i].nodeName;
7120							if (name === "name" || name === 'data-mce-bookmark') {
7121								return false;
7122							}
7123						}
7124					}
7125
7126					// Keep comment nodes
7127					if (type == 8) {
7128						return false;
7129					}
7130
7131					// Keep non whitespace text nodes
7132					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
7133						return false;
7134					}
7135				} while ((node = walker.next()));
7136			}
7137
7138			return brCount <= 1;
7139		},
7140
7141		/**
7142		 * Creates a new DOM Range object. This will use the native DOM Range API if it's
7143		 * available. If it's not, it will fall back to the custom TinyMCE implementation.
7144		 *
7145		 * @method createRng
7146		 * @return {DOMRange} DOM Range object.
7147		 * @example
7148		 * var rng = tinymce.DOM.createRng();
7149		 * alert(rng.startContainer + "," + rng.startOffset);
7150		 */
7151		createRng: function() {
7152			var doc = this.doc;
7153
7154			return doc.createRange ? doc.createRange() : new Range(this);
7155		},
7156
7157		/**
7158		 * Returns the index of the specified node within its parent.
7159		 *
7160		 * @method nodeIndex
7161		 * @param {Node} node Node to look for.
7162		 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
7163		 * @return {Number} Index of the specified node.
7164		 */
7165		nodeIndex: function(node, normalized) {
7166			var idx = 0, lastNodeType, lastNode, nodeType;
7167
7168			if (node) {
7169				for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
7170					nodeType = node.nodeType;
7171
7172					// Normalize text nodes
7173					if (normalized && nodeType == 3) {
7174						if (nodeType == lastNodeType || !node.nodeValue.length) {
7175							continue;
7176						}
7177					}
7178					idx++;
7179					lastNodeType = nodeType;
7180				}
7181			}
7182
7183			return idx;
7184		},
7185
7186		/**
7187		 * Splits an element into two new elements and places the specified split
7188		 * element or elements between the new ones. For example splitting the paragraph at the bold element in
7189		 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
7190		 *
7191		 * @method split
7192		 * @param {Element} parentElm Parent element to split.
7193		 * @param {Element} splitElm Element to split at.
7194		 * @param {Element} replacementElm Optional replacement element to replace the split element with.
7195		 * @return {Element} Returns the split element or the replacement element if that is specified.
7196		 */
7197		split: function(parentElm, splitElm, replacementElm) {
7198			var self = this, r = self.createRng(), bef, aft, pa;
7199
7200			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
7201			// but we don't want that in our code since it serves no purpose for the end user
7202			// For example splitting this html at the bold element:
7203			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
7204			// would produce:
7205			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
7206			// this function will then trim off empty edges and produce:
7207			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
7208			function trimNode(node) {
7209				var i, children = node.childNodes, type = node.nodeType;
7210
7211				function surroundedBySpans(node) {
7212					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
7213					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
7214					return previousIsSpan && nextIsSpan;
7215				}
7216
7217				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
7218					return;
7219				}
7220
7221				for (i = children.length - 1; i >= 0; i--) {
7222					trimNode(children[i]);
7223				}
7224
7225				if (type != 9) {
7226					// Keep non whitespace text nodes
7227					if (type == 3 && node.nodeValue.length > 0) {
7228						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
7229						// Also keep text nodes with only spaces if surrounded by spans.
7230						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
7231						var trimmedLength = trim(node.nodeValue).length;
7232						if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
7233							return;
7234						}
7235					} else if (type == 1) {
7236						// If the only child is a bookmark then move it up
7237						children = node.childNodes;
7238
7239						// TODO fix this complex if
7240						if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
7241							children[0].getAttribute('data-mce-type') == 'bookmark') {
7242							node.parentNode.insertBefore(children[0], node);
7243						}
7244
7245						// Keep non empty elements or img, hr etc
7246						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
7247							return;
7248						}
7249					}
7250
7251					self.remove(node);
7252				}
7253
7254				return node;
7255			}
7256
7257			if (parentElm && splitElm) {
7258				// Get before chunk
7259				r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
7260				r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
7261				bef = r.extractContents();
7262
7263				// Get after chunk
7264				r = self.createRng();
7265				r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
7266				r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
7267				aft = r.extractContents();
7268
7269				// Insert before chunk
7270				pa = parentElm.parentNode;
7271				pa.insertBefore(trimNode(bef), parentElm);
7272
7273				// Insert middle chunk
7274				if (replacementElm) {
7275					pa.replaceChild(replacementElm, splitElm);
7276				} else {
7277					pa.insertBefore(splitElm, parentElm);
7278				}
7279
7280				// Insert after chunk
7281				pa.insertBefore(trimNode(aft), parentElm);
7282				self.remove(parentElm);
7283
7284				return replacementElm || splitElm;
7285			}
7286		},
7287
7288		/**
7289		 * Adds an event handler to the specified object.
7290		 *
7291		 * @method bind
7292		 * @param {Element/Document/Window/Array} target Target element to bind events to.
7293		 * handler to or an array of elements/ids/documents.
7294		 * @param {String} name Name of event handler to add, for example: click.
7295		 * @param {function} func Function to execute when the event occurs.
7296		 * @param {Object} scope Optional scope to execute the function in.
7297		 * @return {function} Function callback handler the same as the one passed in.
7298		 */
7299		bind: function(target, name, func, scope) {
7300			var self = this;
7301
7302			if (Tools.isArray(target)) {
7303				var i = target.length;
7304
7305				while (i--) {
7306					target[i] = self.bind(target[i], name, func, scope);
7307				}
7308
7309				return target;
7310			}
7311
7312			// Collect all window/document events bound by editor instance
7313			if (self.settings.collect && (target === self.doc || target === self.win)) {
7314				self.boundEvents.push([target, name, func, scope]);
7315			}
7316
7317			return self.events.bind(target, name, func, scope || self);
7318		},
7319
7320		/**
7321		 * Removes the specified event handler by name and function from an element or collection of elements.
7322		 *
7323		 * @method unbind
7324		 * @param {Element/Document/Window/Array} target Target element to unbind events on.
7325		 * @param {String} name Event handler name, for example: "click"
7326		 * @param {function}
7326 func Function to remove.
7327		 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
7328		 * were passed in.
7329		 */
7330		unbind: function(target, name, func) {
7331			var self = this, i;
7332
7333			if (Tools.isArray(target)) {
7334				i = target.length;
7335
7336				while (i--) {
7337					target[i] = self.unbind(target[i], name, func);
7338				}
7339
7340				return target;
7341			}
7342
7343			// Remove any bound events matching the input
7344			if (self.boundEvents && (target === self.doc || target === self.win)) {
7345				i = self.boundEvents.length;
7346
7347				while (i--) {
7348					var item = self.boundEvents[i];
7349
7350					if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
7351						this.events.unbind(item[0], item[1], item[2]);
7352					}
7353				}
7354			}
7355
7356			return this.events.unbind(target, name, func);
7357		},
7358
7359		/**
7360		 * Fires the specified event name with object on target.
7361		 *
7362		 * @method fire
7363		 * @param {Node/Document/Window} target Target element or object to fire event on.
7364		 * @param {String} name Name of the event to fire.
7365		 * @param {Object} evt Event object to send.
7366		 * @return {Event} Event object.
7367		 */
7368		fire: function(target, name, evt) {
7369			return this.events.fire(target, name, evt);
7370		},
7371
7372		// Returns the content editable state of a node
7373		getContentEditable: function(node) {
7374			var contentEditable;
7375
7376			// Check type
7377			if (node.nodeType != 1) {
7378				return null;
7379			}
7380
7381			// Check for fake content editable
7382			contentEditable = node.getAttribute("data-mce-contenteditable");
7383			if (contentEditable && contentEditable !== "inherit") {
7384				return contentEditable;
7385			}
7386
7387			// Check for real content editable
7388			return node.contentEditable !== "inherit" ? node.contentEditable : null;
7389		},
7390
7391		/**
7392		 * Destroys all internal references to the DOM to solve IE leak issues.
7393		 *
7394		 * @method destroy
7395		 */
7396		destroy: function() {
7397			var self = this;
7398
7399			// Unbind all events bound to window/document by editor instance
7400			if (self.boundEvents) {
7401				var i = self.boundEvents.length;
7402
7403				while (i--) {
7404					var item = self.boundEvents[i];
7405					this.events.unbind(item[0], item[1], item[2]);
7406				}
7407
7408				self.boundEvents = null;
7409			}
7410
7411			// Restore sizzle document to window.document
7412			// Since the current document might be removed producing "Permission denied" on IE see #6325
7413			if (Sizzle.setDocument) {
7414				Sizzle.setDocument();
7415			}
7416
7417			self.win = self.doc = self.root = self.events = self.frag = null;
7418		},
7419
7420		// #ifdef debug
7421
7422		dumpRng: function(r) {
7423			return (
7424				'startContainer: ' + r.startContainer.nodeName +
7425				', startOffset: ' + r.startOffset +
7426				', endContainer: ' + r.endContainer.nodeName +
7427				', endOffset: ' + r.endOffset
7428			);
7429		},
7430
7431		// #endif
7432
7433		_findSib: function(node, selector, name) {
7434			var self = this, func = selector;
7435
7436			if (node) {
7437				// If expression make a function of it using is
7438				if (typeof(func) == 'string') {
7439					func = function(node) {
7440						return self.is(node, selector);
7441					};
7442				}
7443
7444				// Loop all siblings
7445				for (node = node[name]; node; node = node[name]) {
7446					if (func(node)) {
7447						return node;
7448					}
7449				}
7450			}
7451
7452			return null;
7453		}
7454	};
7455
7456	/**
7457	 * Instance of DOMUtils for the current document.
7458	 *
7459	 * @static
7460	 * @property DOM
7461	 * @type tinymce.dom.DOMUtils
7462	 * @example
7463	 * // Example of how to add a class to some element by id
7464	 * tinymce.DOM.addClass('someid', 'someclass');
7465	 */
7466	DOMUtils.DOM = new DOMUtils(document);
7467
7468	return DOMUtils;
7469});
7470
7471// Included from: js/tinymce/classes/dom/ScriptLoader.js
7472
7473/**
7474 * ScriptLoader.js
7475 *
7476 * Copyright, Moxiecode Systems AB
7477 * Released under LGPL License.
7478 *
7479 * License: http://www.tinymce.com/license
7480 * Contributing: http://www.tinymce.com/contributing
7481 */
7482
7483/*globals console*/
7484
7485/**
7486 * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
7487 * when various items gets loaded. This class is useful to load external JavaScript files.
7488 *
7489 * @class tinymce.dom.ScriptLoader
7490 * @example
7491 * // Load a script from a specific URL using the global script loader
7492 * tinymce.ScriptLoader.load('somescript.js');
7493 *
7494 * // Load a script using a unique instance of the script loader
7495 * var scriptLoader = new tinymce.dom.ScriptLoader();
7496 *
7497 * scriptLoader.load('somescript.js');
7498 *
7499 * // Load multiple scripts
7500 * var scriptLoader = new tinymce.dom.ScriptLoader();
7501 *
7502 * scriptLoader.add('somescript1.js');
7503 * scriptLoader.add('somescript2.js');
7504 * scriptLoader.add('somescript3.js');
7505 *
7506 * scriptLoader.loadQueue(function() {
7507 *    alert('All scripts are now loaded.');
7508 * });
7509 */
7510define("tinymce/dom/ScriptLoader", [
7511	"tinymce/dom/DOMUtils",
7512	"tinymce/util/Tools"
7513], function(DOMUtils, Tools) {
7514	var DOM = DOMUtils.DOM;
7515	var each = Tools.each, grep = Tools.grep;
7516
7517	function ScriptLoader() {
7518		var QUEUED = 0,
7519			LOADING = 1,
7520			LOADED = 2,
7521			states = {},
7522			queue = [],
7523			scriptLoadedCallbacks = {},
7524			queueLoadedCallbacks = [],
7525			loading = 0,
7526			undef;
7527
7528		/**
7529		 * Loads a specific script directly without adding it to the load queue.
7530		 *
7531		 * @method load
7532		 * @param {String} url Absolute URL to script to add.
7533		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
7534		 * @param {Object} scope Optional scope to execute callback in.
7535		 */
7536		function loadScript(url, callback) {
7537			var dom = DOM, elm, id;
7538
7539			// Execute callback when script is loaded
7540			function done() {
7541				dom.remove(id);
7542
7543				if (elm) {
7544					elm.onreadystatechange = elm.onload = elm = null;
7545				}
7546
7547				callback();
7548			}
7549
7550			function error() {
7551				// Report the error so it's easier for people to spot loading errors
7552				if (typeof(console) !== "undefined" && console.log) {
7553					console.log("Failed to load: " + url);
7554				}
7555
7556				// We can't mark it as done if there is a load error since
7557				// A) We don't want to produce 404 errors on the server and
7558				// B) the onerror event won't fire on all browsers.
7559				// done();
7560			}
7561
7562			id = dom.uniqueId();
7563
7564			// Create new script element
7565			elm = document.createElement('script');
7566			elm.id = id;
7567			elm.type = 'text/javascript';
7568			elm.src = url;
7569
7570			// Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
7571			if ("onreadystatechange" in elm) {
7572				elm.onreadystatechange = function() {
7573					if (/loaded|complete/.test(elm.readyState)) {
7574						done();
7575					}
7576				};
7577			} else {
7578				elm.onload = done;
7579			}
7580
7581			// Add onerror event will get fired on some browsers but not all of them
7582			elm.onerror = error;
7583
7584			// Add script to document
7585			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
7586		}
7587
7588		/**
7589		 * Returns true/false if a script has been loaded or not.
7590		 *
7591		 * @method isDone
7592		 * @param {String} url URL to check for.
7593		 * @return {Boolean} true/false if the URL is loaded.
7594		 */
7595		this.isDone = function(url) {
7596			return states[url] == LOADED;
7597		};
7598
7599		/**
7600		 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
7601		 * the script loader or to skip it from loading some script.
7602		 *
7603		 * @method markDone
7604		 * @param {string} u Absolute URL to the script to mark as loaded.
7605		 */
7606		this.markDone = function(url) {
7607			states[url] = LOADED;
7608		};
7609
7610		/**
7611		 * Adds a specific script to the load queue of the script loader.
7612		 *
7613		 * @method add
7614		 * @param {String} url Absolute URL to script to add.
7615		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
7616		 * @param {Object} scope Optional scope to execute callback in.
7617		 */
7618		this.add = this.load = function(url, callback, scope) {
7619			var state = states[url];
7620
7621			// Add url to load queue
7622			if (state == undef) {
7623				queue.push(url);
7624				states[url] = QUEUED;
7625			}
7626
7627			if (callback) {
7628				// Store away callback for later execution
7629				if (!scriptLoadedCallbacks[url]) {
7630					scriptLoadedCallbacks[url] = [];
7631				}
7632
7633				scriptLoadedCallbacks[url].push({
7634					func: callback,
7635					scope: scope || this
7636				});
7637			}
7638		};
7639
7640		/**
7641		 * Starts the loading of the queue.
7642		 *
7643		 * @method loadQueue
7644		 * @param {function} callback Optional callback to execute when all queued items are loaded.
7645		 * @param {Object} scope Optional scope to execute the callback in.
7646		 */
7647		this.loadQueue = function(callback, scope) {
7648			this.loadScripts(queue, callback, scope);
7649		};
7650
7651		/**
7652		 * Loads the specified queue of files and executes the callback ones they are loaded.
7653		 * This method is generally not used outside this class but it might be useful in some scenarios.
7654		 *
7655		 * @method loadScripts
7656		 * @param {Array} scripts Array of queue items to load.
7657		 * @param {function} callback Optional callback to execute ones all items are loaded.
7658		 * @param {Object} scope Optional scope to execute callback in.
7659		 */
7660		this.loadScripts = function(scripts, callback, scope) {
7661			var loadScripts;
7662
7663			function execScriptLoadedCallbacks(url) {
7664				// Execute URL callback functions
7665				each(scriptLoadedCallbacks[url], function(callback) {
7666					callback.func.call(callback.scope);
7667				});
7668
7669				scriptLoadedCallbacks[url] = undef;
7670			}
7671
7672			queueLoadedCallbacks.push({
7673				func: callback,
7674				scope: scope || this
7675			});
7676
7677			loadScripts = function() {
7678				var loadingScripts = grep(scripts);
7679
7680				// Current scripts has been handled
7681				scripts.length = 0;
7682
7683				// Load scripts that needs to be loaded
7684				each(loadingScripts, function(url) {
7685					// Script is already loaded then execute script callbacks directly
7686					if (states[url] == LOADED) {
7687						execScriptLoadedCallbacks(url);
7688						return;
7689					}
7690
7691					// Is script not loading then start loading it
7692					if (states[url] != LOADING) {
7693						states[url] = LOADING;
7694						loading++;
7695
7696						loadScript(url, function() {
7697							states[url] = LOADED;
7698							loading--;
7699
7700							execScriptLoadedCallbacks(url);
7701
7702							// Load more scripts if they where added by the recently loaded script
7703							loadScripts();
7704						});
7705					}
7706				});
7707
7708				// No scripts are currently loading then execute all pending queue loaded callbacks
7709				if (!loading) {
7710					each(queueLoadedCallbacks, function(callback) {
7711						callback.func.call(callback.scope);
7712					});
7713
7714					queueLoadedCallbacks.length = 0;
7715				}
7716			};
7717
7718			loadScripts();
7719		};
7720	}
7721
7722	ScriptLoader.ScriptLoader = new ScriptLoader();
7723
7724	return ScriptLoader;
7725});
7726
7727// Included from: js/tinymce/classes/AddOnManager.js
7728
7729/**
7730 * AddOnManager.js
7731 *
7732 * Copyright, Moxiecode Systems AB
7733 * Released under LGPL License.
7734 *
7735 * License: http://www.tinymce.com/license
7736 * Contributing: http://www.tinymce.com/contributing
7737 */
7738
7739/**
7740 * This class handles the loading of themes/plugins or other add-ons and their language packs.
7741 *
7742 * @class tinymce.AddOnManager
7743 */
7744define("tinymce/AddOnManager", [
7745	"tinymce/dom/ScriptLoader",
7746	"tinymce/util/Tools"
7747], function(ScriptLoader, Tools) {
7748	var each = Tools.each;
7749
7750	function AddOnManager() {
7751		var self = this;
7752
7753		self.items = [];
7754		self.urls = {};
7755		self.lookup = {};
7756	}
7757
7758	AddOnManager.prototype = {
7759		/**
7760		 * Returns the specified add on by the short name.
7761		 *
7762		 * @method get
7763		 * @param {String} name Add-on to look for.
7764		 * @return {tinymce.Theme/tinymce.Plugin}
7764 Theme or plugin add-on instance or undefined.
7765		 */
7766		get: function(name) {
7767			if (this.lookup[name]) {
7768				return this.lookup[name].instance;
7769			} else {
7770				return undefined;
7771			}
7772		},
7773
7774		dependencies: function(name) {
7775			var result;
7776
7777			if (this.lookup[name]) {
7778				result = this.lookup[name].dependencies;
7779			}
7780
7781			return result || [];
7782		},
7783
7784		/**
7785		 * Loads a language pack for the specified add-on.
7786		 *
7787		 * @method requireLangPack
7788		 * @param {String} name Short name of the add-on.
7789		 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
7790		 */
7791		requireLangPack: function(name, languages) {
7792			if (AddOnManager.language && AddOnManager.languageLoad !== false) {
7793				if (languages && new RegExp('([, ]|\\b)' + AddOnManager.language + '([, ]|\\b)').test(languages) === false) {
7794					return;
7795				}
7796
7797				ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + AddOnManager.language + '.js');
7798			}
7799		},
7800
7801		/**
7802		 * Adds a instance of the add-on by it's short name.
7803		 *
7804		 * @method add
7805		 * @param {String} id Short name/id for the add-on.
7806		 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
7807		 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
7808		 * @example
7809		 * // Create a simple plugin
7810		 * tinymce.create('tinymce.plugins.TestPlugin', {
7811		 *   TestPlugin: function(ed, url) {
7812		 *   ed.on('click', function(e) {
7813		 *      ed.windowManager.alert('Hello World!');
7814		 *   });
7815		 *   }
7816		 * });
7817		 *
7818		 * // Register plugin using the add method
7819		 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
7820		 *
7821		 * // Initialize TinyMCE
7822		 * tinymce.init({
7823		 *  ...
7824		 *  plugins: '-test' // Init the plugin but don't try to load it
7825		 * });
7826		 */
7827		add: function(id, addOn, dependencies) {
7828			this.items.push(addOn);
7829			this.lookup[id] = {instance: addOn, dependencies: dependencies};
7830
7831			return addOn;
7832		},
7833
7834		createUrl: function(baseUrl, dep) {
7835			if (typeof dep === "object") {
7836				return dep;
7837			} else {
7838				return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
7839			}
7840		},
7841
7842		/**
7843		 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
7844		 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
7845		 * components are put together into the plugin.js file and compressed correctly.
7846		 *
7847		 * @method addComponents
7848		 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
7849		 * @param {Array} scripts Array containing the names of the scripts to load.
7850		 */
7851		addComponents: function(pluginName, scripts) {
7852			var pluginUrl = this.urls[pluginName];
7853
7854			each(scripts, function(script) {
7855				ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
7856			});
7857		},
7858
7859		/**
7860		 * Loads an add-on from a specific url.
7861		 *
7862		 * @method load
7863		 * @param {String} n Short name of the add-on that gets loaded.
7864		 * @param {String} u URL to the add-on that will get loaded.
7865		 * @param {function} cb Optional callback to execute ones the add-on is loaded.
7866		 * @param {Object} s Optional scope to execute the callback in.
7867		 * @example
7868		 * // Loads a plugin from an external URL
7869		 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
7870		 *
7871		 * // Initialize TinyMCE
7872		 * tinymce.init({
7873		 *  ...
7874		 *  plugins: '-myplugin' // Don't try to load it again
7875		 * });
7876		 */
7877		load: function(n, u, cb, s) {
7878			var t = this, url = u;
7879
7880			function loadDependencies() {
7881				var dependencies = t.dependencies(n);
7882
7883				each(dependencies, function(dep) {
7884					var newUrl = t.createUrl(u, dep);
7885
7886					t.load(newUrl.resource, newUrl, undefined, undefined);
7887				});
7888
7889				if (cb) {
7890					if (s) {
7891						cb.call(s);
7892					} else {
7893						cb.call(ScriptLoader);
7894					}
7895				}
7896			}
7897
7898			if (t.urls[n]) {
7899				return;
7900			}
7901
7902			if (typeof u === "object") {
7903				url = u.prefix + u.resource + u.suffix;
7904			}
7905
7906			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
7907				url = AddOnManager.baseURL + '/' + url;
7908			}
7909
7910			t.urls[n] = url.substring(0, url.lastIndexOf('/'));
7911
7912			if (t.lookup[n]) {
7913				loadDependencies();
7914			} else {
7915				ScriptLoader.ScriptLoader.add(url, loadDependencies, s);
7916			}
7917		}
7918	};
7919
7920	AddOnManager.PluginManager = new AddOnManager();
7921	AddOnManager.ThemeManager = new AddOnManager();
7922
7923	return AddOnManager;
7924});
7925
7926/**
7927 * TinyMCE theme class.
7928 *
7929 * @class tinymce.Theme
7930 */
7931
7932/**
7933 * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
7934 *
7935 * @method renderUI
7936 * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
7937 * @return {Object}
7937 an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
7938 */
7939
7940/**
7941 * 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.
7942 *
7943 * @class tinymce.Plugin
7944 * @example
7945 * tinymce.PluginManager.add('example', function(editor, url) {
7946 *     // Add a button that opens a window
7947 *     editor.addButton('example', {
7948 *         text: 'My button',
7949 *         icon: false,
7950 *         onclick: function() {
7951 *             // Open window
7952 *             editor.windowManager.open({
7953 *                 title: 'Example plugin',
7954 *                 body: [
7955 *                     {type: 'textbox', name: 'title', label: 'Title'}
7956 *                 ],
7957 *                 onsubmit: function(e) {
7958 *                     // Insert content when the window form is submitted
7959 *                     editor.insertContent('Title: ' + e.data.title);
7960 *                 }
7961 *             });
7962 *         }
7963 *     });
7964 *
7965 *     // Adds a menu item to the tools menu
7966 *     editor.addMenuItem('example', {
7967 *         text: 'Example plugin',
7968 *         context: 'tools',
7969 *         onclick: function() {
7970 *             // Open window with a specific url
7971 *             editor.windowManager.open({
7972 *                 title: 'TinyMCE site',
7973 *                 url: 'http://www.tinymce.com',
7974 *                 width: 800,
7975 *                 height: 600,
7976 *                 buttons: [{
7977 *                     text: 'Close',
7978 *                     onclick: 'close'
7979 *                 }]
7980 *             });
7981 *         }
7982 *     });
7983 * });
7984 */
7985
7986// Included from: js/tinymce/classes/html/Node.js
7987
7988/**
7989 * Node.js
7990 *
7991 * Copyright, Moxiecode Systems AB
7992 * Released under LGPL License.
7993 *
7994 * License: http://www.tinymce.com/license
7995 * Contributing: http://www.tinymce.com/contributing
7996 */
7997
7998/**
7999 * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
8000 *
8001 * @example
8002 * var node = new tinymce.html.Node('strong', 1);
8003 * someRoot.append(node);
8004 *
8005 * @class tinymce.html.Node
8006 * @version 3.4
8007 */
8008define("tinymce/html/Node", [], function() {
8009	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
8010		'#text': 3,
8011		'#comment': 8,
8012		'#cdata': 4,
8013		'#pi': 7,
8014		'#doctype': 10,
8015		'#document-fragment': 11
8016	};
8017
8018	// Walks the tree left/right
8019	function walk(node, root_node, prev) {
8020		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
8021
8022		// Walk into nodes if it has a start
8023		if (node[startName]) {
8024			return node[startName];
8025		}
8026
8027		// Return the sibling if it has one
8028		if (node !== root_node) {
8029			sibling = node[siblingName];
8030
8031			if (sibling) {
8032				return sibling;
8033			}
8034
8035			// Walk up the parents to look for siblings
8036			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
8037				sibling = parent[siblingName];
8038
8039				if (sibling) {
8040					return sibling;
8041				}
8042			}
8043		}
8044	}
8045
8046	/**
8047	 * Constructs a new Node instance.
8048	 *
8049	 * @constructor
8050	 * @method Node
8051	 * @param {String} name Name of the node type.
8052	 * @param {Number} type Numeric type representing the node.
8053	 */
8054	function Node(name, type) {
8055		this.name = name;
8056		this.type = type;
8057
8058		if (type === 1) {
8059			this.attributes = [];
8060			this.attributes.map = {};
8061		}
8062	}
8063
8064	Node.prototype = {
8065		/**
8066		 * Replaces the current node with the specified one.
8067		 *
8068		 * @example
8069		 * someNode.replace(someNewNode);
8070		 *
8071		 * @method replace
8072		 * @param {tinymce.html.Node} node Node to replace the current node with.
8073		 * @return {tinymce.html.Node} The old node that got replaced.
8074		 */
8075		replace: function(node) {
8076			var self = this;
8077
8078			if (node.parent) {
8079				node.remove();
8080			}
8081
8082			self.insert(node, self);
8083			self.remove();
8084
8085			return self;
8086		},
8087
8088		/**
8089		 * Gets/sets or removes an attribute by name.
8090		 *
8091		 * @example
8092		 * someNode.attr("name", "value"); // Sets an attribute
8093		 * console.log(someNode.attr("name")); // Gets an attribute
8094		 * someNode.attr("name", null); // Removes an attribute
8095		 *
8096		 * @method attr
8097		 * @param {String} name Attribute name to set or get.
8098		 * @param {String} value Optional value to set.
8099		 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
8100		 */
8101		attr: function(name, value) {
8102			var self = this, attrs, i, undef;
8103
8104			if (typeof name !== "string") {
8105				for (i in name) {
8106					self.attr(i, name[i]);
8107				}
8108
8109				return self;
8110			}
8111
8112			if ((attrs = self.attributes)) {
8113				if (value !== undef) {
8114					// Remove attribute
8115					if (value === null) {
8116						if (name in attrs.map) {
8117							delete attrs.map[name];
8118
8119							i = attrs.length;
8120							while (i--) {
8121								if (attrs[i].name === name) {
8122									attrs = attrs.splice(i, 1);
8123									return self;
8124								}
8125							}
8126						}
8127
8128						return self;
8129					}
8130
8131					// Set attribute
8132					if (name in attrs.map) {
8133						// Set attribute
8134						i = attrs.length;
8135						while (i--) {
8136							if (attrs[i].name === name) {
8137								attrs[i].value = value;
8138								break;
8139							}
8140						}
8141					} else {
8142						attrs.push({name: name, value: value});
8143					}
8144
8145					attrs.map[name] = value;
8146
8147					return self;
8148				} else {
8149					return attrs.map[name];
8150				}
8151			}
8152		},
8153
8154		/**
8155		 * Does a shallow clones the node into a new node. It will also exclude id attributes since
8156		 * there should only be one id per document.
8157		 *
8158		 * @example
8159		 * var clonedNode = node.clone();
8160		 *
8161		 * @method clone
8162		 * @return {tinymce.html.Node} New copy of the original node.
8163		 */
8164		clone: function() {
8165			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
8166
8167			// Clone element attributes
8168			if ((selfAttrs = self.attributes)) {
8169				cloneAttrs = [];
8170				cloneAttrs.map = {};
8171
8172				for (i = 0, l = selfAttrs.length; i < l; i++) {
8173					selfAttr = selfAttrs[i];
8174
8175					// Clone everything except id
8176					if (selfAttr.name !== 'id') {
8177						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
8178						cloneAttrs.map[selfAttr.name] = selfAttr.value;
8179					}
8180				}
8181
8182				clone.attributes = cloneAttrs;
8183			}
8184
8185			clone.value = self.value;
8186			clone.shortEnded = self.shortEnded;
8187
8188			return clone;
8189		},
8190
8191		/**
8192		 * Wraps the node in in another node.
8193		 *
8194		 * @example
8195		 * node.wrap(wrapperNode);
8196		 *
8197		 * @method wrap
8198		 */
8199		wrap: function(wrapper) {
8200			var self = this;
8201
8202			self.parent.insert(wrapper, self);
8203			wrapper.append(self);
8204
8205			return self;
8206		},
8207
8208		/**
8209		 * Unwraps the node in other words it removes the node but keeps the children.
8210		 *
8211		 * @example
8212		 * node.unwrap();
8213		 *
8214		 * @method unwrap
8215		 */
8216		unwrap: function() {
8217			var self = this, node, next;
8218
8219			for (node = self.firstChild; node; ) {
8220				next = node.next;
8221				self.insert(node, self, true);
8222				node = next;
8223			}
8224
8225			self.remove();
8226		},
8227
8228		/**
8229		 * Removes the node from it's parent.
8230		 *
8231		 * @example
8232		 * node.remove();
8233		 *
8234		 * @method remove
8235		 * @return {tinymce.html.Node} Current node that got removed.
8236		 */
8237		remove: function() {
8238			var self = this, parent = self.parent, next = self.next, prev = self.prev;
8239
8240			if (parent) {
8241				if (parent.firstChild === self) {
8242					parent.firstChild = next;
8243
8244					if (next) {
8245						next.prev = null;
8246					}
8247				} else {
8248					prev.next = next;
8249				}
8250
8251				if (parent.lastChild === self) {
8252					parent.lastChild = prev;
8253
8254					if (prev) {
8255						prev.next = null;
8256					}
8257				} else {
8258					next.prev = prev;
8259				}
8260
8261				self.parent = self.next = self.prev = null;
8262			}
8263
8264			return self;
8265		},
8266
8267		/**
8268		 * Appends a new node as a child of the current node.
8269		 *
8270		 * @example
8271		 * node.append(someNode);
8272		 *
8273		 * @method append
8274		 * @param {tinymce.html.Node} node Node to append as a child of the current one.
8275		 * @return {tinymce.html.Node} The node that got appended.
8276		 */
8277		append: function(node) {
8278			var self = this, last;
8279
8280			if (node.parent) {
8281				node.remove();
8282			}
8283
8284			last = self.lastChild;
8285			if (last) {
8286				last.next = node;
8287				node.prev = last;
8288				self.lastChild = node;
8289			} else {
8290				self.lastChild = self.firstChild = node;
8291			}
8292
8293			node.parent = self;
8294
8295			return node;
8296		},
8297
8298		/**
8299		 * Inserts a node at a specific position as a child of the current node.
8300		 *
8301		 * @example
8302		 * parentNode.insert(newChildNode, oldChildNode);
8303		 *
8304		 * @method insert
8305		 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
8306		 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
8307		 * @param {Boolean} before Optional state to insert the node before the reference node.
8308		 * @return {tinymce.html.Node} The node that got inserted.
8309		 */
8310		insert: function(node, ref_node, before) {
8311			var parent;
8312
8313			if (node.parent) {
8314				node.remove();
8315			}
8316
8317			parent = ref_node.parent || this;
8318
8319			if (before) {
8320				if (ref_node === parent.firstChild) {
8321					parent.firstChild = node;
8322				} else {
8323					ref_node.prev.next = node;
8324				}
8325
8326				node.prev = ref_node.prev;
8327				node.next = ref_node;
8328				ref_node.prev = node;
8329			} else {
8330				if (ref_node === parent.lastChild) {
8331					parent.lastChild = node;
8332				} else {
8333					ref_node.next.prev = node;
8334				}
8335
8336				node.next = ref_node.next;
8337				node.prev = ref_node;
8338				ref_node.next = node;
8339			}
8340
8341			node.parent = parent;
8342
8343			return node;
8344		},
8345
8346		/**
8347		 * Get all children by name.
8348		 *
8349		 * @method getAll
8350		 * @param {String} name Name of the child nodes to collect.
8351		 * @return {Array} Array with child nodes matchin the specified name.
8352		 */
8353		getAll: function(name) {
8354			var self = this, node, collection = [];
8355
8356			for (node = self.firstChild; node; node = walk(node, self)) {
8357				if (node.name === name) {
8358					collection.push(node);
8359				}
8360			}
8361
8362			return collection;
8363		},
8364
8365		/**
8366		 * Removes all children of the current node.
8367		 *
8368		 * @method empty
8369		 * @return {tinymce.html.Node} The current node that got cleared.
8370		 */
8371		empty: function() {
8372			var self = this, nodes, i, node;
8373
8374			// Remove all children
8375			if (self.firstChild) {
8376				nodes = [];
8377
8378				// Collect the children
8379				for (node = self.firstChild; node; node = walk(node, self)) {
8380					nodes.push(node);
8381				}
8382
8383				// Remove the children
8384				i = nodes.length;
8385				while (i--) {
8386					node = nodes[i];
8387					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
8388				}
8389			}
8390
8391			self.firstChild = self.lastChild = null;
8392
8393			return self;
8394		},
8395
8396		/**
8397		 * Returns true/false if the node is to be considered empty or not.
8398		 *
8399		 * @example
8400		 * node.isEmpty({img: true});
8401		 * @method isEmpty
8402		 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
8403		 * @return {Boolean} true/false if the node is empty or not.
8404		 */
8405		isEmpty: function(elements) {
8406			var self = this, node = self.firstChild, i, name;
8407
8408			if (node) {
8409				do {
8410					if (node.type === 1) {
8411						// Ignore bogus elements
8412						if (node.attributes.map['data-mce-bogus']) {
8413							continue;
8414						}
8415
8416						// Keep empty elements like <img />
8417						if (elements[node.name]) {
8418							return false;
8419						}
8420
8421						// Keep elements with data attributes or name attribute like <a name="1"></a>
8422						i = node.attributes.length;
8423						while (i--) {
8424							name = node.attributes[i].name;
8425							if (name === "name" || name.indexOf('data-mce-') === 0) {
8426								return false;
8427							}
8428						}
8429					}
8430
8431					// Keep comments
8432					if (node.type === 8) {
8433						return false;
8434					}
8435
8436					// Keep non whitespace text nodes
8437					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
8438						return false;
8439					}
8440				} while ((node = walk(node, self)));
8441			}
8442
8443			return true;
8444		},
8445
8446		/**
8447		 * Walks to the next or previous node and returns that node or null if it wasn't found.
8448		 *
8449		 * @method walk
8450		 * @param {Boolean} prev Optional previous node state defaults to false.
8451		 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
8452		 */
8453		walk: function(prev) {
8454			return walk(this, null, prev);
8455		}
8456	};
8457
8458	/**
8459	 * Creates a node of a specific type.
8460	 *
8461	 * @static
8462	 * @method create
8463	 * @param {String} name Name of the node type to create for example "b" or "#text".
8464	 * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
8465	 */
8466	Node.create = function(name, attrs) {
8467		var node, attrName;
8468
8469		// Create node
8470		node = new Node(name, typeLookup[name] || 1);
8471
8472		// Add attributes if needed
8473		if (attrs) {
8474			for (attrName in attrs) {
8475				node.attr(attrName, attrs[attrName]);
8476			}
8477		}
8478
8479		return node;
8480	};
8481
8482	return Node;
8483});
8484
8485// Included from: js/tinymce/classes/html/Schema.js
8486
8487/**
8488 * Schema.js
8489 *
8490 * Copyright, Moxiecode Systems AB
8491 * Released under LGPL License.
8492 *
8493 * License: http://www.tinymce.com/license
8494 * Contributing: http://www.tinymce.com/contributing
8495 */
8496
8497/**
8498 * Schema validator class.
8499 *
8500 * @class tinymce.html.Schema
8501 * @example
8502 *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
8503 *    alert('span is valid child of p.');
8504 *
8505 *  if (tinymce.activeEditor.schema.getElementRule('p'))
8506 *    alert('P is a valid element.');
8507 *
8508 * @class tinymce.html.Schema
8509 * @version 3.4
8510 */
8511define("tinymce/html/Schema", [
8512	"tinymce/util/Tools"
8513], function(Tools) {
8514	var mapCache = {};
8515	var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
8516
8517	function split(items, delim) {
8518		return items ? items.split(delim || ' ') : [];
8519	}
8520
8521	/**
8522	 * Builds a schema lookup table
8523	 *
8524	 * @private
8525	 * @param {String} type html4, html5 or html5-strict schema type.
8526	 * @return {Object} Schema lookup table.
8527	 */
8528	function compileSchema(type) {
8529		var schema = {}, globalAttributes, eventAttributes, blockContent;
8530		var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
8531
8532		function add(name, attributes, children) {
8533			var ni, i, attributesOrder, args = arguments;
8534
8535			function arrayToMap(array) {
8536				var map = {}, i, l;
8537
8538				for (i = 0, l = array.length; i < l; i++) {
8539					map[array[i]] = {};
8540				}
8541
8542				return map;
8543			}
8544
8545			children = children || [];
8546			attributes = attributes || "";
8547
8548			if (typeof(children) === "string") {
8549				children = split(children);
8550			}
8551
8552			// Split string children
8553			for (i = 3; i < args.length; i++) {
8554				if (typeof(args[i]) === "string") {
8555					args[i] = split(args[i]);
8556				}
8557
8558				children.push.apply(children, args[i]);
8559			}
8560
8561			name = split(name);
8562			ni = name.length;
8563			while (ni--) {
8564				attributesOrder = [].concat(globalAttributes, split(attributes));
8565				schema[name[ni]] = {
8566					attributes: arrayToMap(attributesOrder),
8567					attributesOrder: attributesOrder,
8568					children: arrayToMap(children)
8569				};
8570			}
8571		}
8572
8573		function addAttrs(name, attributes) {
8574			var ni, schemaItem, i, l;
8575
8576			name = split(name);
8577			ni = name.length;
8578			attributes = split(attributes);
8579			while (ni--) {
8580				schemaItem = schema[name[ni]];
8581				for (i = 0, l = attributes.length; i < l; i++) {
8582					schemaItem.attributes[attributes[i]] = {};
8583					schemaItem.attributesOrder.push(attributes[i]);
8584				}
8585			}
8586		}
8587
8588		// Use cached schema
8589		if (mapCache[type]) {
8590			return mapCache[type];
8591		}
8592
8593		// Attributes present on all elements
8594		globalAttributes = split("id accesskey class dir lang style tabindex title");
8595
8596		// Event attributes can be opt-in/opt-out
8597		eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
8598				"ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
8599				"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
8600				"onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
8601				"onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
8602				"onwaiting"
8603		);
8604
8605		// Block content elements
8606		blockContent = split(
8607			"address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
8608		);
8609
8610		// Phrasing content elements from the HTML5 spec (inline)
8611		phrasingContent = split(
8612			"a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
8613			"label map noscript object q s samp script select small span strong sub sup " +
8614			"textarea u var #text #comment"
8615		);
8616
8617		// Add HTML5 items to globalAttributes, blockContent, phrasingContent
8618		if (type != "html4") {
8619			globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
8620				"hidden spellcheck translate"));
8621			blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
8622			phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output progress time wbr " +
8623				"video ruby bdi keygen"));
8624		}
8625
8626		// Add HTML4 elements unless it's html5-strict
8627		if (type != "html5-strict") {
8628			globalAttributes.push("xml:lang");
8629
8630			html4PhrasingContent = split("acronym applet basefont big font strike tt");
8631			phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
8632
8633			each(html4PhrasingContent, function(name) {
8634				add(name, "", phrasingContent);
8635			});
8636
8637			html4BlockContent = split("center dir isindex noframes");
8638			blockContent.push.apply(blockContent, html4BlockContent);
8639
8640			// Flow content elements from the HTML5 spec (block+inline)
8641			flowContent = [].concat(blockContent, phrasingContent);
8642
8643			each(html4BlockContent, function(name) {
8644				add(name, "", flowContent);
8645			});
8646		}
8647
8648		// Flow content elements from the HTML5 spec (block+inline)
8649		flowContent = flowContent || [].concat(blockContent, phrasingContent);
8650
8651		// HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
8652		// Schema items <element name>, <specific attributes>, <children ..>
8653		add("html", "manifest", "head body");
8654		add("head", "", "base command link meta noscript script style title");
8655		add("title hr noscript br");
8656		add("base", "href target");
8657		add("link", "href rel media hreflang type sizes hreflang");
8658		add("meta", "name http-equiv content charset");
8659		add("style", "media type scoped");
8660		add("script", "src async defer type charset");
8661		add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
8662				"onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
8663				"onpopstate onresize onscroll onstorage onunload", flowContent);
8664		add("address dt dd div caption", "", flowContent);
8665		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);
8666		add("blockquote", "cite", flowContent);
8667		add("ol", "reversed start type", "li");
8668		add("ul", "", "li");
8669		add("li", "value", flowContent);
8670		add("dl", "", "dt dd");
8671		add("a", "href target rel media hreflang type", phrasingContent);
8672		add("q", "cite", phrasingContent);
8673		add("ins del", "cite datetime", flowContent);
8674		add("img", "src alt usemap ismap width height");
8675		add("iframe", "src name width height", flowContent);
8676		add("embed", "src type width height");
8677		add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
8678		add("param", "name value");
8679		add("map", "name", flowContent, "area");
8680		add("area", "alt coords shape href target rel media hreflang type");
8681		add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
8682		add("colgroup", "span", "col");
8683		add("col", "span");
8684		add("tbody thead tfoot", "", "tr");
8685		add("tr", "", "td th");
8686		add("td", "colspan rowspan headers", flowContent);
8687		add("th", "colspan rowspan headers scope abbr", flowContent);
8688		add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
8689		add("fieldset", "disabled form name", flowContent, "legend");
8690		add("label", "form for", phrasingContent);
8691		add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
8692				"formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
8693		);
8694		add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
8695			type == "html4" ? flowContent : phrasingContent);
8696		add("select", "disabled form multiple name required size", "option optgroup");
8697		add("optgroup", "disabled label", "option");
8698		add("option", "disabled label selected value");
8699		add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
8700		add("menu", "type label", flowContent, "li");
8701		add("noscript", "", flowContent);
8702
8703		// Extend with HTML5 elements
8704		if (type != "html4") {
8705			add("wbr");
8706			add("ruby", "", phrasingContent, "rt rp");
8707			add("figcaption", "", flowContent);
8708			add("mark rt rp summary bdi", "", phrasingContent);
8709			add("canvas", "width height", flowContent);
8710			add("video", "src crossorigin poster preload autoplay mediagroup loop " +
8711				"muted controls width height", flowContent, "track source");
8712			add("audio", "src crossorigin preload autoplay mediagroup loop muted controls", flowContent, "track source");
8713			add("source", "src type media");
8714			add("track", "kind src srclang label default");
8715			add("datalist", "", phrasingContent, "option");
8716			add("article section nav aside header footer", "", flowContent);
8717			add("hgroup", "", "h1 h2 h3 h4 h5 h6");
8718			add("figure", "", flowContent, "figcaption");
8719			add("time", "datetime", phrasingContent);
8720			add("dialog", "open", flowContent);
8721			add("command", "type label icon disabled checked radiogroup command");
8722			add("output", "for form name", phrasingContent);
8723			add("progress", "value max", phrasingContent);
8724			add("meter", "value min max low high optimum", phrasingContent);
8725			add("details", "open", flowContent, "summary");
8726			add("keygen", "autofocus challenge disabled form keytype name");
8727		}
8728
8729		// Extend with HTML4 attributes unless it's html5-strict
8730		if (type != "html5-strict") {
8731			addAttrs("script", "language xml:space");
8732			addAttrs("style", "xml:space");
8733			addAttrs("object", "declare classid codebase codetype archive standby align border hspace vspace");
8734			addAttrs("param", "valuetype type");
8735			addAttrs("a", "charset name rev shape coords");
8736			addAttrs("br", "clear");
8737			addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
8738			addAttrs("img", "name longdesc align border hspace vspace");
8739			addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
8740			addAttrs("font basefont", "size color face");
8741			addAttrs("input", "usemap align");
8742			addAttrs("select", "onchange");
8743			addAttrs("textarea");
8744			addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
8745			addAttrs("ul", "type compact");
8746			addAttrs("li", "type");
8747			addAttrs("ol dl menu dir", "compact");
8748			addAttrs("pre", "width xml:space");
8749			addAttrs("hr", "align noshade size width");
8750			addAttrs("isindex", "prompt");
8751			addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
8752			addAttrs("col", "width align char charoff valign");
8753			addAttrs("colgroup", "width align char charoff valign");
8754			addAttrs("thead", "align char charoff valign");
8755			addAttrs("tr", "align char charoff valign bgcolor");
8756			addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
8757			addAttrs("form", "accept");
8758			addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
8759			addAttrs("tfoot", "align char charoff valign");
8760			addAttrs("tbody", "align char charoff valign");
8761			addAttrs("area", "nohref");
8762			addAttrs("body", "background bgcolor text link vlink alink");
8763		}
8764
8765		// Extend with HTML5 attributes unless it's html4
8766		if (type != "html4") {
8767			addAttrs("input button select textarea", "autofocus");
8768			addAttrs("input textarea", "placeholder");
8769			addAttrs("a", "download");
8770			addAttrs("link script img", "crossorigin");
8771			addAttrs("iframe", "srcdoc sandbox seamless allowfullscreen");
8772		}
8773
8774		// Special: iframe, ruby, video, audio, label
8775
8776		// Delete children of the same name from it's parent
8777		// For example: form can't have a child of the name form
8778		each(split('a form meter progress dfn'), function(name) {
8779			if (schema[name]) {
8780				delete schema[name].children[name];
8781			}
8782		});
8783
8784		// Delete header, footer, sectioning and heading content descendants
8785		/*each('dt th address', function(name) {
8786			delete schema[name].children[name];
8787		});*/
8788
8789		// Caption can't have tables
8790		delete schema.caption.children.table;
8791
8792		// TODO: LI:s can only have value if parent is OL
8793
8794		// TODO: Handle transparent elements
8795		// a ins del canvas map
8796
8797		mapCache[type] = schema;
8798
8799		return schema;
8800	}
8801
8802	/**
8803	 * Constructs a new Schema instance.
8804	 *
8805	 * @constructor
8806	 * @method Schema
8807	 * @param {Object} settings Name/value settings object.
8808	 */
8809	return function(settings) {
8810		var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
8811		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap;
8812		var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, customElementsMap = {}, specialElements = {};
8813
8814		// Creates an lookup table map object for the specified option or the default value
8815		function createLookupTable(option, default_value, extendWith) {
8816			var value = settings[option];
8817
8818			if (!value) {
8819				// Get cached default map or make it if needed
8820				value = mapCache[option];
8821
8822				if (!value) {
8823					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
8824					value = extend(value, extendWith);
8825
8826					mapCache[option] = value;
8827				}
8828			} else {
8829				// Create custom map
8830				value = makeMap(value, ',', makeMap(value.toUpperCase(), ' '));
8831			}
8832
8833			return value;
8834		}
8835
8836		settings = settings || {};
8837		schemaItems = compileSchema(settings.schema);
8838
8839		// Allow all elements and attributes if verify_html is set to false
8840		if (settings.verify_html === false) {
8841			settings.valid_elements = '*[*]';
8842		}
8843
8844		// Build styles list
8845		if (settings.valid_styles) {
8846			validStyles = {};
8847
8848			// Convert styles into a rule list
8849			each(settings.valid_styles, function(value, key) {
8850				validStyles[key] = explode(value);
8851			});
8852		}
8853
8854		// Setup map objects
8855		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
8856		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
8857		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
8858			'meta param embed source wbr track');
8859		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
8860			'noshade nowrap readonly selected autoplay loop controls');
8861		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
8862		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
8863						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
8864		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
8865						'th tr td li ol ul caption dl dt dd noscript menu isindex samp option ' +
8866						'datalist select optgroup', textBlockElementsMap);
8867
8868		each((settings.special || 'script noscript style textarea').split(' '), function(name) {
8869			specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
8870		});
8871
8872		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
8873		function patternToRegExp(str) {
8874			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
8875		}
8876
8877		// Parses the specified valid_elements string and adds to the current rules
8878		// This function is a bit hard to read since it's heavily optimized for speed
8879		function addValidElements(valid_elements) {
8880			var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
8881				prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
8882				elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
8883				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
8884				hasPatternsRegExp = /[*?+]/;
8885
8886			if (valid_elements) {
8887				// Split valid elements into an array with rules
8888				valid_elements = split(valid_elements, ',');
8889
8890				if (elements['@']) {
8891					globalAttributes = elements['@'].attributes;
8892					globalAttributesOrder = elements['@'].attributesOrder;
8893				}
8894
8895				// Loop all rules
8896				for (ei = 0, el = valid_elements.length; ei < el; ei++) {
8897					// Parse element rule
8898					matches = elementRuleRegExp.exec(valid_elements[ei]);
8899					if (matches) {
8900						// Setup local names for matches
8901						prefix = matches[1];
8902						elementName = matches[2];
8903						outputName = matches[3];
8904						attrData = matches[5];
8905
8906						// Create new attributes and attributesOrder
8907						attributes = {};
8908						attributesOrder = [];
8909
8910						// Create the new element
8911						element = {
8912							attributes: attributes,
8913							attributesOrder: attributesOrder
8914						};
8915
8916						// Padd empty elements prefix
8917						if (prefix === '#') {
8918							element.paddEmpty = true;
8919						}
8920
8921						// Remove empty elements prefix
8922						if (prefix === '-') {
8923							element.removeEmpty = true;
8924						}
8925
8926						if (matches[4] === '!') {
8927							element.removeEmptyAttrs = true;
8928						}
8929
8930						// Copy attributes from global rule into current rule
8931						if (globalAttributes) {
8932							for (key in globalAttributes) {
8933								attributes[key] = globalAttributes[key];
8934							}
8935
8936							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
8937						}
8938
8939						// Attributes defined
8940						if (attrData) {
8941							attrData = split(attrData, '|');
8942							for (ai = 0, al = attrData.length; ai < al; ai++) {
8943								matches = attrRuleRegExp.exec(attrData[ai]);
8944								if (matches) {
8945									attr = {};
8946									attrType = matches[1];
8947									attrName = matches[2].replace(/::/g, ':');
8948									prefix = matches[3];
8949									value = matches[4];
8950
8951									// Required
8952									if (attrType === '!') {
8953										element.attributesRequired = element.attributesRequired || [];
8954										element.attributesRequired.push(attrName);
8955										attr.required = true;
8956									}
8957
8958									// Denied from global
8959									if (attrType === '-') {
8960										delete attributes[attrName];
8961										attributesOrder.splice(inArray(attributesOrder, attrName), 1);
8962										continue;
8963									}
8964
8965									// Default value
8966									if (prefix) {
8967										// Default value
8968										if (prefix === '=') {
8969											element.attributesDefault = element.attributesDefault || [];
8970											element.attributesDefault.push({name: attrName, value: value});
8971											attr.defaultValue = value;
8972										}
8973
8974										// Forced value
8975										if (prefix === ':') {
8976											element.attributesForced = element.attributesForced || [];
8977											element.attributesForced.push({name: attrName, value: value});
8978											attr.forcedValue = value;
8979										}
8980
8981										// Required values
8982										if (prefix === '<') {
8983											attr.validValues = makeMap(value, '?');
8984										}
8985									}
8986
8987									// Check for attribute patterns
8988									if (hasPatternsRegExp.test(attrName)) {
8989										element.attributePatterns = element.attributePatterns || [];
8990										attr.pattern = patternToRegExp(attrName);
8991										element.attributePatterns.push(attr);
8992									} else {
8993										// Add attribute to order list if it doesn't already exist
8994										if (!attributes[attrName]) {
8995											attributesOrder.push(attrName);
8996										}
8997
8998										attributes[attrName] = attr;
8999									}
9000								}
9001							}
9002						}
9003
9004						// Global rule, store away these for later usage
9005						if (!globalAttributes && elementName == '@') {
9006							globalAttributes = attributes;
9007							globalAttributesOrder = attributesOrder;
9008						}
9009
9010						// Handle substitute elements such as b/strong
9011						if (outputName) {
9012							element.outputName = elementName;
9013							elements[outputName] = element;
9014						}
9015
9016						// Add pattern or exact element
9017						if (hasPatternsRegExp.test(elementName)) {
9018							element.pattern = patternToRegExp(elementName);
9019							patternElements.push(element);
9020						} else {
9021							elements[elementName] = element;
9022						}
9023					}
9024				}
9025			}
9026		}
9027
9028		function setValidElements(valid_elements) {
9029			elements = {};
9030			patternElements = [];
9031
9032			addValidElements(valid_elements);
9033
9034			each(schemaItems, function(element, name) {
9035				children[name] = element.children;
9036			});
9037		}
9038
9039		// Adds custom non HTML elements to the schema
9040		function addCustomElements(custom_elements) {
9041			var customElementRegExp = /^(~)?(.+)$/;
9042
9043			if (custom_elements) {
9044				each(split(custom_elements, ','), function(rule) {
9045					var matches = customElementRegExp.exec(rule),
9046						inline = matches[1] === '~',
9047						cloneName = inline ? 'span' : 'div',
9048						name = matches[2];
9049
9050					children[name] = children[cloneName];
9051					customElementsMap[name] = cloneName;
9052
9053					// If it's not marked as inline then add it to valid block elements
9054					if (!inline) {
9055						blockElementsMap[name.toUpperCase()] = {};
9056						blockElementsMap[name] = {};
9057					}
9058
9059					// Add elements clone if needed
9060					if (!elements[name]) {
9061						var customRule = elements[cloneName];
9062
9063						customRule = extend({}, customRule);
9064						delete customRule.removeEmptyAttrs;
9065						delete customRule.removeEmpty;
9066
9067						elements[name] = customRule;
9068					}
9069
9070					// Add custom elements at span/div positions
9071					each(children, function(element) {
9072						if (element[cloneName]) {
9073							element[name] = element[cloneName];
9074						}
9075					});
9076				});
9077			}
9078		}
9079
9080		// Adds valid children to the schema object
9081		function addValidChildren(valid_children) {
9082			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
9083
9084			if (valid_children) {
9085				each(split(valid_children, ','), function(rule) {
9086					var matches = childRuleRegExp.exec(rule), parent, prefix;
9087
9088					if (matches) {
9089						prefix = matches[1];
9090
9091						// Add/remove items from default
9092						if (prefix) {
9093							parent = children[matches[2]];
9094						} else {
9095							parent = children[matches[2]] = {'#comment': {}};
9096						}
9097
9098						parent = children[matches[2]];
9099
9100						each(split(matches[3], '|'), function(child) {
9101							if (prefix === '-') {
9102								delete parent[child];
9103							} else {
9104								parent[child] = {};
9105							}
9106						});
9107					}
9108				});
9109			}
9110		}
9111
9112		function getElementRule(name) {
9113			var element = elements[name], i;
9114
9115			// Exact match found
9116			if (element) {
9117				return element;
9118			}
9119
9120			// No exact match then try the patterns
9121			i = patternElements.length;
9122			while (i--) {
9123				element = patternElements[i];
9124
9125				if (element.pattern.test(name)) {
9126					return element;
9127				}
9128			}
9129		}
9130
9131		if (!settings.valid_elements) {
9132			// No valid elements defined then clone the elements from the schema spec
9133			each(schemaItems, function(element, name) {
9134				elements[name] = {
9135					attributes: element.attributes,
9136					attributesOrder: element.attributesOrder
9137				};
9138
9139				children[name] = element.children;
9140			});
9141
9142			// Switch these on HTML4
9143			if (settings.schema != "html5") {
9144				each(split('strong/b em/i'), function(item) {
9145					item = split(item, '/');
9146					elements[item[1]].outputName = item[0];
9147				});
9148			}
9149
9150			// Add default alt attribute for images
9151			elements.img.attributesDefault = [{name: 'alt', value: ''}];
9152
9153			// Remove these if they are empty by default
9154			each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
9155				if (elements[name]) {
9156					elements[name].removeEmpty = true;
9157				}
9158			});
9159
9160			// Padd these by default
9161			each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
9162				elements[name].paddEmpty = true;
9163			});
9164
9165			// Remove these if they have no attributes
9166			each(split('span'), function(name) {
9167				elements[name].removeEmptyAttrs = true;
9168			});
9169
9170			// Remove these by default
9171			// TODO: Reenable in 4.1
9172			/*each(split('script style'), function(name) {
9173				delete elements[name];
9174			});*/
9175		} else {
9176			setValidElements(settings.valid_elements);
9177		}
9178
9179		addCustomElements(settings.custom_elements);
9180		addValidChildren(settings.valid_children);
9181		addValidElements(settings.extended_valid_elements);
9182
9183		// Todo: Remove this when we fix list handling to be valid
9184		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
9185
9186		// Delete invalid elements
9187		if (settings.invalid_elements) {
9188			each(explode(settings.invalid_elements), function(item) {
9189				if (elements[item]) {
9190					delete elements[item];
9191				}
9192			});
9193		}
9194
9195		// If the user didn't allow span only allow internal spans
9196		if (!getElementRule('span')) {
9197			addValidElements('span[!data-mce-type|*]');
9198		}
9199
9200		/**
9201		 * Name/value map object with valid parents and children to those parents.
9202		 *
9203		 * @example
9204		 * children = {
9205		 *    div:{p:{}, h1:{}}
9206		 * };
9207		 * @field children
9208		 * @type Object
9209		 */
9210		self.children = children;
9211
9212		/**
9213		 * Name/value map object with valid styles for each element.
9214		 *
9215		 * @field styles
9216		 * @type Object
9217		 */
9218		self.styles = validStyles;
9219
9220		/**
9221		 * Returns a map with boolean attributes.
9222		 *
9223		 * @method getBoolAttrs
9224		 * @return {Object} Name/value lookup map for boolean attributes.
9225		 */
9226		self.getBoolAttrs = function() {
9227			return boolAttrMap;
9228		};
9229
9230		/**
9231		 * Returns a map with block elements.
9232		 *
9233		 * @method getBlockElements
9234		 * @return {Object} Name/value lookup map for block elements.
9235		 */
9236		self.getBlockElements = function() {
9237			return blockElementsMap;
9238		};
9239
9240		/**
9241		 * Returns a map with text block elements. Such as: p,h1-h6,div,address
9242		 *
9243		 * @method getTextBlockElements
9244		 * @return {Object} Name/value lookup map for block elements.
9245		 */
9246		self.getTextBlockElements = function() {
9247			return textBlockElementsMap;
9248		};
9249
9250		/**
9251		 * Returns a map with short ended elements such as BR or IMG.
9252		 *
9253		 * @method getShortEndedElements
9254		 * @return {Object} Name/value lookup map for short ended elements.
9255		 */
9256		self.getShortEndedElements = function() {
9257			return shortEndedElementsMap;
9258		};
9259
9260		/**
9261		 * Returns a map with self closing tags such as <li>.
9262		 *
9263		 * @method getSelfClosingElements
9264		 * @return {Object} Name/value lookup map for self closing tags elements.
9265		 */
9266		self.getSelfClosingElements = function() {
9267			return selfClosingElementsMap;
9268		};
9269
9270		/**
9271		 * Returns a map with elements that should be treated as contents regardless if it has text
9272		 * content in them or not such as TD, VIDEO or IMG.
9273		 *
9274		 * @method getNonEmptyElements
9275		 * @return {Object} Name/value lookup map for non empty elements.
9276		 */
9277		self.getNonEmptyElements = function() {
9278			return nonEmptyElementsMap;
9279		};
9280
9281		/**
9282		 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
9283		 *
9284		 * @method getWhiteSpaceElements
9285		 * @return {Object} Name/value lookup map for white space elements.
9286		 */
9287		self.getWhiteSpaceElements = function() {
9288			return whiteSpaceElementsMap;
9289		};
9290
9291		/**
9292		 * Returns a map with special elements. These are elements that needs to be parsed
9293		 * in a special way such as script, style, textarea etc. The map object values
9294		 * are regexps used to find the end of the element.
9295		 *
9296		 * @method getSpecialElements
9297		 * @return {Object} Name/value lookup map for special elements.
9298		 */
9299		self.getSpecialElements = function() {
9300			return specialElements;
9301		};
9302
9303		/**
9304		 * Returns true/false if the specified element and it's child is valid or not
9305		 * according to the schema.
9306		 *
9307		 * @method isValidChild
9308		 * @param {String} name Element name to check for.
9309		 * @param {String} child Element child to verify.
9310		 * @return {Boolean} True/false if the element is a valid child of the specified parent.
9311		 */
9312		self.isValidChild = function(name, child) {
9313			var parent = children[name];
9314
9315			return !!(parent && parent[child]);
9316		};
9317
9318		/**
9319		 * Returns true/false if the specified element name and optional attribute is
9320		 * valid according to the schema.
9321		 *
9322		 * @method isValid
9323		 * @param {String} name Name of element to check.
9324		 * @param {String} attr Optional attribute name to check for.
9325		 * @return {Boolean} True/false if the element and attribute is valid.
9326		 */
9327		self.isValid = function(name, attr) {
9328			var attrPatterns, i, rule = getElementRule(name);
9329
9330			// Check if it's a valid element
9331			if (rule) {
9332				if (attr) {
9333					// Check if attribute name exists
9334					if (rule.attributes[attr]) {
9335						return true;
9336					}
9337
9338					// Check if attribute matches a regexp pattern
9339					attrPatterns = rule.attributePatterns;
9340					if (attrPatterns) {
9341						i = attrPatterns.length;
9342						while (i--) {
9343							if (attrPatterns[i].pattern.test(name)) {
9344								return true;
9345							}
9346						}
9347					}
9348				} else {
9349					return true;
9350				}
9351			}
9352
9353			// No match
9354			return false;
9355		};
9356
9357		/**
9358		 * Returns true/false if the specified element is valid or not
9359		 * according to the schema.
9360		 *
9361		 * @method getElementRule
9362		 * @param {String} name Element name to check for.
9363		 * @return {Object} Element object or undefined if the element isn't valid.
9364		 */
9365		self.getElementRule = getElementRule;
9366
9367		/**
9368		 * Returns an map object of all custom elements.
9369		 *
9370		 * @method getCustomElements
9371		 * @return {Object} Name/value map object of all custom elements.
9372		 */
9373		self.getCustomElements = function() {
9374			return customElementsMap;
9375		};
9376
9377		/**
9378		 * Parses a valid elements string and adds it to the schema. The valid elements
9379		  format is for example "element[attr=default|otherattr]".
9380		 * Existing rules will be replaced with the ones specified, so this extends the schema.
9381		 *
9382		 * @method addValidElements
9383		 * @param {String} valid_elements String in the valid elements format to be parsed.
9384		 */
9385		self.addValidElements = addValidElements;
9386
9387		/**
9388		 * Parses a valid elements string and sets it to the schema. The valid elements
9389		 * format is for example "element[attr=default|otherattr]".
9390		 * Existing rules will be replaced with the ones specified, so this extends the schema.
9391		 *
9392		 * @method setValidElements
9393		 * @param {String} valid_elements String in the valid elements format to be parsed.
9394		 */
9395		self.setValidElements = setValidElements;
9396
9397		/**
9398		 * Adds custom non HTML elements to the schema.
9399		 *
9400		 * @method addCustomElements
9401		 * @param {String} custom_elements Comma separated list of custom elements to add.
9402		 */
9403		self.addCustomElements = addCustomElements;
9404
9405		/**
9406		 * Parses a valid children string and adds them to the schema structure. The valid children
9407		 * format is for example: "element[child1|child2]".
9408		 *
9409		 * @method addValidChildren
9410		 * @param {String} valid_children Valid children elements string to parse
9411		 */
9412		self.addValidChildren = addValidChildren;
9413
9414		self.elements = elements;
9415	};
9416});
9417
9418// Included from: js/tinymce/classes/html/SaxParser.js
9419
9420/**
9421 * SaxParser.js
9422 *
9423 * Copyright, Moxiecode Systems AB
9424 * Released under LGPL License.
9425 *
9426 * License: http://www.tinymce.com/license
9427 * Contributing: http://www.tinymce.com/contributing
9428 */
9429
9430/**
9431 * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
9432 * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
9433 * and attributes that doesn't fit the schema if the validate setting is enabled.
9434 *
9435 * @example
9436 * var parser = new tinymce.html.SaxParser({
9437 *     validate: true,
9438 *
9439 *     comment: function(text) {
9440 *         console.log('Comment:', text);
9441 *     },
9442 *
9443 *     cdata: function(text) {
9444 *         console.log('CDATA:', text);
9445 *     },
9446 *
9447 *     text: function(text, raw) {
9448 *         console.log('Text:', text, 'Raw:', raw);
9449 *     },
9450 *
9451 *     start: function(name, attrs, empty) {
9452 *         console.log('Start:', name, attrs, empty);
9453 *     },
9454 *
9455 *     end: function(name) {
9456 *         console.log('End:', name);
9457 *     },
9458 *
9459 *     pi: function(name, text) {
9460 *         console.log('PI:', name, text);
9461 *     },
9462 *
9463 *     doctype: function(text) {
9464 *         console.log('DocType:', text);
9465 *     }
9466 * }, schema);
9467 * @class tinymce.html.SaxParser
9468 * @version 3.4
9469 */
9470define("tinymce/html/SaxParser", [
9471	"tinymce/html/Schema",
9472	"tinymce/html/Entities",
9473	"tinymce/util/Tools"
9474], function(Schema, Entities, Tools) {
9475	var each = Tools.each;
9476
9477	/**
9478	 * Constructs a new SaxParser instance.
9479	 *
9480	 * @constructor
9481	 * @method SaxParser
9482	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
9483	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
9484	 */
9485	return function(settings, schema) {
9486		var self = this, noop = function() {};
9487
9488		settings = settings || {};
9489		self.schema = schema = schema || new Schema();
9490
9491		if (settings.fix_self_closing !== false) {
9492			settings.fix_self_closing = true;
9493		}
9494
9495		// Add handler functions from settings and setup default handlers
9496		each('comment cdata text start end pi doctype'.split(' '), function(name) {
9497			if (name) {
9498				self[name] = settings[name] || noop;
9499			}
9500		});
9501
9502		/**
9503		 * Parses the specified HTML string and executes the callbacks for each item it finds.
9504		 *
9505		 * @example
9506		 * new SaxParser({...}).parse('<b>text</b>');
9507		 * @method parse
9508		 * @param {String} html Html string to sax parse.
9509		 */
9510		self.parse = function(html) {
9511			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
9512			var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
9513			var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
9514			var attributesRequired, attributesDefault, attributesForced;
9515			var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
9516			var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href');
9517			var scriptUriRegExp = /(java|vb)script:/i;
9518
9519			function processEndTag(name) {
9520				var pos, i;
9521
9522				// Find position of parent of the same type
9523				pos = stack.length;
9524				while (pos--) {
9525					if (stack[pos].name === name) {
9526						break;
9527					}
9528				}
9529
9530				// Found parent
9531				if (pos >= 0) {
9532					// Close all the open elements
9533					for (i = stack.length - 1; i >= pos; i--) {
9534						name = stack[i];
9535
9536						if (name.valid) {
9537							self.end(name.name);
9538						}
9539					}
9540
9541					// Remove the open elements from the stack
9542					stack.length = pos;
9543				}
9544			}
9545
9546			function parseAttribute(match, name, value, val2, val3) {
9547				var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
9548
9549				name = name.toLowerCase();
9550				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
9551
9552				// Validate name and value pass through all data- attributes
9553				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
9554					attrRule = validAttributesMap[name];
9555
9556					// Find rule by pattern matching
9557					if (!attrRule && validAttributePatterns) {
9558						i = validAttributePatterns.length;
9559						while (i--) {
9560							attrRule = validAttributePatterns[i];
9561							if (attrRule.pattern.test(name)) {
9562								break;
9563							}
9564						}
9565
9566						// No rule matched
9567						if (i === -1) {
9568							attrRule = null;
9569						}
9570					}
9571
9572					// No attribute rule found
9573					if (!attrRule) {
9574						return;
9575					}
9576
9577					// Validate value
9578					if (attrRule.validValues && !(value in attrRule.validValues)) {
9579						return;
9580					}
9581				}
9582
9583				// Block any javascript: urls
9584				if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
9585					var uri = value.replace(trimRegExp, '');
9586
9587					try {
9588						// Might throw malformed URI sequence
9589						uri = decodeURIComponent(uri);
9590						if (scriptUriRegExp.test(uri)) {
9591							return;
9592						}
9593					} catch (ex) {
9594						// Fallback to non UTF-8 decoder
9595						uri = unescape(uri);
9596						if (scriptUriRegExp.test(uri)) {
9597							return;
9598						}
9599					}
9600				}
9601
9602				// Add attribute to list and map
9603				attrList.map[name] = value;
9604				attrList.push({
9605					name: name,
9606					value: value
9607				});
9608			}
9609
9610			// Precompile RegExps and map objects
9611			tokenRegExp = new RegExp('<(?:' +
9612				'(?:!--([\\w\\W]*?)-->)|' + // Comment
9613				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
9614				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
9615				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
9616				'(?:\\/([^>]+)>)|' + // End element
9617				'(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
9618			')', 'g');
9619
9620			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
9621
9622			// Setup lookup tables for empty elements and boolean attributes
9623			shortEndedElements = schema.getShortEndedElements();
9624			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
9625			fillAttrsMap = schema.getBoolAttrs();
9626			validate = settings.validate;
9627			removeInternalElements = settings.remove_internals;
9628			fixSelfClosing = settings.fix_self_closing;
9629			specialElements = schema.getSpecialElements();
9630
9631			while ((matches = tokenRegExp.exec(html))) {
9632				// Text
9633				if (index < matches.index) {
9634					self.text(decode(html.substr(index, matches.index - index)));
9635				}
9636
9637				if ((value = matches[6])) { // End element
9638					value = value.toLowerCase();
9639
9640					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
9641					if (value.charAt(0) === ':') {
9642						value = value.substr(1);
9643					}
9644
9645					processEndTag(value);
9646				} else if ((value = matches[7])) { // Start element
9647					value = value.toLowerCase();
9648
9649					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
9650					if (value.charAt(0) === ':') {
9651						value = value.substr(1);
9652					}
9653
9654					isShortEnded = value in shortEndedElements;
9655
9656					// Is self closing tag for example an <li> after an open <li>
9657					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
9658						processEndTag(value);
9659					}
9660
9661					// Validate element
9662					if (!validate || (elementRule = schema.getElementRule(value))) {
9663						isValidElement = true;
9664
9665						// Grab attributes map and patters when validation is enabled
9666						if (validate) {
9667							validAttributesMap = elementRule.attributes;
9668							validAttributePatterns = elementRule.attributePatterns;
9669						}
9670
9671						// Parse attributes
9672						if ((attribsValue = matches[8])) {
9673							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
9674
9675							// If the element has internal attributes then remove it if we are told to do so
9676							if (isInternalElement && removeInternalElements) {
9677								isValidElement = false;
9678							}
9679
9680							attrList = [];
9681							attrList.map = {};
9682
9683							attribsValue.replace(attrRegExp, parseAttribute);
9684						} else {
9685							attrList = [];
9686							attrList.map = {};
9687						}
9688
9689						// Process attributes if validation is enabled
9690						if (validate && !isInternalElement) {
9691							attributesRequired = elementRule.attributesRequired;
9692							attributesDefault = elementRule.attributesDefault;
9693							attributesForced = elementRule.attributesForced;
9694							anyAttributesRequired = elementRule.removeEmptyAttrs;
9695
9696							// Check if any attribute exists
9697							if (anyAttributesRequired && !attrList.length) {
9698								isValidElement = false;
9699							}
9700
9701							// Handle forced attributes
9702							if (attributesForced) {
9703								i = attributesForced.length;
9704								while (i--) {
9705									attr = attributesForced[i];
9706									name = attr.name;
9707									attrValue = attr.value;
9708
9709									if (attrValue === '{$uid}') {
9710										attrValue = 'mce_' + idCount++;
9711									}
9712
9713									attrList.map[name] = attrValue;
9714									attrList.push({name: name, value: attrValue});
9715								}
9716							}
9717
9718							// Handle default attributes
9719							if (attributesDefault) {
9720								i = attributesDefault.length;
9721								while (i--) {
9722									attr = attributesDefault[i];
9723									name = attr.name;
9724
9725									if (!(name in attrList.map)) {
9726										attrValue = attr.value;
9727
9728										if (attrValue === '{$uid}') {
9729											attrValue = 'mce_' + idCount++;
9730										}
9731
9732										attrList.map[name] = attrValue;
9733										attrList.push({name: name, value: attrValue});
9734									}
9735								}
9736							}
9737
9738							// Handle required attributes
9739							if (attributesRequired) {
9740								i = attributesRequired.length;
9741								while (i--) {
9742									if (attributesRequired[i] in attrList.map) {
9743										break;
9744									}
9745								}
9746
9747								// None of the required attributes where found
9748								if (i === -1) {
9749									isValidElement = false;
9750								}
9751							}
9752
9753							// Invalidate element if it's marked as bogus
9754							if (attrList.map['data-mce-bogus']) {
9755								isValidElement = false;
9756							}
9757						}
9758
9759						if (isValidElement) {
9760							self.start(value, attrList, isShortEnded);
9761						}
9762					} else {
9763						isValidElement = false;
9764					}
9765
9766					// Treat script, noscript and style a bit different since they may include code that looks like elements
9767					if ((endRegExp = specialElements[value])) {
9768						endRegExp.lastIndex = index = matches.index + matches[0].length;
9769
9770						if ((matches = endRegExp.exec(html))) {
9771							if (isValidElement) {
9772								text = html.substr(index, matches.index - index);
9773							}
9774
9775							index = matches.index + matches[0].length;
9776						} else {
9777							text = html.substr(index);
9778							index = html.length;
9779						}
9780
9781						if (isValidElement) {
9782							if (text.length > 0) {
9783								self.text(text, true);
9784							}
9785
9786							self.end(value);
9787						}
9788
9789						tokenRegExp.lastIndex = index;
9790						continue;
9791					}
9792
9793					// Push value on to stack
9794					if (!isShortEnded) {
9795						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
9796							stack.push({name: value, valid: isValidElement});
9797						} else if (isValidElement) {
9798							self.end(value);
9799						}
9800					}
9801				} else if ((value = matches[1])) { // Comment
9802					// Padd comment value to avoid browsers from parsing invalid comments as HTML
9803					if (value.charAt(0) === '>') {
9804						value = ' ' + value;
9805					}
9806
9807					if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
9808						value = ' ' + value;
9809					}
9810
9811					self.comment(value);
9812				} else if ((value = matches[2])) { // CDATA
9813					self.cdata(value);
9814				} else if ((value = matches[3])) { // DOCTYPE
9815					self.doctype(value);
9816				} else if ((value = matches[4])) { // PI
9817					self.pi(value, matches[5]);
9818				}
9819
9820				index = matches.index + matches[0].length;
9821			}
9822
9823			// Text
9824			if (index < html.length) {
9825				self.text(decode(html.substr(index)));
9826			}
9827
9828			// Close any open elements
9829			for (i = stack.length - 1; i >= 0; i--) {
9830				value = stack[i];
9831
9832				if (value.valid) {
9833					self.end(value.name);
9834				}
9835			}
9836		};
9837	};
9838});
9839
9840// Included from: js/tinymce/classes/html/DomParser.js
9841
9842/**
9843 * DomParser.js
9844 *
9845 * Copyright, Moxiecode Systems AB
9846 * Released under LGPL License.
9847 *
9848 * License: http://www.tinymce.com/license
9849 * Contributing: http://www.tinymce.com/contributing
9850 */
9851
9852/**
9853 * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
9854 * sure that the node tree is valid according to the specified schema.
9855 * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
9856 *
9857 * @example
9858 * var parser = new tinymce.html.DomParser({validate: true}, schema);
9859 * var rootNode = parser.parse('<h1>content</h1>');
9860 *
9861 * @class tinymce.html.DomParser
9862 * @version 3.4
9863 */
9864define("tinymce/html/DomParser", [
9865	"tinymce/html/Node",
9866	"tinymce/html/Schema",
9867	"tinymce/html/SaxParser",
9868	"tinymce/util/Tools"
9869], function(Node, Schema, SaxParser, Tools) {
9870	var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
9871
9872	/**
9873	 * Constructs a new DomParser instance.
9874	 *
9875	 * @constructor
9876	 * @method DomParser
9877	 * @param {Object}
9877 settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
9878	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
9879	 */
9880	return function(settings, schema) {
9881		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
9882
9883		settings = settings || {};
9884		settings.validate = "validate" in settings ? settings.validate : true;
9885		settings.root_name = settings.root_name || 'body';
9886		self.schema = schema = schema || new Schema();
9887
9888		function fixInvalidChildren(nodes) {
9889			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
9890			var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
9891
9892			nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
9893			nonEmptyElements = schema.getNonEmptyElements();
9894			textBlockElements = schema.getTextBlockElements();
9895
9896			for (ni = 0; ni < nodes.length; ni++) {
9897				node = nodes[ni];
9898
9899				// Already removed or fixed
9900				if (!node.parent || node.fixed) {
9901					continue;
9902				}
9903
9904				// If the invalid element is a text block and the text block is within a parent LI element
9905				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
9906				if (textBlockElements[node.name] && node.parent.name == 'li') {
9907					// Move sibling text blocks after LI element
9908					sibling = node.next;
9909					while (sibling) {
9910						if (textBlockElements[sibling.name]) {
9911							sibling.name = 'li';
9912							sibling.fixed = true;
9913							node.parent.insert(sibling, node.parent);
9914						} else {
9915							break;
9916						}
9917
9918						sibling = sibling.next;
9919					}
9920
9921					// Unwrap current text block
9922					node.unwrap(node);
9923					continue;
9924				}
9925
9926				// Get list of all parent nodes until we find a valid parent to stick the child into
9927				parents = [node];
9928				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
9929					!nonSplitableElements[parent.name]; parent = parent.parent) {
9930					parents.push(parent);
9931				}
9932
9933				// Found a suitable parent
9934				if (parent && parents.length > 1) {
9935					// Reverse the array since it makes looping easier
9936					parents.reverse();
9937
9938					// Clone the related parent and insert that after the moved node
9939					newParent = currentNode = self.filterNode(parents[0].clone());
9940
9941					// Start cloning and moving children on the left side of the target node
9942					for (i = 0; i < parents.length - 1; i++) {
9943						if (schema.isValidChild(currentNode.name, parents[i].name)) {
9944							tempNode = self.filterNode(parents[i].clone());
9945							currentNode.append(tempNode);
9946						} else {
9947							tempNode = currentNode;
9948						}
9949
9950						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {
9951							nextNode = childNode.next;
9952							tempNode.append(childNode);
9953							childNode = nextNode;
9954						}
9955
9956						currentNode = tempNode;
9957					}
9958
9959					if (!newParent.isEmpty(nonEmptyElements)) {
9960						parent.insert(newParent, parents[0], true);
9961						parent.insert(node, newParent);
9962					} else {
9963						parent.insert(node, parents[0], true);
9964					}
9965
9966					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
9967					parent = parents[0];
9968					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
9969						parent.empty().remove();
9970					}
9971				} else if (node.parent) {
9972					// If it's an LI try to find a UL/OL for it or wrap it
9973					if (node.name === 'li') {
9974						sibling = node.prev;
9975						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
9976							sibling.append(node);
9977							continue;
9978						}
9979
9980						sibling = node.next;
9981						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
9982							sibling.insert(node, sibling.firstChild, true);
9983							continue;
9984						}
9985
9986						node.wrap(self.filterNode(new Node('ul', 1)));
9987						continue;
9988					}
9989
9990					// Try wrapping the element in a DIV
9991					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
9992						node.wrap(self.filterNode(new Node('div', 1)));
9993					} else {
9994						// We failed wrapping it, then remove or unwrap it
9995						if (node.name === 'style' || node.name === 'script') {
9996							node.empty().remove();
9997						} else {
9998							node.unwrap();
9999						}
10000					}
10001				}
10002			}
10003		}
10004
10005		/**
10006		 * Runs the specified node though the element and attributes filters.
10007		 *
10008		 * @method filterNode
10009		 * @param {tinymce.html.Node} Node the node to run filters on.
10010		 * @return {tinymce.html.Node} The passed in node.
10011		 */
10012		self.filterNode = function(node) {
10013			var i, name, list;
10014
10015			// Run element filters
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			// Run attribute filters
10027			i = attributeFilters.length;
10028			while (i--) {
10029				name = attributeFilters[i].name;
10030
10031				if (name in node.attributes.map) {
10032					list = matchedAttributes[name];
10033
10034					if (list) {
10035						list.push(node);
10036					} else {
10037						matchedAttributes[name] = [node];
10038					}
10039				}
10040			}
10041
10042			return node;
10043		};
10044
10045		/**
10046		 * Adds a node filter function to the parser, the parser will collect the specified nodes by name
10047		 * and then execute the callback ones it has finished parsing the document.
10048		 *
10049		 * @example
10050		 * parser.addNodeFilter('p,h1', function(nodes, name) {
10051		 *		for (var i = 0; i < nodes.length; i++) {
10052		 *			console.log(nodes[i].name);
10053		 *		}
10054		 * });
10055		 * @method addNodeFilter
10056		 * @method {String} name Comma separated list of nodes to collect.
10057		 * @param {function} callback Callback function to execute once it has collected nodes.
10058		 */
10059		self.addNodeFilter = function(name, callback) {
10060			each(explode(name), function(name) {
10061				var list = nodeFilters[name];
10062
10063				if (!list) {
10064					nodeFilters[name] = list = [];
10065				}
10066
10067				list.push(callback);
10068			});
10069		};
10070
10071		/**
10072		 * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
10073		 * and then execute the callback ones it has finished parsing the document.
10074		 *
10075		 * @example
10076		 * parser.addAttributeFilter('src,href', function(nodes, name) {
10077		 *		for (var i = 0; i < nodes.length; i++) {
10078		 *			console.log(nodes[i].name);
10079		 *		}
10080		 * });
10081		 * @method addAttributeFilter
10082		 * @method {String} name Comma separated list of nodes to collect.
10083		 * @param {function} callback Callback function to execute once it has collected nodes.
10084		 */
10085		self.addAttributeFilter = function(name, callback) {
10086			each(explode(name), function(name) {
10087				var i;
10088
10089				for (i = 0; i < attributeFilters.length; i++) {
10090					if (attributeFilters[i].name === name) {
10091						attributeFilters[i].callbacks.push(callback);
10092						return;
10093					}
10094				}
10095
10096				attributeFilters.push({name: name, callbacks: [callback]});
10097			});
10098		};
10099
10100		/**
10101		 * Parses the specified HTML string into a DOM like node tree and returns the result.
10102		 *
10103		 * @example
10104		 * var rootNode = new DomParser({...}).parse('<b>text</b>');
10105		 * @method parse
10106		 * @param {String} html Html string to sax parse.
10107		 * @param {Object} args Optional args object that gets passed to all filter functions.
10108		 * @return {tinymce.html.Node} Root node containing the tree.
10109		 */
10110		self.parse = function(html, args) {
10111			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
10112			var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
10113			var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
10114			var children, nonEmptyElements, rootBlockName;
10115
10116			args = args || {};
10117			matchedNodes = {};
10118			matchedAttributes = {};
10119			blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
10120			nonEmptyElements = schema.getNonEmptyElements();
10121			children = schema.children;
10122			validate = settings.validate;
10123			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
10124
10125			whiteSpaceElements = schema.getWhiteSpaceElements();
10126			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
10127			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
10128			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
10129			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
10130
10131			function addRootBlocks() {
10132				var node = rootNode.firstChild, next, rootBlockNode;
10133
10134				// Removes whitespace at beginning and end of block so:
10135				// <p> x </p> -> <p>x</p>
10136				function trim(rootBlockNode) {
10137					if (rootBlockNode) {
10138						node = rootBlockNode.firstChild;
10139						if (node && node.type == 3) {
10140							node.value = node.value.replace(startWhiteSpaceRegExp, '');
10141						}
10142
10143						node = rootBlockNode.lastChild;
10144						if (node && node.type == 3) {
10145							node.value = node.value.replace(endWhiteSpaceRegExp, '');
10146						}
10147					}
10148				}
10149
10150				// Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
10151				if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
10152					return;
10153				}
10154
10155				while (node) {
10156					next = node.next;
10157
10158					if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
10159						!blockElements[node.name] && !node.attr('data-mce-type'))) {
10160						if (!rootBlockNode) {
10161							// Create a new root block element
10162							rootBlockNode = createNode(rootBlockName, 1);
10163							rootBlockNode.attr(settings.forced_root_block_attrs);
10164							rootNode.insert(rootBlockNode, node);
10165							rootBlockNode.append(node);
10166						} else {
10167							rootBlockNode.append(node);
10168						}
10169					} else {
10170						trim(rootBlockNode);
10171						rootBlockNode = null;
10172					}
10173
10174					node = next;
10175				}
10176
10177				trim(rootBlockNode);
10178			}
10179
10180			function createNode(name, type) {
10181				var node = new Node(name, type), list;
10182
10183				if (name in nodeFilters) {
10184					list = matchedNodes[name];
10185
10186					if (list) {
10187						list.push(node);
10188					} else {
10189						matchedNodes[name] = [node];
10190					}
10191				}
10192
10193				return node;
10194			}
10195
10196			function removeWhitespaceBefore(node) {
10197				var textNode, textVal, sibling;
10198
10199				for (textNode = node.prev; textNode && textNode.type === 3; ) {
10200					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
10201
10202					if (textVal.length > 0) {
10203						textNode.value = textVal;
10204						textNode = textNode.prev;
10205					} else {
10206						sibling = textNode.prev;
10207						textNode.remove();
10208						textNode = sibling;
10209					}
10210				}
10211			}
10212
10213			function cloneAndExcludeBlocks(input) {
10214				var name, output = {};
10215
10216				for (name in input) {
10217					if (name !== 'li' && name != 'p') {
10218						output[name] = input[name];
10219					}
10220				}
10221
10222				return output;
10223			}
10224
10225			parser = new SaxParser({
10226				validate: validate,
10227				allow_script_urls: settings.allow_script_urls,
10228				allow_conditional_comments: settings.allow_conditional_comments,
10229
10230				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
10231				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
10232
10233				cdata: function(text) {
10234					node.append(createNode('#cdata', 4)).value = text;
10235				},
10236
10237				text: function(text, raw) {
10238					var textNode;
10239
10240					// Trim all redundant whitespace on non white space elements
10241					if (!isInWhiteSpacePreservedElement) {
10242						text = text.replace(allWhiteSpaceRegExp, ' ');
10243
10244						if (node.lastChild && blockElements[node.lastChild.name]) {
10245							text = text.replace(startWhiteSpaceRegExp, '');
10246						}
10247					}
10248
10249					// Do we need to create the node
10250					if (text.length !== 0) {
10251						textNode = createNode('#text', 3);
10252						textNode.raw = !!raw;
10253						node.append(textNode).value = text;
10254					}
10255				},
10256
10257				comment: function(text) {
10258					node.append(createNode('#comment', 8)).value = text;
10259				},
10260
10261				pi: function(name, text) {
10262					node.append(createNode(name, 7)).value = text;
10263					removeWhitespaceBefore(node);
10264				},
10265
10266				doctype: function(text) {
10267					var newNode;
10268
10269					newNode = node.append(createNode('#doctype', 10));
10270					newNode.value = text;
10271					removeWhitespaceBefore(node);
10272				},
10273
10274				start: function(name, attrs, empty) {
10275					var newNode, attrFiltersLen, elementRule, attrName, parent;
10276
10277					elementRule = validate ? schema.getElementRule(name) : {};
10278					if (elementRule) {
10279						newNode = createNode(elementRule.outputName || name, 1);
10280						newNode.attributes = attrs;
10281						newNode.shortEnded = empty;
10282
10283						node.append(newNode);
10284
10285						// Check if node is valid child of the parent node is the child is
10286						// unknown we don't collect it since it's probably a custom element
10287						parent = children[node.name];
10288						if (parent && children[newNode.name] && !parent[newNode.name]) {
10289							invalidChildren.push(newNode);
10290						}
10291
10292						attrFiltersLen = attributeFilters.length;
10293						while (attrFiltersLen--) {
10294							attrName = attributeFilters[attrFiltersLen].name;
10295
10296							if (attrName in attrs.map) {
10297								list = matchedAttributes[attrName];
10298
10299								if (list) {
10300									list.push(newNode);
10301								} else {
10302									matchedAttributes[attrName] = [newNode];
10303								}
10304							}
10305						}
10306
10307						// Trim whitespace before block
10308						if (blockElements[name]) {
10309							removeWhitespaceBefore(newNode);
10310						}
10311
10312						// Change current node if the element wasn't empty i.e not <br /> or <img />
10313						if (!empty) {
10314							node = newNode;
10315						}
10316
10317						// Check if we are inside a whitespace preserved element
10318						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
10319							isInWhiteSpacePreservedElement = true;
10320						}
10321					}
10322				},
10323
10324				end: function(name) {
10325					var textNode, elementRule, text, sibling, tempNode;
10326
10327					elementRule = validate ? schema.getElementRule(name) : {};
10328					if (elementRule) {
10329						if (blockElements[name]) {
10330							if (!isInWhiteSpacePreservedElement) {
10331								// Trim whitespace of the first node in a block
10332								textNode = node.firstChild;
10333								if (textNode && textNode.type === 3) {
10334									text = textNode.value.replace(startWhiteSpaceRegExp, '');
10335
10336									// Any characters left after trim or should we remove it
10337									if (text.length > 0) {
10338										textNode.value = text;
10339										textNode = textNode.next;
10340									} else {
10341										sibling = textNode.next;
10342										textNode.remove();
10343										textNode = sibling;
10344
10345										// Remove any pure whitespace siblings
10346										while (textNode && textNode.type === 3) {
10347											text = textNode.value;
10348											sibling = textNode.next;
10349
10350											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
10351												textNode.remove();
10352												textNode = sibling;
10353											}
10354
10355											textNode = sibling;
10356										}
10357									}
10358								}
10359
10360								// Trim whitespace of the last node in a block
10361								textNode = node.lastChild;
10362								if (textNode && textNode.type === 3) {
10363									text = textNode.value.replace(endWhiteSpaceRegExp, '');
10364
10365									// Any characters left after trim or should we remove it
10366									if (text.length > 0) {
10367										textNode.value = text;
10368										textNode = textNode.prev;
10369									} else {
10370										sibling = textNode.prev;
10371										textNode.remove();
10372										textNode = sibling;
10373
10374										// Remove any pure whitespace siblings
10375										while (textNode && textNode.type === 3) {
10376											text = textNode.value;
10377											sibling = textNode.prev;
10378
10379											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
10380												textNode.remove();
10381												textNode = sibling;
10382											}
10383
10384											textNode = sibling;
10385										}
10386									}
10387								}
10388							}
10389
10390							// Trim start white space
10391							// Removed due to: #5424
10392							/*textNode = node.prev;
10393							if (textNode && textNode.type === 3) {
10394								text = textNode.value.replace(startWhiteSpaceRegExp, '');
10395
10396								if (text.length > 0)
10397									textNode.value = text;
10398								else
10399									textNode.remove();
10400							}*/
10401						}
10402
10403						// Check if we exited a whitespace preserved element
10404						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
10405							isInWhiteSpacePreservedElement = false;
10406						}
10407
10408						// Handle empty nodes
10409						if (elementRule.removeEmpty || elementRule.paddEmpty) {
10410							if (node.isEmpty(nonEmptyElements)) {
10411								if (elementRule.paddEmpty) {
10412									node.empty().append(new Node('#text', '3')).value = '\u00a0';
10413								} else {
10414									// Leave nodes that have a name like <a name="name">
10415									if (!node.attributes.map.name && !node.attributes.map.id) {
10416										tempNode = node.parent;
10417										node.empty().remove();
10418										node = tempNode;
10419										return;
10420									}
10421								}
10422							}
10423						}
10424
10425						node = node.parent;
10426					}
10427				}
10428			}, schema);
10429
10430			rootNode = node = new Node(args.context || settings.root_name, 11);
10431
10432			parser.parse(html);
10433
10434			// Fix invalid children or report invalid children in a contextual parsing
10435			if (validate && invalidChildren.length) {
10436				if (!args.context) {
10437					fixInvalidChildren(invalidChildren);
10438				} else {
10439					args.invalid = true;
10440				}
10441			}
10442
10443			// Wrap nodes in the root into block elements if the root is body
10444			if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
10445				addRootBlocks();
10446			}
10447
10448			// Run filters only when the contents is valid
10449			if (!args.invalid) {
10450				// Run node filters
10451				for (name in matchedNodes) {
10452					list = nodeFilters[name];
10453					nodes = matchedNodes[name];
10454
10455					// Remove already removed children
10456					fi = nodes.length;
10457					while (fi--) {
10458						if (!nodes[fi].parent) {
10459							nodes.splice(fi, 1);
10460						}
10461					}
10462
10463					for (i = 0, l = list.length; i < l; i++) {
10464						list[i](nodes, name, args);
10465					}
10466				}
10467
10468				// Run attribute filters
10469				for (i = 0, l = attributeFilters.length; i < l; i++) {
10470					list = attributeFilters[i];
10471
10472					if (list.name in matchedAttributes) {
10473						nodes = matchedAttributes[list.name];
10474
10475						// Remove already removed children
10476						fi = nodes.length;
10477						while (fi--) {
10478							if (!nodes[fi].parent) {
10479								nodes.splice(fi, 1);
10480							}
10481						}
10482
10483						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
10484							list.callbacks[fi](nodes, list.name, args);
10485						}
10486					}
10487				}
10488			}
10489
10490			return rootNode;
10491		};
10492
10493		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
10494		// make it possible to place the caret inside empty blocks. This logic tries to remove
10495		// these elements and keep br elements that where intended to be there intact
10496		if (settings.remove_trailing_brs) {
10497			self.addNodeFilter('br', function(nodes) {
10498				var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
10499				var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
10500				var elementRule, textNode;
10501
10502				// Remove brs from body element as well
10503				blockElements.body = 1;
10504
10505				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
10506				for (i = 0; i < l; i++) {
10507					node = nodes[i];
10508					parent = node.parent;
10509
10510					if (blockElements[node.parent.name] && node === parent.lastChild) {
10511						// Loop all nodes to the left of the current node and check for other BR elements
10512						// excluding bookmarks since they are invisible
10513						prev = node.prev;
10514						while (prev) {
10515							prevName = prev.name;
10516
10517							// Ignore bookmarks
10518							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
10519								// Found a non BR element
10520								if (prevName !== "br") {
10521									break;
10522								}
10523
10524								// Found another br it's a <br><br> structure then don't remove anything
10525								if (prevName === 'br') {
10526									node = null;
10527									break;
10528								}
10529							}
10530
10531							prev = prev.prev;
10532						}
10533
10534						if (node) {
10535							node.remove();
10536
10537							// Is the parent to be considered empty after we removed the BR
10538							if (parent.isEmpty(nonEmptyElements)) {
10539								elementRule = schema.getElementRule(parent.name);
10540
10541								// Remove or padd the element depending on schema rule
10542								if (elementRule) {
10543									if (elementRule.removeEmpty) {
10544										parent.remove();
10545									} else if (elementRule.paddEmpty) {
10546										parent.empty().append(new Node('#text', 3)).value = '\u00a0';
10547									}
10548								}
10549							}
10550						}
10551					} else {
10552						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
10553						// so they become <p><b><i>&nbsp;</i></b></p>
10554						lastParent = node;
10555						while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
10556							lastParent = parent;
10557
10558							if (blockElements[parent.name]) {
10559								break;
10560							}
10561
10562							parent = parent.parent;
10563						}
10564
10565						if (lastParent === parent) {
10566							textNode = new Node('#text', 3);
10567							textNode.value = '\u00a0';
10568							node.replace(textNode);
10569						}
10570					}
10571				}
10572			});
10573		}
10574
10575		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
10576		if (!settings.allow_html_in_named_anchor) {
10577			self.addAttributeFilter('id,name', function(nodes) {
10578				var i = nodes.length, sibling, prevSibling, parent, node;
10579
10580				while (i--) {
10581					node = nodes[i];
10582					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
10583						parent = node.parent;
10584
10585						// Move children after current node
10586						sibling = node.lastChild;
10587						do {
10588							prevSibling = sibling.prev;
10589							parent.insert(sibling, node);
10590							sibling = prevSibling;
10591						} while (sibling);
10592					}
10593				}
10594			});
10595		}
10596	};
10597});
10598
10599// Included from: js/tinymce/classes/html/Writer.js
10600
10601/**
10602 * Writer.js
10603 *
10604 * Copyright, Moxiecode Systems AB
10605 * Released under LGPL License.
10606 *
10607 * License: http://www.tinymce.com/license
10608 * Contributing: http://www.tinymce.com/contributing
10609 */
10610
10611/**
10612 * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
10613 *
10614 * @class tinymce.html.Writer
10615 * @example
10616 * var writer = new tinymce.html.Writer({indent: true});
10617 * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
10618 * console.log(writer.getContent());
10619 *
10620 * @class tinymce.html.Writer
10621 * @version 3.4
10622 */
10623define("tinymce/html/Writer", [
10624	"tinymce/html/Entities",
10625	"tinymce/util/Tools"
10626], function(Entities, Tools) {
10627	var makeMap = Tools.makeMap;
10628
10629	/**
10630	 * Constructs a new Writer instance.
10631	 *
10632	 * @constructor
10633	 * @method Writer
10634	 * @param {Object} settings Name/value settings object.
10635	 */
10636	return function(settings) {
10637		var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
10638
10639		settings = settings || {};
10640		indent = settings.indent;
10641		indentBefore = makeMap(settings.indent_before || '');
10642		indentAfter = makeMap(settings.indent_after || '');
10643		encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
10644		htmlOutput = settings.element_format == "html";
10645
10646		return {
10647			/**
10648			 * Writes the a start element such as <p id="a">.
10649			 *
10650			 * @method start
10651			 * @param {String} name Name of the element.
10652			 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
10653			 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
10654			 */
10655			start: function(name, attrs, empty) {
10656				var i, l, attr, value;
10657
10658				if (indent && indentBefore[name] && html.length > 0) {
10659					value = html[html.length - 1];
10660
10661					if (value.length > 0 && value !== '\n') {
10662						html.push('\n');
10663					}
10664				}
10665
10666				html.push('<', name);
10667
10668				if (attrs) {
10669					for (i = 0, l = attrs.length; i < l; i++) {
10670						attr = attrs[i];
10671						html.push(' ', attr.name, '="', encode(attr.value, true), '"');
10672					}
10673				}
10674
10675				if (!empty || htmlOutput) {
10676					html[html.length] = '>';
10677				} else {
10678					html[html.length] = ' />';
10679				}
10680
10681				if (empty && indent && indentAfter[name] && html.length > 0) {
10682					value = html[html.length - 1];
10683
10684					if (value.length > 0 && value !== '\n') {
10685						html.push('\n');
10686					}
10687				}
10688			},
10689
10690			/**
10691			 * Writes the a end element such as </p>.
10692			 *
10693			 * @method end
10694			 * @param {String} name Name of the element.
10695			 */
10696			end: function(name) {
10697				var value;
10698
10699				/*if (indent && indentBefore[name] && html.length > 0) {
10700					value = html[html.length - 1];
10701
10702					if (value.length > 0 && value !== '\n')
10703						html.push('\n');
10704				}*/
10705
10706				html.push('</', name, '>');
10707
10708				if (indent && indentAfter[name] && html.length > 0) {
10709					value = html[html.length - 1];
10710
10711					if (value.length > 0 && value !== '\n') {
10712						html.push('\n');
10713					}
10714				}
10715			},
10716
10717			/**
10718			 * Writes a text node.
10719			 *
10720			 * @method text
10721			 * @param {String} text String to write out.
10722			 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
10723			 */
10724			text: function(text, raw) {
10725				if (text.length > 0) {
10726					html[html.length] = raw ? text : encode(text);
10727				}
10728			},
10729
10730			/**
10731			 * Writes a cdata node such as <![CDATA[data]]>.
10732			 *
10733			 * @method cdata
10734			 * @param {String} text String to write out inside the cdata.
10735			 */
10736			cdata: function(text) {
10737				html.push('<![CDATA[', text, ']]>');
10738			},
10739
10740			/**
10741			 * Writes a comment node such as <!-- Comment -->.
10742			 *
10743			 * @method cdata
10744			 * @param {String} text String to write out inside the comment.
10745			 */
10746			comment: function(text) {
10747				html.push('<!--', text, '-->');
10748			},
10749
10750			/**
10751			 * Writes a PI node such as <?xml attr="value" ?>.
10752			 *
10753			 * @method pi
10754			 * @param {String} name Name of the pi.
10755			 * @param {String} text String to write out inside the pi.
10756			 */
10757			pi: function(name, text) {
10758				if (text) {
10759					html.push('<?', name, ' ', text, '?>');
10760				} else {
10761					html.push('<?', name, '?>');
10762				}
10763
10764				if (indent) {
10765					html.push('\n');
10766				}
10767			},
10768
10769			/**
10770			 * Writes a doctype node such as <!DOCTYPE data>.
10771			 *
10772			 * @method doctype
10773			 * @param {String} text String to write out inside the doctype.
10774			 */
10775			doctype: function(text) {
10776				html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
10777			},
10778
10779			/**
10780			 * Resets the internal buffer if one wants to reuse the writer.
10781			 *
10782			 * @method reset
10783			 */
10784			reset: function() {
10785				html.length = 0;
10786			},
10787
10788			/**
10789			 * Returns the contents that got serialized.
10790			 *
10791			 * @method getContent
10792			 * @return {String} HTML contents that got written down.
10793			 */
10794			getContent: function() {
10795				return html.join('').replace(/\n$/, '');
10796			}
10797		};
10798	};
10799});
10800
10801// Included from: js/tinymce/classes/html/Serializer.js
10802
10803/**
10804 * Serializer.js
10805 *
10806 * Copyright, Moxiecode Systems AB
10807 * Released under LGPL License.
10808 *
10809 * License: http://www.tinymce.com/license
10810 * Contributing: http://www.tinymce.com/contributing
10811 */
10812
10813/**
10814 * This class is used to serialize down the DOM tree into a string using a Writer instance.
10815 *
10816 *
10817 * @example
10818 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
10819 * @class tinymce.html.Serializer
10820 * @version 3.4
10821 */
10822define("tinymce/html/Serializer", [
10823	"tinymce/html/Writer",
10824	"tinymce/html/Schema"
10825], function(Writer, Schema) {
10826	/**
10827	 * Constructs a new Serializer instance.
10828	 *
10829	 * @constructor
10830	 * @method Serializer
10831	 * @param {Object} settings Name/value settings object.
10832	 * @param {tinymce.html.Schema} schema Schema instance to use.
10833	 */
10834	return function(settings, schema) {
10835		var self = this, writer = new Writer(settings);
10836
10837		settings = settings || {};
10838		settings.validate = "validate" in settings ? settings.validate : true;
10839
10840		self.schema = schema = schema || new Schema();
10841		self.writer = writer;
10842
10843		/**
10844		 * Serializes the specified node into a string.
10845		 *
10846		 * @example
10847		 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
10848		 * @method serialize
10849		 * @param {tinymce.html.Node} node Node instance to serialize.
10850		 * @return {String} String with HTML based on DOM tree.
10851		 */
10852		self.serialize = function(node) {
10853			var handlers, validate;
10854
10855			validate = settings.validate;
10856
10857			handlers = {
10858				// #text
10859				3: function(node) {
10860					writer.text(node.value, node.raw);
10861				},
10862
10863				// #comment
10864				8: function(node) {
10865					writer.comment(node.value);
10866				},
10867
10868				// Processing instruction
10869				7: function(node) {
10870					writer.pi(node.name, node.value);
10871				},
10872
10873				// Doctype
10874				10: function(node) {
10875					writer.doctype(node.value);
10876				},
10877
10878				// CDATA
10879				4: function(node) {
10880					writer.cdata(node.value);
10881				},
10882
10883				// Document fragment
10884				11: function(node) {
10885					if ((node = node.firstChild)) {
10886						do {
10887							walk(node);
10888						} while ((node = node.next));
10889					}
10890				}
10891			};
10892
10893			writer.reset();
10894
10895			function walk(node) {
10896				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
10897
10898				if (!handler) {
10899					name = node.name;
10900					isEmpty = node.shortEnded;
10901					attrs = node.attributes;
10902
10903					// Sort attributes
10904					if (validate && attrs && attrs.length > 1) {
10905						sortedAttrs = [];
10906						sortedAttrs.map = {};
10907
10908						elementRule = schema.getElementRule(node.name);
10909						for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
10910							attrName = elementRule.attributesOrder[i];
10911
10912							if (attrName in attrs.map) {
10913								attrValue = attrs.map[attrName];
10914								sortedAttrs.map[attrName] = attrValue;
10915								sortedAttrs.push({name: attrName, value: attrValue});
10916							}
10917						}
10918
10919						for (i = 0, l = attrs.length; i < l; i++) {
10920							attrName = attrs[i].name;
10921
10922							if (!(attrName in sortedAttrs.map)) {
10923								attrValue = attrs.map[attrName];
10924								sortedAttrs.map[attrName] = attrValue;
10925								sortedAttrs.push({name: attrName, value: attrValue});
10926							}
10927						}
10928
10929						attrs = sortedAttrs;
10930					}
10931
10932					writer.start(node.name, attrs, isEmpty);
10933
10934					if (!isEmpty) {
10935						if ((node = node.firstChild)) {
10936							do {
10937								walk(node);
10938							} while ((node = node.next));
10939						}
10940
10941						writer.end(name);
10942					}
10943				} else {
10944					handler(node);
10945				}
10946			}
10947
10948			// Serialize element and treat all non elements as fragments
10949			if (node.type == 1 && !settings.inner) {
10950				walk(node);
10951			} else {
10952				handlers[11](node);
10953			}
10954
10955			return writer.getContent();
10956		};
10957	};
10958});
10959
10960// Included from: js/tinymce/classes/dom/Serializer.js
10961
10962/**
10963 * Serializer.js
10964 *
10965 * Copyright, Moxiecode Systems AB
10966 * Released under LGPL License.
10967 *
10968 * License: http://www.tinymce.com/license
10969 * Contributing: http://www.tinymce.com/contributing
10970 */
10971
10972/**
10973 * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
10974 * more details and examples on how to use this class.
10975 *
10976 * @class tinymce.dom.Serializer
10977 */
10978define("tinymce/dom/Serializer", [
10979	"tinymce/dom/DOMUtils",
10980	"tinymce/html/DomParser",
10981	"tinymce/html/Entities",
10982	"tinymce/html/Serializer",
10983	"tinymce/html/Node",
10984	"tinymce/html/Schema",
10985	"tinymce/Env",
10986	"tinymce/util/Tools"
10987], function(DOMUtils, DomParser, Entities, Serializer, Node, Schema, Env, Tools) {
10988	var each = Tools.each, trim = Tools.trim;
10989	var DOM = DOMUtils.DOM;
10990
10991	/**
10992	 * Constructs a new DOM serializer class.
10993	 *
10994	 * @constructor
10995	 * @method Serializer
10996	 * @param {Object} settings Serializer settings object.
10997	 * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
10998	 */
10999	return function(settings, editor) {
11000		var dom, schema, htmlParser;
11001
11002		if (editor) {
11003			dom = editor.dom;
11004			schema = editor.schema;
11005		}
11006
11007		// Default DOM and Schema if they are undefined
11008		dom = dom || DOM;
11009		schema = schema || new Schema(settings);
11010		settings.entity_encoding = settings.entity_encoding || 'named';
11011		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
11012
11013		htmlParser = new DomParser(settings, schema);
11014
11015		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
11016		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
11017			var i = nodes.length, node, value, internalName = 'data-mce-' + name;
11018			var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
11019
11020			while (i--) {
11021				node = nodes[i];
11022
11023				value = node.attributes.map[internalName];
11024				if (value !== undef) {
11025					// Set external name to internal value and remove internal
11026					node.attr(name, value.length > 0 ? value : null);
11027					node.attr(internalName, null);
11028				} else {
11029					// No internal attribute found then convert the value we have in the DOM
11030					value = node.attributes.map[name];
11031
11032					if (name === "style") {
11033						value = dom.serializeStyle(dom.parseStyle(value), node.name);
11034					} else if (urlConverter) {
11035						value = urlConverter.call(urlConverterScope, value, name, node.name);
11036					}
11037
11038					node.attr(name, value.length > 0 ? value : null);
11039				}
11040			}
11041		});
11042
11043		// Remove internal classes mceItem<..> or mceSelected
11044		htmlParser.addAttributeFilter('class', function(nodes) {
11045			var i = nodes.length, node, value;
11046
11047			while (i--) {
11048				node = nodes[i];
11049				value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
11050				node.attr('class', value.length > 0 ? value : null);
11051			}
11052		});
11053
11054		// Remove bookmark elements
11055		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
11056			var i = nodes.length, node;
11057
11058			while (i--) {
11059				node = nodes[i];
11060
11061				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
11062					node.remove();
11063				}
11064			}
11065		});
11066
11067		// Remove expando attributes
11068		htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name) {
11069			var i = nodes.length;
11070
11071			while (i--) {
11072				nodes[i].attr(name, null);
11073			}
11074		});
11075
11076		htmlParser.addNodeFilter('noscript', function(nodes) {
11077			var i = nodes.length, node;
11078
11079			while (i--) {
11080				node = nodes[i].firstChild;
11081
11082				if (node) {
11083					node.value = Entities.decode(node.value);
11084				}
11085			}
11086		});
11087
11088		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
11089		htmlParser.addNodeFilter('script,style', function(nodes, name) {
11090			var i = nodes.length, node, value;
11091
11092			function trim(value) {
11093				/*jshint maxlen:255 */
11094				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
11095						.replace(/^[\r\n]*|[\r\n]*$/g, '')
11096						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
11097						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
11098			}
11099
11100			while (i--) {
11101				node = nodes[i];
11102				value = node.firstChild ? node.firstChild.value : '';
11103
11104				if (name === "script") {
11105					// Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
11106					var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
11107					node.attr('type', type === 'text/javascript' ? null : type);
11108
11109					if (value.length > 0) {
11110						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
11111					}
11112				} else {
11113					if (value.length > 0) {
11114						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
11115					}
11116				}
11117			}
11118		});
11119
11120		// Convert comments to cdata and handle protected comments
11121		htmlParser.addNodeFilter('#comment', function(nodes) {
11122			var i = nodes.length, node;
11123
11124			while (i--) {
11125				node = nodes[i];
11126
11127				if (node.value.indexOf('[CDATA[') === 0) {
11128					node.name = '#cdata';
11129					node.type = 4;
11130					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
11131				} else if (node.value.indexOf('mce:protected ') === 0) {
11132					node.name = "#text";
11133					node.type = 3;
11134					node.raw = true;
11135					node.value = unescape(node.value).substr(14);
11136				}
11137			}
11138		});
11139
11140		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
11141			var i = nodes.length, node;
11142
11143			while (i--) {
11144				node = nodes[i];
11145				if (node.type === 7) {
11146					node.remove();
11147				} else if (node.type === 1) {
11148					if (name === "input" && !("type" in node.attributes.map)) {
11149						node.attr('type', 'text');
11150					}
11151				}
11152			}
11153		});
11154
11155		// Fix list elements, TODO: Replace this later
11156		if (settings.fix_list_elements) {
11157			htmlParser.addNodeFilter('ul,ol', function(nodes) {
11158				var i = nodes.length, node, parentNode;
11159
11160				while (i--) {
11161					node = nodes[i];
11162					parentNode = node.parent;
11163
11164					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
11165						if (node.prev && node.prev.name === 'li') {
11166							node.prev.append(node);
11167						}
11168					}
11169				}
11170			});
11171		}
11172
11173		// Remove internal data attributes
11174		htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,data-mce-selected', function(nodes, name) {
11175			var i = nodes.length;
11176
11177			while (i--) {
11178				nodes[i].attr(name, null);
11179			}
11180		});
11181
11182		// Return public methods
11183		return {
11184			/**
11185			 * Schema instance that was used to when the Serializer was constructed.
11186			 *
11187			 * @field {tinymce.html.Schema} schema
11188			 */
11189			schema: schema,
11190
11191			/**
11192			 * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
11193			 * and then execute the callback ones it has finished parsing the document.
11194			 *
11195			 * @example
11196			 * parser.addNodeFilter('p,h1', function(nodes, name) {
11197			 *		for (var i = 0; i < nodes.length; i++) {
11198			 *			console.log(nodes[i].name);
11199			 *		}
11200			 * });
11201			 * @method addNodeFilter
11202			 * @method {String} name Comma separated list of nodes to collect.
11203			 * @param {function} callback Callback function to execute once it has collected nodes.
11204			 */
11205			addNodeFilter: htmlParser.addNodeFilter,
11206
11207			/**
11208			 * Adds a attribute filter function to the parser used by the serializer, the parser will
11209			 * collect nodes that has the specified attributes
11210			 * and then execute the callback ones it has finished parsing the document.
11211			 *
11212			 * @example
11213			 * parser.addAttributeFilter('src,href', function(nodes, name) {
11214			 *		for (var i = 0; i < nodes.length; i++) {
11215			 *			console.log(nodes[i].name);
11216			 *		}
11217			 * });
11218			 * @method addAttributeFilter
11219			 * @method {String} name Comma separated list of nodes to collect.
11220			 * @param {function} callback Callback function to execute once it has collected nodes.
11221			 */
11222			addAttributeFilter: htmlParser.addAttributeFilter,
11223
11224			/**
11225			 * Serializes the specified browser DOM node into a HTML string.
11226			 *
11227			 * @method serialize
11228			 * @param {DOMNode} node DOM node to serialize.
11229			 * @param {Object} args Arguments option that gets passed to event handlers.
11230			 */
11231			serialize: function(node, args) {
11232				var self = this, impl, doc, oldDoc, htmlSerializer, content;
11233
11234				// Explorer won't clone contents of script and style and the
11235				// selected index of select elements are cleared on a clone operation.
11236				if (Env.ie && dom.select('script,style,select,map').length > 0) {
11237					content = node.innerHTML;
11238					node = node.cloneNode(false);
11239					dom.setHTML(node, content);
11240				} else {
11241					node = node.cloneNode(true);
11242				}
11243
11244				// Nodes needs to be attached to something in WebKit/Opera
11245				// This fix will make DOM ranges and make Sizzle happy!
11246				impl = node.ownerDocument.implementation;
11247				if (impl.createHTMLDocument) {
11248					// Create an empty HTML document
11249					doc = impl.createHTMLDocument("");
11250
11251					// Add the element or it's children if it's a body element to the new document
11252					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
11253						doc.body.appendChild(doc.importNode(node, true));
11254					});
11255
11256					// Grab first child or body element for serialization
11257					if (node.nodeName != 'BODY') {
11258						node = doc.body.firstChild;
11259					} else {
11260						node = doc.body;
11261					}
11262
11263					// set the new document in DOMUtils so createElement etc works
11264					oldDoc = dom.doc;
11265					dom.doc = doc;
11266				}
11267
11268				args = args || {};
11269				args.format = args.format || 'html';
11270
11271				// Don't wrap content if we want selected html
11272				if (args.selection) {
11273					args.forced_root_block = '';
11274				}
11275
11276				// Pre process
11277				if (!args.no_events) {
11278					args.node = node;
11279					self.onPreProcess(args);
11280				}
11281
11282				// Setup serializer
11283				htmlSerializer = new Serializer(settings, schema);
11284
11285				// Parse and serialize HTML
11286				args.content = htmlSerializer.serialize(
11287					htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
11288				);
11289
11290				// Replace all BOM characters for now until we can find a better solution
11291				if (!args.cleanup) {
11292					args.content = args.content.replace(/\uFEFF/g, '');
11293				}
11294
11295				// Post process
11296				if (!args.no_events) {
11297					self.onPostProcess(args);
11298				}
11299
11300				// Restore the old document if it was changed
11301				if (oldDoc) {
11302					dom.doc = oldDoc;
11303				}
11304
11305				args.node = null;
11306
11307				return args.content;
11308			},
11309
11310			/**
11311			 * Adds valid elements rules to the serializers schema instance this enables you to specify things
11312			 * like what elements should be outputted and what attributes specific elements might have.
11313			 * Consult the Wiki for more details on this format.
11314			 *
11315			 * @method addRules
11316			 * @param {String} rules Valid elements rules string to add to schema.
11317			 */
11318			addRules: function(rules) {
11319				schema.addValidElements(rules);
11320			},
11321
11322			/**
11323			 * Sets the valid elements rules to the serializers schema instance this enables you to specify things
11324			 * like what elements should be outputted and what attributes specific elements might have.
11325			 * Consult the Wiki for more details on this format.
11326			 *
11327			 * @method setRules
11328			 * @param {String} rules Valid elements rules string.
11329			 */
11330			setRules: function(rules) {
11331				schema.setValidElements(rules);
11332			},
11333
11334			onPreProcess: function(args) {
11335				if (editor) {
11336					editor.fire('PreProcess', args);
11337				}
11338			},
11339
11340			onPostProcess: function(args) {
11341				if (editor) {
11342					editor.fire('PostProcess', args);
11343				}
11344			}
11345		};
11346	};
11347});
11348
11349// Included from: js/tinymce/classes/dom/TridentSelection.js
11350
11351/**
11352 * TridentSelection.js
11353 *
11354 * Copyright, Moxiecode Systems AB
11355 * Released under LGPL License.
11356 *
11357 * License: http://www.tinymce.com/license
11358 * Contributing: http://www.tinymce.com/contributing
11359 */
11360
11361/**
11362 * Selection class for old explorer versions. This one fakes the
11363 * native selection object available on modern browsers.
11364 *
11365 * @class tinymce.dom.TridentSelection
11366 */
11367define("tinymce/dom/TridentSelection", [], function() {
11368	function Selection(selection) {
11369		var self = this, dom = selection.dom, FALSE = false;
11370
11371		function getPosition(rng, start) {
11372			var checkRng, startIndex = 0, endIndex, inside,
11373				children, child, offset, index, position = -1, parent;
11374
11375			// Setup test range, collapse it and get the parent
11376			checkRng = rng.duplicate();
11377			checkRng.collapse(start);
11378			parent = checkRng.parentElement();
11379
11380			// Check if the selection is within the right document
11381			if (parent.ownerDocument !== selection.dom.doc) {
11382				return;
11383			}
11384
11385			// IE will report non editable elements as it's parent so look for an editable one
11386			while (parent.contentEditable === "false") {
11387				parent = parent.parentNode;
11388			}
11389
11390			// If parent doesn't have any children then return that we are inside the element
11391			if (!parent.hasChildNodes()) {
11392				return {node: parent, inside: 1};
11393			}
11394
11395			// Setup node list and endIndex
11396			children = parent.children;
11397			endIndex = children.length - 1;
11398
11399			// Perform a binary search for the position
11400			while (startIndex <= endIndex) {
11401				index = Math.floor((startIndex + endIndex) / 2);
11402
11403				// Move selection to node and compare the ranges
11404				child = children[index];
11405				checkRng.moveToElementText(child);
11406				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
11407
11408				// Before/after or an exact match
11409				if (position > 0) {
11410					endIndex = index - 1;
11411				} else if (position < 0) {
11412					startIndex = index + 1;
11413				} else {
11414					return {node: child};
11415				}
11416			}
11417
11418			// Check if child position is before or we didn't find a position
11419			if (position < 0) {
11420				// No element child was found use the parent element and the offset inside that
11421				if (!child) {
11422					checkRng.moveToElementText(parent);
11423					checkRng.collapse(true);
11424					child = parent;
11425					inside = true;
11426				} else {
11427					checkRng.collapse(false);
11428				}
11429
11430				// Walk character by character in text node until we hit the selected range endpoint,
11431				// hit the end of document or parent isn't the right one
11432				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
11433				offset = 0;
11434				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
11435					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
11436						break;
11437					}
11438
11439					offset++;
11440				}
11441			} else {
11442				// Child position is after the selection endpoint
11443				checkRng.collapse(true);
11444
11445				// Walk character by character in text node until we hit the selected range endpoint, hit
11446				// the end of document or parent isn't the right one
11447				offset = 0;
11448				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
11449					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
11450						break;
11451					}
11452
11453					offset++;
11454				}
11455			}
11456
11457			return {node: child, position: position, offset: offset, inside: inside};
11458		}
11459
11460		// Returns a W3C DOM compatible range object by using the IE Range API
11461		function getRange() {
11462			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
11463
11464			// If selection is outside the current document just return an empty range
11465			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
11466			if (element.ownerDocument != dom.doc) {
11467				return domRange;
11468			}
11469
11470			collapsed = selection.isCollapsed();
11471
11472			// Handle control selection
11473			if (ieRange.item) {
11474				domRange.setStart(element.parentNode, dom.nodeIndex(element));
11475				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
11476
11477				return domRange;
11478			}
11479
11480			function findEndPoint(start) {
11481				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
11482
11483				container = endPoint.node;
11484				offset = endPoint.offset;
11485
11486				if (endPoint.inside && !container.hasChildNodes()) {
11487					domRange[start ? 'setStart' : 'setEnd'](container, 0);
11488					return;
11489				}
11490
11491				if (offset === undef) {
11492					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
11493					return;
11494				}
11495
11496				if (endPoint.position < 0) {
11497					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
11498
11499					if (!sibling) {
11500						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
11501						return;
11502					}
11503
11504					if (!offset) {
11505						if (sibling.nodeType == 3) {
11506							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
11507						} else {
11508							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
11509						}
11510
11511						return;
11512					}
11513
11514					// Find the text node and offset
11515					while (sibling) {
11516						nodeValue = sibling.nodeValue;
11517						textNodeOffset += nodeValue.length;
11518
11519						// We are at or passed the position we where looking for
11520						if (textNodeOffset >= offset) {
11521							container = sibling;
11522							textNodeOffset -= offset;
11523							textNodeOffset = nodeValue.length - textNodeOffset;
11524							break;
11525						}
11526
11527						sibling = sibling.nextSibling;
11528					}
11529				} else {
11530					// Find the text node and offset
11531					sibling = container.previousSibling;
11532
11533					if (!sibling) {
11534						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
11535					}
11536
11537					// If there isn't any text to loop then use the first position
11538					if (!offset) {
11539						if (container.nodeType == 3) {
11540							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
11541						} else {
11542							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
11543						}
11544
11545						return;
11546					}
11547
11548					while (sibling) {
11549						textNodeOffset += sibling.nodeValue.length;
11550
11551						// We are at or passed the position we where looking for
11552						if (textNodeOffset >= offset) {
11553							container = sibling;
11554							textNodeOffset -= offset;
11555							break;
11556						}
11557
11558						sibling = sibling.previousSibling;
11559					}
11560				}
11561
11562				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
11563			}
11564
11565			try {
11566				// Find start point
11567				findEndPoint(true);
11568
11569				// Find end point if needed
11570				if (!collapsed) {
11571					findEndPoint();
11572				}
11573			} catch (ex) {
11574				// IE has a nasty bug where text nodes might throw "invalid argument" when you
11575				// access the nodeValue or other properties of text nodes. This seems to happend when
11576				// text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
11577				if (ex.number == -2147024809) {
11578					// Get the current selection
11579					bookmark = self.getBookmark(2);
11580
11581					// Get start element
11582					tmpRange = ieRange.duplicate();
11583					tmpRange.collapse(true);
11584					element = tmpRange.parentElement();
11585
11586					// Get end element
11587					if (!collapsed) {
11588						tmpRange = ieRange.duplicate();
11589						tmpRange.collapse(false);
11590						element2 = tmpRange.parentElement();
11591						element2.innerHTML = element2.innerHTML;
11592					}
11593
11594					// Remove the broken elements
11595					element.innerHTML = element.innerHTML;
11596
11597					// Restore the selection
11598					self.moveToBookmark(bookmark);
11599
11600					// Since the range has moved we need to re-get it
11601					ieRange = selection.getRng();
11602
11603					// Find start point
11604					findEndPoint(true);
11605
11606					// Find end point if needed
11607					if (!collapsed) {
11608						findEndPoint();
11609					}
11610				} else {
11611					throw ex; // Throw other errors
11612				}
11613			}
11614
11615			return domRange;
11616		}
11617
11618		this.getBookmark = function(type) {
11619			var rng = selection.getRng(), bookmark = {};
11620
11621			function getIndexes(node) {
11622				var parent, root, children, i, indexes = [];
11623
11624				parent = node.parentNode;
11625				root = dom.getRoot().parentNode;
11626
11627				while (parent != root && parent.nodeType !== 9) {
11628					children = parent.children;
11629
11630					i = children.length;
11631					while (i--) {
11632						if (node === children[i]) {
11633							indexes.push(i);
11634							break;
11635						}
11636					}
11637
11638					node = parent;
11639					parent = parent.parentNode;
11640				}
11641
11642				return indexes;
11643			}
11644
11645			function getBookmarkEndPoint(start) {
11646				var position;
11647
11648				position = getPosition(rng, start);
11649				if (position) {
11650					return {
11651						position: position.position,
11652						offset: position.offset,
11653						indexes: getIndexes(position.node),
11654						inside: position.inside
11655					};
11656				}
11657			}
11658
11659			// Non ubstructive bookmark
11660			if (type === 2) {
11661				// Handle text selection
11662				if (!rng.item) {
11663					bookmark.start = getBookmarkEndPoint(true);
11664
11665					if (!selection.isCollapsed()) {
11666						bookmark.end = getBookmarkEndPoint();
11667					}
11668				} else {
11669					bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
11670				}
11671			}
11672
11673			return bookmark;
11674		};
11675
11676		this.moveToBookmark = function(bookmark) {
11677			var rng, body = dom.doc.body;
11678
11679			function resolveIndexes(indexes) {
11680				var node, i, idx, children;
11681
11682				node = dom.getRoot();
11683				for (i = indexes.length - 1; i >= 0; i--) {
11684					children = node.children;
11685					idx = indexes[i];
11686
11687					if (idx <= children.length - 1) {
11688						node = children[idx];
11689					}
11690				}
11691
11692				return node;
11693			}
11694
11695			function setBookmarkEndPoint(start) {
11696				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
11697
11698				if (endPoint) {
11699					moveLeft = endPoint.position > 0;
11700
11701					moveRng = body.createTextRange();
11702					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
11703
11704					offset = endPoint.offset;
11705					if (offset !== undef) {
11706						moveRng.collapse(endPoint.inside || moveLeft);
11707						moveRng.moveStart('character', moveLeft ? -offset : offset);
11708					} else {
11709						moveRng.collapse(start);
11710					}
11711
11712					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
11713
11714					if (start) {
11715						rng.collapse(true);
11716					}
11717				}
11718			}
11719
11720			if (bookmark.start) {
11721				if (bookmark.start.ctrl) {
11722					rng = body.createControlRange();
11723					rng.addElement(resolveIndexes(bookmark.start.indexes));
11724					rng.select();
11725				} else {
11726					rng = body.createTextRange();
11727					setBookmarkEndPoint(true);
11728					setBookmarkEndPoint();
11729					rng.select();
11730				}
11731			}
11732		};
11733
11734		this.addRange = function(rng) {
11735			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
11736				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
11737
11738			function setEndPoint(start) {
11739				var container, offset, marker, tmpRng, nodes;
11740
11741				marker = dom.create('a');
11742				container = start ? startContainer : endContainer;
11743				offset = start ? startOffset : endOffset;
11744				tmpRng = ieRng.duplicate();
11745
11746				if (container == doc || container == doc.documentElement) {
11747					container = body;
11748					offset = 0;
11749				}
11750
11751				if (container.nodeType == 3) {
11752					container.parentNode.insertBefore(marker, container);
11753					tmpRng.moveToElementText(marker);
11754					tmpRng.moveStart('character', offset);
11755					dom.remove(marker);
11756					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
11757				} else {
11758					nodes = container.childNodes;
11759
11760					if (nodes.length) {
11761						if (offset >= nodes.length) {
11762							dom.insertAfter(marker, nodes[nodes.length - 1]);
11763						} else {
11764							container.insertBefore(marker, nodes[offset]);
11765						}
11766
11767						tmpRng.moveToElementText(marker);
11768					} else if (container.canHaveHTML) {
11769						// Empty node selection for example <div>|</div>
11770						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
11771						container.innerHTML = '<span>&#xFEFF;</span>';
11772						marker = container.firstChild;
11773						tmpRng.moveToElementText(marker);
11774						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
11775					}
11776
11777					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
11778					dom.remove(marker);
11779				}
11780			}
11781
11782			// Setup some shorter versions
11783			startContainer = rng.startContainer;
11784			startOffset = rng.startOffset;
11785			endContainer = rng.endContainer;
11786			endOffset = rng.endOffset;
11787			ieRng = body.createTextRange();
11788
11789			// If single element selection then try making a control selection out of it
11790			if (startContainer == endContainer && startContainer.nodeType == 1) {
11791				// Trick to place the caret inside an empty block element like <p></p>
11792				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
11793					if (startContainer.canHaveHTML) {
11794						// Check if previous sibling is an empty block if it is then we need to render it
11795						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
11796						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
11797						sibling = startContainer.previousSibling;
11798						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
11799							sibling.innerHTML = '&#xFEFF;';
11800						} else {
11801							sibling = null;
11802						}
11803
11804						startContainer.innerHTML = '<span>&#xFEFF;</span><span>&#xFEFF;</span>';
11805						ieRng.moveToElementText(startContainer.lastChild);
11806						ieRng.select();
11807						dom.doc.selection.clear();
11808						startContainer.innerHTML = '';
11809
11810						if (sibling) {
11811							sibling.innerHTML = '';
11812						}
11813						return;
11814					} else {
11815						startOffset = dom.nodeIndex(startContainer);
11816						startContainer = startContainer.parentNode;
11817					}
11818				}
11819
11820				if (startOffset == endOffset - 1) {
11821					try {
11822						ctrlElm = startContainer.childNodes[startOffset];
11823						ctrlRng = body.createControlRange();
11824						ctrlRng.addElement(ctrlElm);
11825						ctrlRng.select();
11826
11827						// Check if the range produced is on the correct element and is a control range
11828						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
11829						nativeRng = selection.getRng();
11830						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
11831							return;
11832						}
11833					} catch (ex) {
11834						// Ignore
11835					}
11836				}
11837			}
11838
11839			// Set start/end point of selection
11840			setEndPoint(true);
11841			setEndPoint();
11842
11843			// Select the new range and scroll it into view
11844			ieRng.select();
11845		};
11846
11847		// Expose range method
11848		this.getRangeAt = getRange;
11849	}
11850
11851	return Selection;
11852});
11853
11854// Included from: js/tinymce/classes/util/VK.js
11855
11856/**
11857 * VK.js
11858 *
11859 * Copyright, Moxiecode Systems AB
11860 * Released under LGPL License.
11861 *
11862 * License: http://www.tinymce.com/license
11863 * Contributing: http://www.tinymce.com/contributing
11864 */
11865
11866/**
11867 * This file exposes a set of the common KeyCodes for use.  Please grow it as needed.
11868 */
11869define("tinymce/util/VK", [
11870	"tinymce/Env"
11871], function(Env) {
11872	return {
11873		BACKSPACE: 8,
11874		DELETE: 46,
11875		DOWN: 40,
11876		ENTER: 13,
11877		LEFT: 37,
11878		RIGHT: 39,
11879		SPACEBAR: 32,
11880		TAB: 9,
11881		UP: 38,
11882
11883		modifierPressed: function(e) {
11884			return e.shiftKey || e.ctrlKey || e.altKey;
11885		},
11886
11887		metaKeyPressed: function(e) {
11888			// Check if ctrl or meta key is pressed also check if alt is false for Polish users
11889			return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
11890		}
11891	};
11892});
11893
11894// Included from: js/tinymce/classes/dom/ControlSelection.js
11895
11896/**
11897 * ControlSelection.js
11898 *
11899 * Copyright, Moxiecode Systems AB
11900 * Released under LGPL License.
11901 *
11902 * License: http://www.tinymce.com/license
11903 * Contributing: http://www.tinymce.com/contributing
11904 */
11905
11906/**
11907 * This class handles control selection of elements. Controls are elements
11908 * that can be resized and needs to be selected as a whole. It adds custom resize handles
11909 * to all browser engines that support properly disabling the built in resize logic.
11910 *
11911 * @class tinymce.dom.ControlSelection
11912 */
11913define("tinymce/dom/ControlSelection", [
11914	"tinymce/util/VK",
11915	"tinymce/util/Tools",
11916	"tinymce/Env"
11917], function(VK, Tools, Env) {
11918	return function(selection, editor) {
11919		var dom = editor.dom, each = Tools.each;
11920		var selectedElm, selectedElmGhost, resizeHandles, selectedHandle, lastMouseDownEvent;
11921		var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
11922		var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
11923
11924		// Details about each resize handle how to scale etc
11925		resizeHandles = {
11926			// Name: x multiplier, y multiplier, delta size x, delta size y
11927			n:  [0.5,   0,     0,   -1],
11928			e:  [1,    0.5,    1,    0],
11929			s:  [0.5,   1,     0,    1],
11930			w:  [0,    0.5,   -1,    0],
11931			nw: [0,     0,    -1,   -1],
11932			ne: [1,     0,     1,   -1],
11933			se: [1,     1,     1,    1],
11934			sw: [0,     1,    -1,    1]
11935		};
11936
11937		// Add CSS for resize handles, cloned element and selected
11938		var rootClass = '.mce-content-body';
11939		editor.contentStyles.push(
11940			rootClass + ' div.mce-resizehandle {' +
11941				'position: absolute;' +
11942				'border: 1px solid black;' +
11943				'background: #FFF;' +
11944				'width: 5px;' +
11945				'height: 5px;' +
11946				'z-index: 10000' +
11947			'}' +
11948			rootClass + ' .mce-resizehandle:hover {' +
11949				'background: #000' +
11950			'}' +
11951			rootClass + ' img[data-mce-selected], hr[data-mce-selected] {' +
11952				'outline: 1px solid black;' +
11953				'resize: none' + // Have been talks about implementing this in browsers
11954			'}' +
11955			rootClass + ' .mce-clonedresizable {' +
11956				'position: absolute;' +
11957				(Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
11958				'opacity: .5;' +
11959				'filter: alpha(opacity=50);' +
11960				'z-index: 10000' +
11961			'}'
11962		);
11963
11964		function isResizable(elm) {
11965			if (editor.settings.object_resizing === false) {
11966				return false;
11967			}
11968
11969			if (!/TABLE|IMG|DIV/.test(elm.nodeName)) {
11970				return false;
11971			}
11972
11973			if (elm.getAttribute('data-mce-resize') === 'false') {
11974				return false;
11975			}
11976
11977			return true;
11978		}
11979
11980		function resizeGhostElement(e) {
11981			var deltaX, deltaY;
11982
11983			// Calc new width/height
11984			deltaX = e.screenX - startX;
11985			deltaY = e.screenY - startY;
11986
11987			// Calc new size
11988			width = deltaX * selectedHandle[2] + startW;
11989			height = deltaY * selectedHandle[3] + startH;
11990
11991			// Never scale down lower than 5 pixels
11992			width = width < 5 ? 5 : width;
11993			height = height < 5 ? 5 : height;
11994
11995			// Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image
11996			if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {
11997				width = Math.round(height / ratio);
11998				height = Math.round(width * ratio);
11999			}
12000
12001			// Update ghost size
12002			dom.setStyles(selectedElmGhost, {
12003				width: width,
12004				height: height
12005			});
12006
12007			// Update ghost X position if needed
12008			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
12009				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
12010			}
12011
12012			// Update ghost Y position if needed
12013			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
12014				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
12015			}
12016
12017			if (!resizeStarted) {
12018				editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
12019				resizeStarted = true;
12020			}
12021		}
12022
12023		function endGhostResize() {
12024			resizeStarted = false;
12025
12026			function setSizeProp(name, value) {
12027				if (value) {
12028					// Resize by using style or attribute
12029					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
12030						dom.setStyle(selectedElm, name, value);
12031					} else {
12032						dom.setAttrib(selectedElm, name, value);
12033					}
12034				}
12035			}
12036
12037			// Set width/height properties
12038			setSizeProp('width', width);
12039			setSizeProp('height', height);
12040
12041			dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
12042			dom.unbind(editableDoc, 'mouseup', endGhostResize);
12043
12044			if (rootDocument != editableDoc) {
12045				dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
12046				dom.unbind(rootDocument, 'mouseup', endGhostResize);
12047			}
12048
12049			// Remove ghost and update resize handle positions
12050			dom.remove(selectedElmGhost);
12051
12052			if (!isIE || selectedElm.nodeName == "TABLE") {
12053				showResizeRect(selectedElm);
12054			}
12055
12056			editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
12057			editor.nodeChanged();
12058		}
12059
12060		function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
12061			var position, targetWidth, targetHeight, e, rect, offsetParent = editor.getBody();
12062
12063			// Get position and size of target
12064			position = dom.getPos(targetElm, offsetParent);
12065			selectedElmX = position.x;
12066			selectedElmY = position.y;
12067			rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
12068			targetWidth = rect.width || (rect.right - rect.left);
12069			targetHeight = rect.height || (rect.bottom - rect.top);
12070
12071			// Reset width/height if user selects a new image/table
12072			if (selectedElm != targetElm) {
12073				detachResizeStartListener();
12074				selectedElm = targetElm;
12075				width = height = 0;
12076			}
12077
12078			// Makes it possible to disable resizing
12079			e = editor.fire('ObjectSelected', {target: targetElm});
12080
12081			if (isResizable(targetElm) && !e.isDefaultPrevented()) {
12082				each(resizeHandles, function(handle, name) {
12083					var handleElm, handlerContainerElm;
12084
12085					function startDrag(e) {
12086						resizeStarted = true;
12087
12088						startX = e.screenX;
12089						startY = e.screenY;
12090						startW = selectedElm.clientWidth;
12091						startH = selectedElm.clientHeight;
12092						ratio = startH / startW;
12093						selectedHandle = handle;
12094
12095						selectedElmGhost = selectedElm.cloneNode(true);
12096						dom.addClass(selectedElmGhost, 'mce-clonedresizable');
12097						selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
12098						selectedElmGhost.unSelectabe = true;
12099						dom.setStyles(selectedElmGhost, {
12100							left: selectedElmX,
12101							top: selectedElmY,
12102							margin: 0
12103						});
12104
12105						selectedElmGhost.removeAttribute('data-mce-selected');
12106						editor.getBody().appendChild(selectedElmGhost);
12107
12108						dom.bind(editableDoc, 'mousemove', resizeGhostElement);
12109						dom.bind(editableDoc, 'mouseup', endGhostResize);
12110
12111						if (rootDocument != editableDoc) {
12112							dom.bind(rootDocument, 'mousemove', resizeGhostElement);
12113							dom.bind(rootDocument, 'mouseup', endGhostResize);
12114						}
12115					}
12116
12117					if (mouseDownHandleName) {
12118						// Drag started by IE native resizestart
12119						if (name == mouseDownHandleName) {
12120							startDrag(mouseDownEvent);
12121						}
12122
12123						return;
12124					}
12125
12126					// Get existing or render resize handle
12127					handleElm = dom.get('mceResizeHandle' + name);
12128					if (!handleElm) {
12129						handlerContainerElm = editor.getBody();
12130
12131						handleElm = dom.add(handlerContainerElm, 'div', {
12132							id: 'mceResizeHandle' + name,
12133							'data-mce-bogus': true,
12134							'class': 'mce-resizehandle',
12135							contentEditable: false, // Hides IE move layer cursor
12136							unSelectabe: true,
12137							style: 'cursor:' + name + '-resize; margin:0; padding:0'
12138						});
12139
12140						dom.bind(handleElm, 'mousedown', function(e) {
12141							e.preventDefault();
12142							startDrag(e);
12143						});
12144					} else {
12145						dom.show(handleElm);
12146					}
12147
12148					/*
12149					var halfHandleW = handleElm.offsetWidth / 2;
12150					var halfHandleH = handleElm.offsetHeight / 2;
12151
12152					// Position element
12153					dom.setStyles(handleElm, {
12154						left: Math.floor((targetWidth * handle[0] + selectedElmX) - halfHandleW + (handle[2] * halfHandleW)),
12155						top: Math.floor((targetHeight * handle[1] + selectedElmY) - halfHandleH + (handle[3] * halfHandleH))
12156					});
12157					*/
12158
12159					// Position element
12160					dom.setStyles(handleElm, {
12161						left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
12162						top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
12163					});
12164				});
12165			} else {
12166				hideResizeRect();
12167			}
12168
12169			selectedElm.setAttribute('data-mce-selected', '1');
12170		}
12171
12172		function hideResizeRect() {
12173			var name, handleElm;
12174
12175			if (selectedElm) {
12176				selectedElm.removeAttribute('data-mce-selected');
12177			}
12178
12179			for (name in resizeHandles) {
12180				handleElm = dom.get('mceResizeHandle' + name);
12181				if (handleElm) {
12182					dom.unbind(handleElm);
12183					dom.remove(handleElm);
12184				}
12185			}
12186		}
12187
12188		function updateResizeRect(e) {
12189			var controlElm;
12190
12191			function isChildOrEqual(node, parent) {
12192				do {
12193					if (node === parent) {
12194						return true;
12195					}
12196				} while ((node = node.parentNode));
12197			}
12198
12199			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
12200			each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
12201				img.removeAttribute('data-mce-selected');
12202			});
12203
12204			controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
12205			controlElm = dom.getParent(controlElm, isIE ? 'table' : 'table,img,hr');
12206
12207			if (controlElm) {
12208				disableGeckoResize();
12209
12210				if (isChildOrEqual(selection.getStart(), controlElm) && isChildOrEqual(selection.getEnd(), controlElm)) {
12211					if (!isIE || (controlElm != selection.getStart() && selection.getStart().nodeName !== 'IMG')) {
12212						showResizeRect(controlElm);
12213						return;
12214					}
12215				}
12216			}
12217
12218			hideResizeRect();
12219		}
12220
12221		function attachEvent(elm, name, func) {
12222			if (elm && elm.attachEvent) {
12223				elm.attachEvent('on' + name, func);
12224			}
12225		}
12226
12227		function detachEvent(elm, name, func) {
12228			if (elm && elm.detachEvent) {
12229				elm.detachEvent('on' + name, func);
12230			}
12231		}
12232
12233		function resizeNativeStart(e) {
12234			var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
12235
12236			pos = target.getBoundingClientRect();
12237			relativeX = lastMouseDownEvent.clientX - pos.left;
12238			relativeY = lastMouseDownEvent.clientY - pos.top;
12239
12240			// Figure out what corner we are draging on
12241			for (name in resizeHandles) {
12242				corner = resizeHandles[name];
12243
12244				cornerX = target.offsetWidth * corner[0];
12245				cornerY = target.offsetHeight * corner[1];
12246
12247				if (Math.abs(cornerX - relativeX) < 8 && Math.abs(cornerY - relativeY) < 8) {
12248					selectedHandle = corner;
12249					break;
12250				}
12251			}
12252
12253			// Remove native selection and let the magic begin
12254			resizeStarted = true;
12255			editor.getDoc().selection.empty();
12256			showResizeRect(target, name, lastMouseDownEvent);
12257		}
12258
12259		function nativeControlSelect(e) {
12260			var target = e.srcElement;
12261
12262			if (target != selectedElm) {
12263				detachResizeStartListener();
12264
12265				if (target.id.indexOf('mceResizeHandle') === 0) {
12266					e.returnValue = false;
12267					return;
12268				}
12269
12270				if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
12271					hideResizeRect();
12272					selectedElm = target;
12273					attachEvent(target, 'resizestart', resizeNativeStart);
12274				}
12275			}
12276		}
12277
12278		function detachResizeStartListener() {
12279			detachEvent(selectedElm, 'resizestart', resizeNativeStart);
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(editor.getBody(), '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 (/^(TABLE|IMG|HR)$/.test(nodeName)) {
12333							editor.selection.select(e.target, nodeName == 'TABLE');
12334							editor.nodeChanged();
12335						}
12336					});
12337
12338					editor.dom.bind(editor.getBody(), 'mscontrolselect', function(e) {
12339						if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
12340							e.preventDefault();
12341						}
12342					});
12343				}
12344			}
12345
12346			editor.on('nodechange mousedown mouseup ResizeEditor', updateResizeRect);
12347
12348			// Update resize rect while typing in a table
12349			editor.on('keydown keyup', function(e) {
12350				if (selectedElm && selectedElm.nodeName == "TABLE") {
12351					updateResizeRect(e);
12352				}
12353			});
12354
12355			// Hide rect on focusout since it would float on top of windows otherwise
12356			//editor.on('focusout', hideResizeRect);
12357		});
12358
12359		function destroy() {
12360			selectedElm = selectedElmGhost = null;
12361
12362			if (isIE) {
12363				detachResizeStartListener();
12364				detachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
12365			}
12366		}
12367
12368		return {
12369			controlSelect: controlSelect,
12370			destroy: destroy
12371		};
12372	};
12373});
12374
12375// Included from: js/tinymce/classes/dom/Selection.js
12376
12377/**
12378 * Selection.js
12379 *
12380 * Copyright, Moxiecode Systems AB
12381 * Released under LGPL License.
12382 *
12383 * License: http://www.tinymce.com/license
12384 * Contributing: http://www.tinymce.com/contributing
12385 */
12386
12387/**
12388 * This class handles text and control selection it's an crossbrowser utility class.
12389 * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
12390 *
12391 * @class tinymce.dom.Selection
12392 * @example
12393 * // Getting the currently selected node for the active editor
12394 * alert(tinymce.activeEditor.selection.getNode().nodeName);
12395 */
12396define("tinymce/dom/Selection", [
12397	"tinymce/dom/TreeWalker",
12398	"tinymce/dom/TridentSelection",
12399	"tinymce/dom/ControlSelection",
12400	"tinymce/Env",
12401	"tinymce/util/Tools"
12402], function(TreeWalker, TridentSelection, ControlSelection, Env, Tools) {
12403	var each = Tools.each, grep = Tools.grep, trim = Tools.trim;
12404	var isIE = Env.ie, isOpera = Env.opera;
12405
12406	/**
12407	 * Constructs a new selection instance.
12408	 *
12409	 * @constructor
12410	 * @method Selection
12411	 * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
12412	 * @param {Window} win Window to bind the selection object to.
12413	 * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
12414	 */
12415	function Selection(dom, win, serializer, editor) {
12416		var self = this;
12417
12418		self.dom = dom;
12419		self.win = win;
12420		self.serializer = serializer;
12421		self.editor = editor;
12422
12423		self.controlSelection = new ControlSelection(self, editor);
12424
12425		// No W3C Range support
12426		if (!self.win.getSelection) {
12427			self.tridentSel = new TridentSelection(self);
12428		}
12429	}
12430
12431	Selection.prototype = {
12432		/**
12433		 * Move the selection cursor range to the specified node and offset.
12434		 * If there is no node specified it will move it to the first suitable location within the body.
12435		 *
12436		 * @method setCursorLocation
12437		 * @param {Node} node Optional node to put the cursor in.
12438		 * @param {Number} offset Optional offset from the start of the node to put the cursor at.
12439		 */
12440		setCursorLocation: function(node, offset) {
12441			var self = this, rng = self.dom.createRng();
12442
12443			if (!node) {
12444				self._moveEndPoint(rng, self.editor.getBody(), true);
12445				self.setRng(rng);
12446			} else {
12447				rng.setStart(node, offset);
12448				rng.setEnd(node, offset);
12449				self.setRng(rng);
12450				self.collapse(false);
12451			}
12452		},
12453
12454		/**
12455		 * Returns the selected contents using the DOM serializer passed in to this class.
12456		 *
12457		 * @method getContent
12458		 * @param {Object} s Optional settings class with for example output format text or html.
12459		 * @return {String} Selected contents in for example HTML format.
12460		 * @example
12461		 * // Alerts the currently selected contents
12462		 * alert(tinymce.activeEditor.selection.getContent());
12463		 *
12464		 * // Alerts the currently selected contents as plain text
12465		 * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
12466		 */
12467		getContent: function(args) {
12468			var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
12469			var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
12470
12471			args = args || {};
12472			whiteSpaceBefore = whiteSpaceAfter = '';
12473			args.get = true;
12474			args.format = args.format || 'html';
12475			args.selection = true;
12476			self.editor.fire('BeforeGetContent', args);
12477
12478			if (args.format == 'text') {
12479				return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
12480			}
12481
12482			if (rng.cloneContents) {
12483				fragment = rng.cloneContents();
12484
12485				if (fragment) {
12486					tmpElm.appendChild(fragment);
12487				}
12488			} else if (rng.item !== undefined || rng.htmlText !== undefined) {
12489				// IE will produce invalid markup if elements are present that
12490				// it doesn't understand like custom elements or HTML5 elements.
12491				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
12492				tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
12493				tmpElm.removeChild(tmpElm.firstChild);
12494			} else {
12495				tmpElm.innerHTML = rng.toString();
12496			}
12497
12498			// Keep whitespace before and after
12499			if (/^\s/.test(tmpElm.innerHTML)) {
12500				whiteSpaceBefore = ' ';
12501			}
12502
12503			if (/\s+$/.test(tmpElm.innerHTML)) {
12504				whiteSpaceAfter = ' ';
12505			}
12506
12507			args.getInner = true;
12508
12509			args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
12510			self.editor.fire('GetContent', args);
12511
12512			return args.content;
12513		},
12514
12515		/**
12516		 * Sets the current selection to the specified content. If any contents is selected it will be replaced
12517		 * with the contents passed in to this function. If there is no selection the contents will be inserted
12518		 * where the caret is placed in the editor/page.
12519		 *
12520		 * @method setContent
12521		 * @param {String} content HTML contents to set could also be other formats depending on settings.
12522		 * @param {Object} args Optional settings object with for example data format.
12523		 * @example
12524		 * // Inserts some HTML contents at the current selection
12525		 * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
12526		 */
12527		setContent: function(content, args) {
12528			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
12529
12530			args = args || {format: 'html'};
12531			args.set = true;
12532			args.selection = true;
12533			content = args.content = content;
12534
12535			// Dispatch before set content event
12536			if (!args.no_events) {
12537				self.editor.fire('BeforeSetContent', args);
12538			}
12539
12540			content = args.content;
12541
12542			if (rng.insertNode) {
12543				// Make caret marker since insertNode places the caret in the beginning of text after insert
12544				content += '<span id="__caret">_</span>';
12545
12546				// Delete and insert new node
12547				if (rng.startContainer == doc && rng.endContainer == doc) {
12548					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
12549					doc.body.innerHTML = content;
12550				} else {
12551					rng.deleteContents();
12552
12553					if (doc.body.childNodes.length === 0) {
12554						doc.body.innerHTML = content;
12555					} else {
12556						// createContextualFragment doesn't exists in IE 9 DOMRanges
12557						if (rng.createContextualFragment) {
12558							rng.insertNode(rng.createContextualFragment(content));
12559						} else {
12560							// Fake createContextualFragment call in IE 9
12561							frag = doc.createDocumentFragment();
12562							temp = doc.createElement('div');
12563
12564							frag.appendChild(temp);
12565							temp.outerHTML = content;
12566
12567							rng.insertNode(frag);
12568						}
12569					}
12570				}
12571
12572				// Move to caret marker
12573				caretNode = self.dom.get('__caret');
12574
12575				// Make sure we wrap it compleatly, Opera fails with a simple select call
12576				rng = doc.createRange();
12577				rng.setStartBefore(caretNode);
12578				rng.setEndBefore(caretNode);
12579				self.setRng(rng);
12580
12581				// Remove the caret position
12582				self.dom.remove('__caret');
12583
12584				try {
12585					self.setRng(rng);
12586				} catch (ex) {
12587					// Might fail on Opera for some odd reason
12588				}
12589			} else {
12590				if (rng.item) {
12591					// Delete content and get caret text selection
12592					doc.execCommand('Delete', false, null);
12593					rng = self.getRng();
12594				}
12595
12596				// Explorer removes spaces from the beginning of pasted contents
12597				if (/^\s+/.test(content)) {
12598					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
12599					self.dom.remove('__mce_tmp');
12600				} else {
12601					rng.pasteHTML(content);
12602				}
12603			}
12604
12605			// Dispatch set content event
12606			if (!args.no_events) {
12607				self.editor.fire('SetContent', args);
12608			}
12609		},
12610
12611		/**
12612		 * Returns the start element of a selection range. If the start is in a text
12613		 * node the parent element will be returned.
12614		 *
12615		 * @method getStart
12616		 * @return {Element} Start element of selection range.
12617		 */
12618		getStart: function() {
12619			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
12620
12621			if (rng.duplicate || rng.item) {
12622				// Control selection, return first item
12623				if (rng.item) {
12624					return rng.item(0);
12625				}
12626
12627				// Get start element
12628				checkRng = rng.duplicate();
12629				checkRng.collapse(1);
12630				startElement = checkRng.parentElement();
12631				if (startElement.ownerDocument !== self.dom.doc) {
12632					startElement = self.dom.getRoot();
12633				}
12634
12635				// Check if range parent is inside the start element, then return the inner parent element
12636				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
12637				parentElement = node = rng.parentElement();
12638				while ((node = node.parentNode)) {
12639					if (node == startElement) {
12640						startElement = parentElement;
12641						break;
12642					}
12643				}
12644
12645				return startElement;
12646			} else {
12647				startElement = rng.startContainer;
12648
12649				if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
12650					startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
12651				}
12652
12653				if (startElement && startElement.nodeType == 3) {
12654					return startElement.parentNode;
12655				}
12656
12657				return startElement;
12658			}
12659		},
12660
12661		/**
12662		 * Returns the end element of a selection range. If the end is in a text
12663		 * node the parent element will be returned.
12664		 *
12665		 * @method getEnd
12666		 * @return {Element} End element of selection range.
12667		 */
12668		getEnd: function() {
12669			var self = this, rng = self.getRng(), endElement, endOffset;
12670
12671			if (rng.duplicate || rng.item) {
12672				if (rng.item) {
12673					return rng.item(0);
12674				}
12675
12676				rng = rng.duplicate();
12677				rng.collapse(0);
12678				endElement = rng.parentElement();
12679				if (endElement.ownerDocument !== self.dom.doc) {
12680					endElement = self.dom.getRoot();
12681				}
12682
12683				if (endElement && endElement.nodeName == 'BODY') {
12684					return endElement.lastChild || endElement;
12685				}
12686
12687				return endElement;
12688			} else {
12689				endElement = rng.endContainer;
12690				endOffset = rng.endOffset;
12691
12692				if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
12693					endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
12694				}
12695
12696				if (endElement && endElement.nodeType == 3) {
12697					return endElement.parentNode;
12698				}
12699
12700				return endElement;
12701			}
12702		},
12703
12704		/**
12705		 * Returns a bookmark location for the current selection. This bookmark object
12706		 * can then be used to restore the selection after some content modification to the document.
12707		 *
12708		 * @method getBookmark
12709		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
12710		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
12711		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
12712		 * @example
12713		 * // Stores a bookmark of the current selection
12714		 * var bm = tinymce.activeEditor.selection.getBookmark();
12715		 *
12716		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
12717		 *
12718		 * // Restore the selection bookmark
12719		 * tinymce.activeEditor.selection.moveToBookmark(bm);
12720		 */
12721		getBookmark: function(type, normalized) {
12722			var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
12723
12724			function findIndex(name, element) {
12725				var index = 0;
12726
12727				each(dom.select(name), function(node, i) {
12728					if (node == element) {
12729						index = i;
12730					}
12731				});
12732
12733				return index;
12734			}
12735
12736			function normalizeTableCellSelection(rng) {
12737				function moveEndPoint(start) {
12738					var container, offset, childNodes, prefix = start ? 'start' : 'end';
12739
12740					container = rng[prefix + 'Container'];
12741					offset = rng[prefix + 'Offset'];
12742
12743					if (container.nodeType == 1 && container.nodeName == "TR") {
12744						childNodes = container.childNodes;
12745						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
12746						if (container) {
12747							offset = start ? 0 : container.childNodes.length;
12748							rng['set' + (start ? 'Start' : 'End')](container, offset);
12749						}
12750					}
12751				}
12752
12753				moveEndPoint(true);
12754				moveEndPoint();
12755
12756				return rng;
12757			}
12758
12759			function getLocation() {
12760				var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};
12761
12762				function getPoint(rng, start) {
12763					var container = rng[start ? 'startContainer' : 'endContainer'],
12764						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
12765
12766					if (container.nodeType == 3) {
12767						if (normalized) {
12768							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
12769								offset += node.nodeValue.length;
12770							}
12771						}
12772
12773						point.push(offset);
12774					} else {
12775						childNodes = container.childNodes;
12776
12777						if (offset >= childNodes.length && childNodes.length) {
12778							after = 1;
12779							offset = Math.max(0, childNodes.length - 1);
12780						}
12781
12782						point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
12783					}
12784
12785					for (; container && container != root; container = container.parentNode) {
12786						point.push(t.dom.nodeIndex(container, normalized));
12787					}
12788
12789					return point;
12790				}
12791
12792				bookmark.start = getPoint(rng, true);
12793
12794				if (!t.isCollapsed()) {
12795					bookmark.end = getPoint(rng);
12796				}
12797
12798				return bookmark;
12799			}
12800
12801			if (type == 2) {
12802				element = t.getNode();
12803				name = element.nodeName;
12804
12805				if (name == 'IMG') {
12806					return {name: name, index: findIndex(name, element)};
12807				}
12808
12809				if (t.tridentSel) {
12810					return t.tridentSel.getBookmark(type);
12811				}
12812
12813				return getLocation();
12814			}
12815
12816			// Handle simple range
12817			if (type) {
12818				return {rng: t.getRng()};
12819			}
12820
12821			rng = t.getRng();
12822			id = dom.uniqueId();
12823			collapsed = t.isCollapsed();
12824			styles = 'overflow:hidden;line-height:0px';
12825
12826			// Explorer method
12827			if (rng.duplicate || rng.item) {
12828				// Text selection
12829				if (!rng.item) {
12830					rng2 = rng.duplicate();
12831
12832					try {
12833						// Insert start marker
12834						rng.collapse();
12835						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
12836
12837						// Insert end marker
12838						if (!collapsed) {
12839							rng2.collapse(false);
12840
12841							// Detect the empty space after block elements in IE and move the
12842							// end back one character <p></p>] becomes <p>]</p>
12843							rng.moveToElementText(rng2.parentElement());
12844							if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
12845								rng2.move('character', -1);
12846							}
12847
12848							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
12849						}
12850					} catch (ex) {
12851						// IE might throw unspecified error so lets ignore it
12852						return null;
12853					}
12854				} else {
12855					// Control selection
12856					element = rng.item(0);
12857					name = element.nodeName;
12858
12859					return {name: name, index: findIndex(name, element)};
12860				}
12861			} else {
12862				element = t.getNode();
12863				name = element.nodeName;
12864				if (name == 'IMG') {
12865					return {name: name, index: findIndex(name, element)};
12866				}
12867
12868				// W3C method
12869				rng2 = normalizeTableCellSelection(rng.cloneRange());
12870
12871				// Insert end marker
12872				if (!collapsed) {
12873					rng2.collapse(false);
12874					rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
12875				}
12876
12877				rng = normalizeTableCellSelection(rng);
12878				rng.collapse(true);
12879				rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
12880			}
12881
12882			t.moveToBookmark({id: id, keep: 1});
12883
12884			return {id: id};
12885		},
12886
12887		/**
12888		 * Restores the selection to the specified bookmark.
12889		 *
12890		 * @method moveToBookmark
12891		 * @param {Object} bookmark Bookmark to restore selection from.
12892		 * @return {Boolean} true/false if it was successful or not.
12893		 * @example
12894		 * // Stores a bookmark of the current selection
12895		 * var bm = tinymce.activeEditor.selection.getBookmark();
12896		 *
12897		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
12898		 *
12899		 * // Restore the selection bookmark
12900		 * tinymce.activeEditor.selection.moveToBookmark(bm);
12901		 */
12902		moveToBookmark: function(bookmark) {
12903			var t = this, dom = t.dom, rng, root, startContainer, endContainer, startOffset, endOffset;
12904
12905			function setEndPoint(start) {
12906				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
12907
12908				if (point) {
12909					offset = point[0];
12910
12911					// Find container node
12912					for (node = root, i = point.length - 1; i >= 1; i--) {
12913						children = node.childNodes;
12914
12915						if (point[i] > children.length - 1) {
12916							return;
12917						}
12918
12919						node = children[point[i]];
12920					}
12921
12922					// Move text offset to best suitable location
12923					if (node.nodeType === 3) {
12924						offset = Math.min(point[0], node.nodeValue.length);
12925					}
12926
12927					// Move element offset to best suitable location
12928					if (node.nodeType === 1) {
12929						offset = Math.min(point[0], node.childNodes.length);
12930					}
12931
12932					// Set offset within container node
12933					if (start) {
12934						rng.setStart(node, offset);
12935					} else {
12936						rng.setEnd(node, offset);
12937					}
12938				}
12939
12940				return true;
12941			}
12942
12943			function restoreEndPoint(suffix) {
12944				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
12945
12946				if (marker) {
12947					node = marker.parentNode;
12948
12949					if (suffix == 'start') {
12950						if (!keep) {
12951							idx = dom.nodeIndex(marker);
12952						} else {
12953							node = marker.firstChild;
12954							idx = 1;
12955						}
12956
12957						startContainer = endContainer = node;
12958						startOffset = endOffset = idx;
12959					} else {
12960						if (!keep) {
12961							idx = dom.nodeIndex(marker);
12962						} else {
12963							node = marker.firstChild;
12964							idx = 1;
12965						}
12966
12967						endContainer = node;
12968						endOffset = idx;
12969					}
12970
12971					if (!keep) {
12972						prev = marker.previousSibling;
12973						next = marker.nextSibling;
12974
12975						// Remove all marker text nodes
12976						each(grep(marker.childNodes), function(node) {
12977							if (node.nodeType == 3) {
12978								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
12979							}
12980						});
12981
12982						// Remove marker but keep children if for example contents where inserted into the marker
12983						// Also remove duplicated instances of the marker for example by a
12984						// split operation or by WebKit auto split on paste feature
12985						while ((marker = dom.get(bookmark.id + '_' + suffix))) {
12986							dom.remove(marker, 1);
12987						}
12988
12989						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
12990						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market
12991						// isn't worth the effort. Sorry, Opera but it's just a fact
12992						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !isOpera) {
12993							idx = prev.nodeValue.length;
12994							prev.appendData(next.nodeValue);
12995							dom.remove(next);
12996
12997							if (suffix == 'start') {
12998								startContainer = endContainer = prev;
12999								startOffset = endOffset = idx;
13000							} else {
13001								endContainer = prev;
13002								endOffset = idx;
13003							}
13004						}
13005					}
13006				}
13007			}
13008
13009			function addBogus(node) {
13010				// Adds a bogus BR element for empty block elements
13011				if (dom.isBlock(node) && !node.innerHTML && !isIE) {
13012					node.innerHTML = '<br data-mce-bogus="1" />';
13013				}
13014
13015				return node;
13016			}
13017
13018			if (bookmark) {
13019				if (bookmark.start) {
13020					rng = dom.createRng();
13021					root = dom.getRoot();
13022
13023					if (t.tridentSel) {
13024						return t.tridentSel.moveToBookmark(bookmark);
13025					}
13026
13027					if (setEndPoint(true) && setEndPoint()) {
13028						t.setRng(rng);
13029					}
13030				} else if (bookmark.id) {
13031					// Restore start/end points
13032					restoreEndPoint('start');
13033					restoreEndPoint('end');
13034
13035					if (startContainer) {
13036						rng = dom.createRng();
13037						rng.setStart(addBogus(startContainer), startOffset);
13038						rng.setEnd(addBogus(endContainer), endOffset);
13039						t.setRng(rng);
13040					}
13041				} else if (bookmark.name) {
13042					t.select(dom.select(bookmark.name)[bookmark.index]);
13043				} else if (bookmark.rng) {
13044					t.setRng(bookmark.rng);
13045				}
13046			}
13047		},
13048
13049		/**
13050		 * Selects the specified element. This will place the start and end of the selection range around the element.
13051		 *
13052		 * @method select
13053		 * @param {Element} node HMTL DOM element to select.
13054		 * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser.
13055		 * @return {Element} Selected element the same element as the one that got passed in.
13056		 * @example
13057		 * // Select the first paragraph in the active editor
13058		 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
13059		 */
13060		select: function(node, content) {
13061			var self = this, dom = self.dom, rng = dom.createRng(), idx;
13062
13063			// Clear stored range set by FocusManager
13064			self.lastFocusBookmark = null;
13065
13066			if (node) {
13067				if (!content && self.controlSelection.controlSelect(node)) {
13068					return;
13069				}
13070
13071				idx = dom.nodeIndex(node);
13072				rng.setStart(node.parentNode, idx);
13073				rng.setEnd(node.parentNode, idx + 1);
13074
13075				// Find first/last text node or BR element
13076				if (content) {
13077					self._moveEndPoint(rng, node, true);
13078					self._moveEndPoint(rng, node);
13079				}
13080
13081				self.setRng(rng);
13082			}
13083
13084			return node;
13085		},
13086
13087		/**
13088		 * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection.
13089		 *
13090		 * @method isCollapsed
13091		 * @return {Boolean} true/false state if the selection range is collapsed or not.
13092		 * Collapsed means if it's a caret or a larger selection.
13093		 */
13094		isCollapsed: function() {
13095			var self = this, rng = self.getRng(), sel = self.getSel();
13096
13097			if (!rng || rng.item) {
13098				return false;
13099			}
13100
13101			if (rng.compareEndPoints) {
13102				return rng.compareEndPoints('StartToEnd', rng) === 0;
13103			}
13104
13105			return !sel || rng.collapsed;
13106		},
13107
13108		/**
13109		 * Collapse the selection to start or end of range.
13110		 *
13111		 * @method collapse
13112		 * @param {Boolean} to_start Optional boolean state if to collapse to end or not. Defaults to start.
13113		 */
13114		collapse: function(to_start) {
13115			var self = this, rng = self.getRng(), node;
13116
13117			// Control range on IE
13118			if (rng.item) {
13119				node = rng.item(0);
13120				rng = self.win.document.body.createTextRange();
13121				rng.moveToElementText(node);
13122			}
13123
13124			rng.collapse(!!to_start);
13125			self.setRng(rng);
13126		},
13127
13128		/**
13129		 * Returns the browsers internal selection object.
13130		 *
13131		 * @method getSel
13132		 * @return {Selection} Internal browser selection object.
13133		 */
13134		getSel: function() {
13135			var win = this.win;
13136
13137			return win.getSelection ? win.getSelection() : win.document.selection;
13138		},
13139
13140		/**
13141		 * Returns the browsers internal range object.
13142		 *
13143		 * @method getRng
13144		 * @param {Boolean} w3c Forces a compatible W3C range on IE.
13145		 * @return {Range} Internal browser range object.
13146		 * @see http://www.quirksmode.org/dom/range_intro.html
13147		 * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/
13148		 */
13149		getRng: function(w3c) {
13150			var self = this, selection, rng, elm, doc = self.win.document, ieRng;
13151
13152			// Use last rng passed from FocusManager if it's available this enables
13153			// calls to editor.selection.getStart() to work when caret focus is lost on IE
13154			if (!w3c && self.lastFocusBookmark) {
13155				var bookmark = self.lastFocusBookmark;
13156
13157				// Convert bookmark to range IE 11 fix
13158				if (bookmark.startContainer) {
13159					rng = doc.createRange();
13160					rng.setStart(bookmark.startContainer, bookmark.startOffset);
13161					rng.setEnd(bookmark.endContainer, bookmark.endOffset);
13162				} else {
13163					rng = bookmark;
13164				}
13165
13166				return rng;
13167			}
13168
13169			// Found tridentSel object then we need to use that one
13170			if (w3c && self.tridentSel) {
13171				return self.tridentSel.getRangeAt(0);
13172			}
13173
13174			try {
13175				if ((selection = self.getSel())) {
13176					if (selection.rangeCount > 0) {
13177						rng = selection.getRangeAt(0);
13178					} else {
13179						rng = selection.createRange ? selection.createRange() : doc.createRange();
13180					}
13181				}
13182			} catch (ex) {
13183				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
13184			}
13185
13186			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
13187			if (isIE && rng && rng.setStart) {
13188				try {
13189					// IE will sometimes throw an exception here
13190					ieRng = doc.selection.createRange();
13191				} catch (ex) {
13192
13193				}
13194
13195				if (ieRng && ieRng.item) {
13196					elm = ieRng.item(0);
13197					rng = doc.createRange();
13198					rng.setStartBefore(elm);
13199					rng.setEndAfter(elm);
13200				}
13201			}
13202
13203			// No range found then create an empty one
13204			// This can occur when the editor is placed in a hidden container element on Gecko
13205			// Or on IE when there was an exception
13206			if (!rng) {
13207				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
13208			}
13209
13210			// If range is at start of document then move it to start of body
13211			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
13212				elm = self.dom.getRoot();
13213				rng.setStart(elm, 0);
13214				rng.setEnd(elm, 0);
13215			}
13216
13217			if (self.selectedRange && self.explicitRange) {
13218				if (rng.compareBoundaryPoints(rng.START_TO_START, self.selectedRange) === 0 &&
13219					rng.compareBoundaryPoints(rng.END_TO_END, self.selectedRange) === 0) {
13220					// Safari, Opera and Chrome only ever select text which causes the range to change.
13221					// This lets us use the originally set range if the selection hasn't been changed by the user.
13222					rng = self.explicitRange;
13223				} else {
13224					self.selectedRange = null;
13225					self.explicitRange = null;
13226				}
13227			}
13228
13229			return rng;
13230		},
13231
13232		/**
13233		 * Changes the selection to the specified DOM range.
13234		 *
13235		 * @method setRng
13236		 * @param {Range} rng Range to select.
13237		 */
13238		setRng: function(rng, forward) {
13239			var self = this, sel;
13240
13241			// Is IE specific range
13242			if (rng.select) {
13243				try {
13244					rng.select();
13245				} catch (ex) {
13246					// Needed for some odd IE bug #1843306
13247				}
13248
13249				return;
13250			}
13251
13252			if (!self.tridentSel) {
13253				sel = self.getSel();
13254
13255				if (sel) {
13256					self.explicitRange = rng;
13257
13258					try {
13259						sel.removeAllRanges();
13260						sel.addRange(rng);
13261					} catch (ex) {
13262						// IE might throw errors here if the editor is within a hidden container and selection is changed
13263					}
13264
13265					// Forward is set to false and we have an extend function
13266					if (forward === false && sel.extend) {
13267						sel.collapse(rng.endContainer, rng.endOffset);
13268						sel.extend(rng.startContainer, rng.startOffset);
13269					}
13270
13271					// adding range isn't always successful so we need to check range count otherwise an exception can occur
13272					self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
13273				}
13274			} else {
13275				// Is W3C Range fake range on IE
13276				if (rng.cloneRange) {
13277					try {
13278						self.tridentSel.addRange(rng);
13279						return;
13280					} catch (ex) {
13281						//IE9 throws an error here if called before selection is placed in the editor
13282					}
13283				}
13284			}
13285		},
13286
13287		/**
13288		 * Sets the current selection to the specified DOM element.
13289		 *
13290		 * @method setNode
13291		 * @param {Element} elm Element to set as the contents of the selection.
13292		 * @return {Element} Returns the element that got passed in.
13293		 * @example
13294		 * // Inserts a DOM node at current selection/caret location
13295		 * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'}));
13296		 */
13297		setNode: function(elm) {
13298			var self = this;
13299
13300			self.setContent(self.dom.getOuterHTML(elm));
13301
13302			return elm;
13303		},
13304
13305		/**
13306		 * Returns the currently selected element or the common ancestor element for both start and end of the selection.
13307		 *
13308		 * @method getNode
13309		 * @return {Element} Currently selected element or common ancestor element.
13310		 * @example
13311		 * // Alerts the currently selected elements node name
13312		 * alert(tinymce.activeEditor.selection.getNode().nodeName);
13313		 */
13314		getNode: function() {
13315			var self = this, rng = self.getRng(), elm;
13316			var startContainer = rng.startContainer, endContainer = rng.endContainer;
13317			var startOffset = rng.startOffset, endOffset = rng.endOffset, root = self.dom.getRoot();
13318
13319			function skipEmptyTextNodes(node, forwards) {
13320				var orig = node;
13321
13322				while (node && node.nodeType === 3 && node.length === 0) {
13323					node = forwards ? node.nextSibling : node.previousSibling;
13324				}
13325
13326				return node || orig;
13327			}
13328
13329			// Range maybe lost after the editor is made visible again
13330			if (!rng) {
13331				return root;
13332			}
13333
13334			if (rng.setStart) {
13335				elm = rng.commonAncestorContainer;
13336
13337				// Handle selection a image or other control like element such as anchors
13338				if (!rng.collapsed) {
13339					if (startContainer == endContainer) {
13340						if (endOffset - startOffset < 2) {
13341							if (startContainer.hasChildNodes()) {
13342								elm = startContainer.childNodes[startOffset];
13343							}
13344						}
13345					}
13346
13347					// If the anchor node is a element instead of a text node then return this element
13348					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
13349					//	return sel.anchorNode.childNodes[sel.anchorOffset];
13350
13351					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
13352					// This happens when you double click an underlined word in FireFox.
13353					if (startContainer.nodeType === 3 && endContainer.nodeType === 3) {
13354						if (startContainer.length === startOffset) {
13355							startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
13356						} else {
13357							startContainer = startContainer.parentNode;
13358						}
13359
13360						if (endOffset === 0) {
13361							endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
13362						} else {
13363							endContainer = endContainer.parentNode;
13364						}
13365
13366						if (startContainer && startContainer === endContainer) {
13367							return startContainer;
13368						}
13369					}
13370				}
13371
13372				if (elm && elm.nodeType == 3) {
13373					return elm.parentNode;
13374				}
13375
13376				return elm;
13377			}
13378
13379			elm = rng.item ? rng.item(0) : rng.parentElement();
13380
13381			// IE 7 might return elements outside the iframe
13382			if (elm.ownerDocument !== self.win.document) {
13383				elm = root;
13384			}
13385
13386			return elm;
13387		},
13388
13389		getSelectedBlocks: function(startElm, endElm) {
13390			var self = this, dom = self.dom, node, root, selectedBlocks = [];
13391
13392			root = dom.getRoot();
13393			startElm = dom.getParent(startElm || self.getStart(), dom.isBlock);
13394			endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock);
13395
13396			if (startElm && startElm != root) {
13397				selectedBlocks.push(startElm);
13398			}
13399
13400			if (startElm && endElm && startElm != endElm) {
13401				node = startElm;
13402
13403				var walker = new TreeWalker(startElm, root);
13404				while ((node = walker.next()) && node != endElm) {
13405					if (dom.isBlock(node)) {
13406						selectedBlocks.push(node);
13407					}
13408				}
13409			}
13410
13411			if (endElm && startElm != endElm && endElm != root) {
13412				selectedBlocks.push(endElm);
13413			}
13414
13415			return selectedBlocks;
13416		},
13417
13418		isForward: function(){
13419			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
13420
13421			// No support for selection direction then always return true
13422			if (!sel || !sel.anchorNode || !sel.focusNode) {
13423				return true;
13424			}
13425
13426			anchorRange = dom.createRng();
13427			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
13428			anchorRange.collapse(true);
13429
13430			focusRange = dom.createRng();
13431			focusRange.setStart(sel.focusNode, sel.focusOffset);
13432			focusRange.collapse(true);
13433
13434			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
13435		},
13436
13437		normalize: function() {
13438			var self = this, rng, normalized, collapsed;
13439
13440			function normalizeEndPoint(start) {
13441				var container, offset, walker, dom = self.dom, body = dom.getRoot(), node, nonEmptyElementsMap, nodeName;
13442				var directionLeft;
13443
13444				function hasBrBeforeAfter(node, left) {
13445					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
13446
13447					while ((node = walker[left ? 'prev' : 'next']())) {
13448						if (node.nodeName === "BR") {
13449							return true;
13450						}
13451					}
13452				}
13453
13454				function isPrevNode(node, name) {
13455					return node.previousSibling && node.previousSibling.nodeName == name;
13456				}
13457
13458				// Walks the dom left/right to find a suitable text node to move the endpoint into
13459				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
13460				function findTextNodeRelative(left, startNode) {
13461					var walker, lastInlineElement;
13462
13463					startNode = startNode || container;
13464					walker = new TreeWalker(startNode, dom.getParent(startNode.parentNode, dom.isBlock) || body);
13465
13466					// Walk left until we hit a text node we can move to or a block/br/img
13467					while ((node = walker[left ? 'prev' : 'next']())) {
13468						// Found text node that has a length
13469						if (node.nodeType === 3 && node.nodeValue.length > 0) {
13470							container = node;
13471							offset = left ? node.nodeValue.length : 0;
13472							normalized = true;
13473							return;
13474						}
13475
13476						// Break if we find a block or a BR/IMG/INPUT etc
13477						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
13478							return;
13479						}
13480
13481						lastInlineElement = node;
13482					}
13483
13484					// Only fetch the last inline element when in caret mode for now
13485					if (collapsed && lastInlineElement) {
13486						container = lastInlineElement;
13487						normalized = true;
13488						offset = 0;
13489					}
13490				}
13491
13492				container = rng[(start ? 'start' : 'end') + 'Container'];
13493				offset = rng[(start ? 'start' : 'end') + 'Offset'];
13494				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
13495				directionLeft = start;
13496
13497				if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
13498					directionLeft = false;
13499				}
13500
13501				// If the container is a document move it to the body element
13502				if (container.nodeType === 9) {
13503					container = dom.getRoot();
13504					offset = 0;
13505				}
13506
13507				// If the container is body try move it into the closest text node or position
13508				if (container === body) {
13509					// If start is before/after a image, table etc
13510					if (directionLeft) {
13511						node = container.childNodes[offset > 0 ? offset - 1 : 0];
13512						if (node) {
13513							nodeName = node.nodeName.toLowerCase();
13514							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
13515								return;
13516							}
13517						}
13518					}
13519
13520					// Resolve the index
13521					if (container.hasChildNodes()) {
13522						offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
13523						container = container.childNodes[offset];
13524						offset = 0;
13525
13526						// Don't walk into elements that doesn't have any child nodes like a IMG
13527						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
13528							// Walk the DOM to find a text node to place the caret at or a BR
13529							node = container;
13530							walker = new TreeWalker(container, body);
13531
13532							do {
13533								// Found a text node use that position
13534								if (node.nodeType === 3 && node.nodeValue.length > 0) {
13535									offset = directionLeft ? 0 : node.nodeValue.length;
13536									container = node;
13537									normalized = true;
13538									break;
13539								}
13540
13541								// Found a BR/IMG element that we can place the caret before
13542								if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
13543									offset = dom.nodeIndex(node);
13544									container = node.parentNode;
13545
13546									// Put caret after image when moving the end point
13547									if (node.nodeName ==  "IMG" && !directionLeft) {
13548										offset++;
13549									}
13550
13551									normalized = true;
13552									break;
13553								}
13554							} while ((node = (directionLeft ? walker.next() : walker.prev())));
13555						}
13556					}
13557				}
13558
13559				// Lean the caret to the left if possible
13560				if (collapsed) {
13561					// So this: <b>x</b><i>|x</i>
13562					// Becomes: <b>x|</b><i>x</i>
13563					// Seems that only gecko has issues with this
13564					if (container.nodeType === 3 && offset === 0) {
13565						findTextNodeRelative(true);
13566					}
13567
13568					// Lean left into empty inline elements when the caret is before a BR
13569					// So this: <i><b></b><i>|<br></i>
13570					// Becomes: <i><b>|</b><i><br></i>
13571					// Seems that only gecko has issues with this.
13572					// Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p>
13573					if (container.nodeType === 1) {
13574						node = container.childNodes[offset];
13575						if(node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
13576							!hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
13577							findTextNodeRelative(true, container.childNodes[offset]);
13578						}
13579					}
13580				}
13581
13582				// Lean the start of the selection right if possible
13583				// So this: x[<b>x]</b>
13584				// Becomes: x<b>[x]</b>
13585				if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
13586					findTextNodeRelative(false);
13587				}
13588
13589				// Set endpoint if it was normalized
13590				if (normalized) {
13591					rng['set' + (start ? 'Start' : 'End')](container, offset);
13592				}
13593			}
13594
13595			// Normalize only on non IE browsers for now
13596			if (isIE) {
13597				return;
13598			}
13599
13600			rng = self.getRng();
13601			collapsed = rng.collapsed;
13602
13603			// Normalize the end points
13604			normalizeEndPoint(true);
13605
13606			if (!collapsed) {
13607				normalizeEndPoint();
13608			}
13609
13610			// Set the selection if it was normalized
13611			if (normalized) {
13612				// If it was collapsed then make sure it still is
13613				if (collapsed) {
13614					rng.collapse(true);
13615				}
13616
13617				//console.log(self.dom.dumpRng(rng));
13618				self.setRng(rng, self.isForward());
13619			}
13620		},
13621
13622		/**
13623		 * Executes callback of the current selection matches the specified selector or not and passes the state and args to the callback.
13624		 *
13625		 * @method selectorChanged
13626		 * @param {String} selector CSS selector to check for.
13627		 * @param {function} callback Callback with state and args when the selector is matches or not.
13628		 */
13629		selectorChanged: function(selector, callback) {
13630			var self = this, currentSelectors;
13631
13632			if (!self.selectorChangedData) {
13633				self.selectorChangedData = {};
13634				currentSelectors = {};
13635
13636				self.editor.on('NodeChange', function(e) {
13637					var node = e.element, dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
13638
13639					// Check for new matching selectors
13640					each(self.selectorChangedData, function(callbacks, selector) {
13641						each(parents, function(node) {
13642							if (dom.is(node, selector)) {
13643								if (!currentSelectors[selector]) {
13644									// Execute callbacks
13645									each(callbacks, function(callback) {
13646										callback(true, {node: node, selector: selector, parents: parents});
13647									});
13648
13649									currentSelectors[selector] = callbacks;
13650								}
13651
13652								matchedSelectors[selector] = callbacks;
13653								return false;
13654							}
13655						});
13656					});
13657
13658					// Check if current selectors still match
13659					each(currentSelectors, function(callbacks, selector) {
13660						if (!matchedSelectors[selector]) {
13661							delete currentSelectors[selector];
13662
13663							each(callbacks, function(callback) {
13664								callback(false, {node: node, selector: selector, parents: parents});
13665							});
13666						}
13667					});
13668				});
13669			}
13670
13671			// Add selector listeners
13672			if (!self.selectorChangedData[selector]) {
13673				self.selectorChangedData[selector] = [];
13674			}
13675
13676			self.selectorChangedData[selector].push(callback);
13677
13678			return self;
13679		},
13680
13681		getScrollContainer: function() {
13682			var scrollContainer, node = this.dom.getRoot();
13683
13684			while (node && node.nodeName != 'BODY') {
13685				if (node.scrollHeight > node.clientHeight) {
13686					scrollContainer = node;
13687					break;
13688				}
13689
13690				node = node.parentNode;
13691			}
13692
13693			return scrollContainer;
13694		},
13695
13696		scrollIntoView: function(elm) {
13697			var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH;
13698
13699			function getPos(elm) {
13700				var x = 0, y = 0;
13701
13702				var offsetParent = elm;
13703				while (offsetParent && offsetParent.nodeType) {
13704					x += offsetParent.offsetLeft || 0;
13705					y += offsetParent.offsetTop || 0;
13706					offsetParent = offsetParent.offsetParent;
13707				}
13708
13709				return {x: x, y: y};
13710			}
13711
13712			if (root.nodeName != 'BODY') {
13713				var scrollContainer = self.getScrollContainer();
13714				if (scrollContainer) {
13715					y = getPos(elm).y - getPos(scrollContainer).y;
13716					viewPortH = scrollContainer.clientHeight;
13717					viewPortY = scrollContainer.scrollTop;
13718					if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
13719						scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
13720					}
13721
13722					return;
13723				}
13724			}
13725
13726			viewPort = dom.getViewPort(self.editor.getWin());
13727			y = dom.getPos(elm).y;
13728			viewPortY = viewPort.y;
13729			viewPortH = viewPort.h;
13730			if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
13731				self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
13732			}
13733		},
13734
13735		_moveEndPoint: function(rng, node, start) {
13736			var root = node, walker = new TreeWalker(node, root);
13737			var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
13738
13739			do {
13740				// Text node
13741				if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
13742					if (start) {
13743						rng.setStart(node, 0);
13744					} else {
13745						rng.setEnd(node, node.nodeValue.length);
13746					}
13747
13748					return;
13749				}
13750
13751				// BR/IMG/INPUT elements
13752				if (nonEmptyElementsMap[node.nodeName]) {
13753					if (start) {
13754						rng.setStartBefore(node);
13755					} else {
13756						if (node.nodeName == 'BR') {
13757							rng.setEndBefore(node);
13758						} else {
13759							rng.setEndAfter(node);
13760						}
13761					}
13762
13763					return;
13764				}
13765
13766				// Found empty text block old IE can place the selection inside those
13767				if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
13768					if (start) {
13769						rng.setStart(node, 0);
13770					} else {
13771						rng.setEnd(node, 0);
13772					}
13773
13774					return;
13775				}
13776			} while ((node = (start ? walker.next() : walker.prev())));
13777
13778			// Failed to find any text node or other suitable location then move to the root of body
13779			if (root.nodeName == 'BODY') {
13780				if (start) {
13781					rng.setStart(root, 0);
13782				} else {
13783					rng.setEnd(root, root.childNodes.length);
13784				}
13785			}
13786		},
13787
13788		destroy: function() {
13789			this.win = null;
13790			this.controlSelection.destroy();
13791		}
13792	};
13793
13794	return Selection;
13795});
13796
13797// Included from: js/tinymce/classes/dom/RangeUtils.js
13798
13799/**
13800 * Range.js
13801 *
13802 * Copyright, Moxiecode Systems AB
13803 * Released under LGPL License.
13804 *
13805 * License: http://www.tinymce.com/license
13806 * Contributing: http://www.tinymce.com/contributing
13807 */
13808
13809/**
13810 * RangeUtils
13811 *
13812 * @class tinymce.dom.RangeUtils
13813 * @private
13814 */
13815define("tinymce/dom/RangeUtils", [
13816	"tinymce/util/Tools"
13817], function(Tools) {
13818	var each = Tools.each;
13819
13820	function RangeUtils(dom) {
13821		/**
13822		 * Walks the specified range like object and executes the callback for each sibling collection it finds.
13823		 *
13824		 * @method walk
13825		 * @param {Object} rng Range like object.
13826		 * @param {function} callback Callback function to execute for each sibling collection.
13827		 */
13828		this.walk = function(rng, callback) {
13829			var startContainer = rng.startContainer,
13830				startOffset = rng.startOffset,
13831				endContainer = rng.endContainer,
13832				endOffset = rng.endOffset,
13833				ancestor, startPoint,
13834				endPoint, node, parent, siblings, nodes;
13835
13836			// Handle table cell selection the table plugin enables
13837			// you to fake select table cells and perform formatting actions on them
13838			nodes = dom.select('td.mce-item-selected,th.mce-item-selected');
13839			if (nodes.length > 0) {
13840				each(nodes, function(node) {
13841					callback([node]);
13842				});
13843
13844				return;
13845			}
13846
13847			/**
13848			 * Excludes start/end text node if they are out side the range
13849			 *
13850			 * @private
13851			 * @param {Array} nodes Nodes to exclude items from.
13852			 * @return {Array} Array with nodes excluding the start/end container if needed.
13853			 */
13854			function exclude(nodes) {
13855				var node;
13856
13857				// First node is excluded
13858				node = nodes[0];
13859				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
13860					nodes.splice(0, 1);
13861				}
13862
13863				// Last node is excluded
13864				node = nodes[nodes.length - 1];
13865				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
13866					nodes.splice(nodes.length - 1, 1);
13867				}
13868
13869				return nodes;
13870			}
13871
13872			/**
13873			 * Collects siblings
13874			 *
13875			 * @private
13876			 * @param {Node} node Node to collect siblings from.
13877			 * @param {String} name Name of the sibling to check for.
13878			 * @return {Array} Array of collected siblings.
13879			 */
13880			function collectSiblings(node, name, end_node) {
13881				var siblings = [];
13882
13883				for (; node && node != end_node; node = node[name]) {
13884					siblings.push(node);
13885				}
13886
13887				return siblings;
13888			}
13889
13890			/**
13891			 * Find an end point this is the node just before the common ancestor root.
13892			 *
13893			 * @private
13894			 * @param {Node} node Node to start at.
13895			 * @param {Node} root Root/ancestor element to stop just before.
13896			 * @return {Node} Node just before the root element.
13897			 */
13898			function findEndPoint(node, root) {
13899				do {
13900					if (node.parentNode == root) {
13901						return node;
13902					}
13903
13904					node = node.parentNode;
13905				} while(node);
13906			}
13907
13908			function walkBoundary(start_node, end_node, next) {
13909				var siblingName = next ? 'nextSibling' : 'previousSibling';
13910
13911				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
13912					parent = node.parentNode;
13913					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
13914
13915					if (siblings.length) {
13916						if (!next) {
13917							siblings.reverse();
13918						}
13919
13920						callback(exclude(siblings));
13921					}
13922				}
13923			}
13924
13925			// If index based start position then resolve it
13926			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
13927				startContainer = startContainer.childNodes[startOffset];
13928			}
13929
13930			// If index based end position then resolve it
13931			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
13932				endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
13933			}
13934
13935			// Same container
13936			if (startContainer == endContainer) {
13937				return callback(exclude([startContainer]));
13938			}
13939
13940			// Find common ancestor and end points
13941			ancestor = dom.findCommonAncestor(startContainer, endContainer);
13942
13943			// Process left side
13944			for (node = startContainer; node; node = node.parentNode) {
13945				if (node === endContainer) {
13946					return walkBoundary(startContainer, ancestor, true);
13947				}
13948
13949				if (node === ancestor) {
13950					break;
13951				}
13952			}
13953
13954			// Process right side
13955			for (node = endContainer; node; node = node.parentNode) {
13956				if (node === startContainer) {
13957					return walkBoundary(endContainer, ancestor);
13958				}
13959
13960				if (node === ancestor) {
13961					break;
13962				}
13963			}
13964
13965			// Find start/end point
13966			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
13967			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
13968
13969			// Walk left leaf
13970			walkBoundary(startContainer, startPoint, true);
13971
13972			// Walk the middle from start to end point
13973			siblings = collectSiblings(
13974				startPoint == startContainer ? startPoint : startPoint.nextSibling,
13975				'nextSibling',
13976				endPoint == endContainer ? endPoint.nextSibling : endPoint
13977			);
13978
13979			if (siblings.length) {
13980				callback(exclude(siblings));
13981			}
13982
13983			// Walk right leaf
13984			walkBoundary(endContainer, endPoint);
13985		};
13986
13987		/**
13988		 * Splits the specified range at it's start/end points.
13989		 *
13990		 * @private
13991		 * @param {Range/RangeObject} rng Range to split.
13992		 * @return {Object} Range position object.
13993		 */
13994		this.split = function(rng) {
13995			var startContainer = rng.startContainer,
13996				startOffset = rng.startOffset,
13997				endContainer = rng.endContainer,
13998				endOffset = rng.endOffset;
13999
14000			function splitText(node, offset) {
14001				return node.splitText(offset);
14002			}
14003
14004			// Handle single text node
14005			if (startContainer == endContainer && startContainer.nodeType == 3) {
14006				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
14007					endContainer = splitText(startContainer, startOffset);
14008					startContainer = endContainer.previousSibling;
14009
14010					if (endOffset > startOffset) {
14011						endOffset = endOffset - startOffset;
14012						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
14013						endOffset = endContainer.nodeValue.length;
14014						startOffset = 0;
14015					} else {
14016						endOffset = 0;
14017					}
14018				}
14019			} else {
14020				// Split startContainer text node if needed
14021				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
14022					startContainer = splitText(startContainer, startOffset);
14023					startOffset = 0;
14024				}
14025
14026				// Split endContainer text node if needed
14027				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
14028					endContainer = splitText(endContainer, endOffset).previousSibling;
14029					endOffset = endContainer.nodeValue.length;
14030				}
14031			}
14032
14033			return {
14034				startContainer: startContainer,
14035				startOffset: startOffset,
14036				endContainer: endContainer,
14037				endOffset: endOffset
14038			};
14039		};
14040	}
14041
14042	/**
14043	 * Compares two ranges and checks if they are equal.
14044	 *
14045	 * @static
14046	 * @method compareRanges
14047	 * @param {DOMRange} rng1 First range to compare.
14048	 * @param {DOMRange} rng2 First range to compare.
14049	 * @return {Boolean} true/false if the ranges are equal.
14050	 */
14051	RangeUtils.compareRanges = function(rng1, rng2) {
14052		if (rng1 && rng2) {
14053			// Compare native IE ranges
14054			if (rng1.item || rng1.duplicate) {
14055				// Both are control ranges and the selected element matches
14056				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
14057					return true;
14058				}
14059
14060				// Both are text ranges and the range matches
14061				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
14062					return true;
14063				}
14064			} else {
14065				// Compare w3c ranges
14066				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
14067			}
14068		}
14069
14070		return false;
14071	};
14072
14073	return RangeUtils;
14074});
14075
14076// Included from: js/tinymce/classes/Formatter.js
14077
14078/**
14079 * Formatter.js
14080 *
14081 * Copyright, Moxiecode Systems AB
14082 * Released under LGPL License.
14083 *
14084 * License: http://www.tinymce.com/license
14085 * Contributing: http://www.tinymce.com/contributing
14086 */
14087
14088/**
14089 * Text formatter engine class. This class is used to apply formats like bold, italic, font size
14090 * etc to the current selection or specific nodes. This engine was build to replace the browsers
14091 * default formatting logic for execCommand due to it's inconsistent and buggy behavior.
14092 *
14093 * @class tinymce.Formatter
14094 * @example
14095 *  tinymce.activeEditor.formatter.register('mycustomformat', {
14096 *    inline: 'span',
14097 *    styles: {color: '#ff0000'}
14098 *  });
14099 *
14100 *  tinymce.activeEditor.formatter.apply('mycustomformat');
14101 */
14102define("tinymce/Formatter", [
14103	"tinymce/dom/TreeWalker",
14104	"tinymce/dom/RangeUtils",
14105	"tinymce/util/Tools"
14106], function(TreeWalker, RangeUtils, Tools) {
14107	/**
14108	 * Constructs a new formatter instance.
14109	 *
14110	 * @constructor Formatter
14111	 * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to.
14112	 */
14113	return function(ed) {
14114		var formats = {},
14115			dom = ed.dom,
14116			selection = ed.selection,
14117			rangeUtils = new RangeUtils(dom),
14118			isValid = ed.schema.isValidChild,
14119			isBlock = dom.isBlock,
14120			forcedRootBlock = ed.settings.forced_root_block,
14121			nodeIndex = dom.nodeIndex,
14122			INVISIBLE_CHAR = '\uFEFF',
14123			MCE_ATTR_RE = /^(src|href|style)$/,
14124			FALSE = false,
14125			TRUE = true,
14126			formatChangeData,
14127			undef,
14128			getContentEditable = dom.getContentEditable,
14129			disableCaretContainer,
14130			markCaretContainersBogus;
14131
14132		var each = Tools.each,
14133			grep = Tools.grep,
14134			walk = Tools.walk,
14135			extend = Tools.extend;
14136
14137		function isTextBlock(name) {
14138			if (name.nodeType) {
14139				name = name.nodeName;
14140			}
14141
14142			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
14143		}
14144
14145		function getParents(node, selector) {
14146			return dom.getParents(node, selector, dom.getRoot());
14147		}
14148
14149		function isCaretNode(node) {
14150			return node.nodeType === 1 && node.id === '_mce_caret';
14151		}
14152
14153		function defaultFormats() {
14154			register({
14155				alignleft: [
14156					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'left'}, defaultBlock: 'div'},
14157					{selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
14158				],
14159
14160				aligncenter: [
14161					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'center'}, defaultBlock: 'div'},
14162					{selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
14163					{selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
14164				],
14165
14166				alignright: [
14167					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'right'}, defaultBlock: 'div'},
14168					{selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
14169				],
14170
14171				alignjustify: [
14172					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'justify'}, defaultBlock: 'div'}
14173				],
14174
14175				bold: [
14176					{inline: 'strong', remove: 'all'},
14177					{inline: 'span', styles: {fontWeight: 'bold'}},
14178					{inline: 'b', remove: 'all'}
14179				],
14180
14181				italic: [
14182					{inline: 'em', remove: 'all'},
14183					{inline: 'span', styles: {fontStyle: 'italic'}},
14184					{inline: 'i', remove: 'all'}
14185				],
14186
14187				underline: [
14188					{inline: 'span', styles: {textDecoration: 'underline'}, exact: true},
14189					{inline: 'u', remove: 'all'}
14190				],
14191
14192				strikethrough: [
14193					{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
14194					{inline: 'strike', remove: 'all'}
14195				],
14196
14197				forecolor: {inline: 'span', styles: {color: '%value'}, wrap_links: false},
14198				hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, wrap_links: false},
14199				fontname: {inline: 'span', styles: {fontFamily: '%value'}},
14200				fontsize: {inline: 'span', styles: {fontSize: '%value'}},
14201				fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
14202				blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
14203				subscript: {inline: 'sub'},
14204				superscript: {inline: 'sup'},
14205				code: {inline: 'code'},
14206
14207				link: {inline: 'a', selector: 'a', remove: 'all', split: true, deep: true,
14208					onmatch: function() {
14209						return true;
14210					},
14211
14212					onformat: function(elm, fmt, vars) {
14213						each(vars, function(value, key) {
14214							dom.setAttrib(elm, key, value);
14215						});
14216					}
14217				},
14218
14219				removeformat: [
14220					{
14221						selector: 'b,strong,em,i,font,u,strike,sub,sup',
14222						remove: 'all',
14223						split: true,
14224						expand: false,
14225						block_expand: true,
14226						deep: true
14227					},
14228					{selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
14229					{selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
14230				]
14231			});
14232
14233			// Register default block formats
14234			each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) {
14235				register(name, {block: name, remove: 'all'});
14236			});
14237
14238			// Register user defined formats
14239			register(ed.settings.formats);
14240		}
14241
14242		function addKeyboardShortcuts() {
14243			// Add some inline shortcuts
14244			ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');
14245			ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');
14246			ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');
14247
14248			// BlockFormat shortcuts keys
14249			for (var i = 1; i <= 6; i++) {
14250				ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
14251			}
14252
14253			ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
14254			ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
14255			ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
14256		}
14257
14258		// Public functions
14259
14260		/**
14261		 * Returns the format by name or all formats if no name is specified.
14262		 *
14263		 * @method get
14264		 * @param {String} name Optional name to retrive by.
14265		 * @return {Array/Object} Array/Object with all registred formats or a specific format.
14266		 */
14267		function get(name) {
14268			return name ? formats[name] : formats;
14269		}
14270
14271		/**
14272		 * Registers a specific format by name.
14273		 *
14274		 * @method register
14275		 * @param {Object/String} name Name of the format for example "bold".
14276		 * @param {Object/Array} format Optional format object or array of format variants
14277		 * can only be omitted if the first arg is an object.
14278		 */
14279		function register(name, format) {
14280			if (name) {
14281				if (typeof(name) !== 'string') {
14282					each(name, function(format, name) {
14283						register(name, format);
14284					});
14285				} else {
14286					// Force format into array and add it to internal collection
14287					format = format.length ? format : [format];
14288
14289					each(format, function(format) {
14290						// Set deep to false by default on selector formats this to avoid removing
14291						// alignment on images inside paragraphs when alignment is changed on paragraphs
14292						if (format.deep === undef) {
14293							format.deep = !format.selector;
14294						}
14295
14296						// Default to true
14297						if (format.split === undef) {
14298							format.split = !format.selector || format.inline;
14299						}
14300
14301						// Default to true
14302						if (format.remove === undef && format.selector && !format.inline) {
14303							format.remove = 'none';
14304						}
14305
14306						// Mark format as a mixed format inline + block level
14307						if (format.selector && format.inline) {
14308							format.mixed = true;
14309							format.block_expand = true;
14310						}
14311
14312						// Split classes if needed
14313						if (typeof(format.classes) === 'string') {
14314							format.classes = format.classes.split(/\s+/);
14315						}
14316					});
14317
14318					formats[name] = format;
14319				}
14320			}
14321		}
14322
14323		function getTextDecoration(node) {
14324			var decoration;
14325
14326			ed.dom.getParent(node, function(n) {
14327				decoration = ed.dom.getStyle(n, 'text-decoration');
14328				return decoration && decoration !== 'none';
14329			});
14330
14331			return decoration;
14332		}
14333
14334		function processUnderlineAndColor(node) {
14335			var textDecoration;
14336			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
14337				textDecoration = getTextDecoration(node.parentNode);
14338				if (ed.dom.getStyle(node, 'color') && textDecoration) {
14339					ed.dom.setStyle(node, 'text-decoration', textDecoration);
14340				} else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {
14341					ed.dom.setStyle(node, 'text-decoration', null);
14342				}
14343			}
14344		}
14345
14346		/**
14347		 * Applies the specified format to the current selection or specified node.
14348		 *
14349		 * @method apply
14350		 * @param {String} name Name of format to apply.
14351		 * @param {Object} vars Optional list of variables to replace within format before applying it.
14352		 * @param {Node} node Optional node to apply the format to defaults to current selection.
14353		 */
14354		function apply(name, vars, node) {
14355			var formatList = get(name), format = formatList[0], bookmark, rng, isCollapsed = !node && selection.isCollapsed();
14356
14357			function setElementFormat(elm, fmt) {
14358				fmt = fmt || format;
14359
14360				if (elm) {
14361					if (fmt.onformat) {
14362						fmt.onformat(elm, fmt, vars, node);
14363					}
14364
14365					each(fmt.styles, function(value, name) {
14366						dom.setStyle(elm, name, replaceVars(value, vars));
14367					});
14368
14369					each(fmt.attributes, function(value, name) {
14370						dom.setAttrib(elm, name, replaceVars(value, vars));
14371					});
14372
14373					each(fmt.classes, function(value) {
14374						value = replaceVars(value, vars);
14375
14376						if (!dom.hasClass(elm, value)) {
14377							dom.addClass(elm, value);
14378						}
14379					});
14380				}
14381			}
14382
14383			function adjustSelectionToVisibleSelection() {
14384				function findSelectionEnd(start, end) {
14385					var walker = new TreeWalker(end);
14386					for (node = walker.current(); node; node = walker.prev()) {
14387						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
14388							return node;
14389						}
14390					}
14391				}
14392
14393				// Adjust selection so that a end container with a end offset of zero is not included in the selection
14394				// as this isn't visible to the user.
14395				var rng = ed.selection.getRng();
14396				var start = rng.startContainer;
14397				var end = rng.endContainer;
14398
14399				if (start != end && rng.endOffset === 0) {
14400					var newEnd = findSelectionEnd(start, end);
14401					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
14402
14403					rng.setEnd(newEnd, endOffset);
14404				}
14405
14406				return rng;
14407			}
14408
14409			function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){
14410				var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;
14411
14412				// find the index of the first child list.
14413				each(node.childNodes, function(n, index) {
14414					if (n.nodeName === "UL" || n.nodeName === "OL") {
14415						listIndex = index;
14416						list = n;
14417						return false;
14418					}
14419				});
14420
14421				// get the index of the bookmarks
14422				each(node.childNodes, function(n, index) {
14423					if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
14424						if (n.id == bookmark.id + "_start") {
14425							startIndex = index;
14426						} else if (n.id == bookmark.id + "_end") {
14427							endIndex = index;
14428						}
14429					}
14430				});
14431
14432				// if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally
14433				if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {
14434					each(grep(node.childNodes), process);
14435					return 0;
14436				} else {
14437					currentWrapElm = dom.clone(wrapElm, FALSE);
14438
14439					// create a list of the nodes on the same side of the list as the selection
14440					each(grep(node.childNodes), function(n, index) {
14441						if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
14442							nodes.push(n);
14443							n.parentNode.removeChild(n);
14444						}
14445					});
14446
14447					// insert the wrapping element either before or after the list.
14448					if (startIndex < listIndex) {
14449						node.insertBefore(currentWrapElm, list);
14450					} else if (startIndex > listIndex) {
14451						node.insertBefore(currentWrapElm, list.nextSibling);
14452					}
14453
14454					// add the new nodes to the list.
14455					newWrappers.push(currentWrapElm);
14456
14457					each(nodes, function(node) {
14458						currentWrapElm.appendChild(node);
14459					});
14460
14461					return currentWrapElm;
14462				}
14463			}
14464
14465			function applyRngStyle(rng, bookmark, node_specific) {
14466				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
14467
14468				// Setup wrapper element
14469				wrapName = format.inline || format.block;
14470				wrapElm = dom.create(wrapName);
14471				setElementFormat(wrapElm);
14472
14473				rangeUtils.walk(rng, function(nodes) {
14474					var currentWrapElm;
14475
14476					/**
14477					 * Process a list of nodes wrap them.
14478					 */
14479					function process(node) {
14480						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
14481
14482						lastContentEditable = contentEditable;
14483						nodeName = node.nodeName.toLowerCase();
14484						parentName = node.parentNode.nodeName.toLowerCase();
14485
14486						// Node has a contentEditable value
14487						if (node.nodeType === 1 && getContentEditable(node)) {
14488							lastContentEditable = contentEditable;
14489							contentEditable = getContentEditable(node) === "true";
14490							hasContentEditableState = true; // We don't want to wrap the container only it's children
14491						}
14492
14493						// Stop wrapping on br elements
14494						if (isEq(nodeName, 'br')) {
14495							currentWrapElm = 0;
14496
14497							// Remove any br elements when we wrap things
14498							if (format.block) {
14499								dom.remove(node);
14500							}
14501
14502							return;
14503						}
14504
14505						// If node is wrapper type
14506						if (format.wrapper && matchNode(node, name, vars)) {
14507							currentWrapElm = 0;
14508							return;
14509						}
14510
14511						// Can we rename the block
14512						// TODO: Break this if up, too complex
14513						if (contentEditable && !hasContentEditableState && format.block &&
14514							!format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) {
14515							node = dom.rename(node, wrapName);
14516							setElementFormat(node);
14517							newWrappers.push(node);
14518							currentWrapElm = 0;
14519							return;
14520						}
14521
14522						// Handle selector patterns
14523						if (format.selector) {
14524							// Look for matching formats
14525							each(formatList, function(format) {
14526								// Check collapsed state if it exists
14527								if ('collapsed' in format && format.collapsed !== isCollapsed) {
14528									return;
14529								}
14530
14531								if (dom.is(node, format.selector) && !isCaretNode(node)) {
14532									setElementFormat(node, format);
14533									found = true;
14534								}
14535							});
14536
14537							// Continue processing if a selector match wasn't found and a inline element is defined
14538							if (!format.inline || found) {
14539								currentWrapElm = 0;
14540								return;
14541							}
14542						}
14543
14544						// Is it valid to wrap this item
14545						// TODO: Break this if up, too complex
14546						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
14547								!(!node_specific && node.nodeType === 3 &&
14548								node.nodeValue.length === 1 &&
14549								node.nodeValue.charCodeAt(0) === 65279) &&
14550								!isCaretNode(node) &&
14551								(!format.inline || !isBlock(node))) {
14552							// Start wrapping
14553							if (!currentWrapElm) {
14554								// Wrap the node
14555								currentWrapElm = dom.clone(wrapElm, FALSE);
14556								node.parentNode.insertBefore(currentWrapElm, node);
14557								newWrappers.push(currentWrapElm);
14558							}
14559
14560							currentWrapElm.appendChild(node);
14561						} else if (nodeName == 'li' && bookmark) {
14562							// Start wrapping - if we are in a list node and have a bookmark, then
14563							// we will always begin by wrapping in a new element.
14564							currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);
14565						} else {
14566							// Start a new wrapper for possible children
14567							currentWrapElm = 0;
14568
14569							each(grep(node.childNodes), process);
14570
14571							if (hasContentEditableState) {
14572								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
14573							}
14574
14575							// End the last wrapper
14576							currentWrapElm = 0;
14577						}
14578					}
14579
14580					// Process siblings from range
14581					each(nodes, process);
14582				});
14583
14584				// Wrap links inside as well, for example color inside a link when the wrapper is around the link
14585				if (format.wrap_links === false) {
14586					each(newWrappers, function(node) {
14587						function process(node) {
14588							var i, currentWrapElm, children;
14589
14590							if (node.nodeName === 'A') {
14591								currentWrapElm = dom.clone(wrapElm, FALSE);
14592								newWrappers.push(currentWrapElm);
14593
14594								children = grep(node.childNodes);
14595								for (i = 0; i < children.length; i++) {
14596									currentWrapElm.appendChild(children[i]);
14597								}
14598
14599								node.appendChild(currentWrapElm);
14600							}
14601
14602							each(grep(node.childNodes), process);
14603						}
14604
14605						process(node);
14606					});
14607				}
14608
14609				// Cleanup
14610				each(newWrappers, function(node) {
14611					var childCount;
14612
14613					function getChildCount(node) {
14614						var count = 0;
14615
14616						each(node.childNodes, function(node) {
14617							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) {
14618								count++;
14619							}
14620						});
14621
14622						return count;
14623					}
14624
14625					function mergeStyles(node) {
14626						var child, clone;
14627
14628						each(node.childNodes, function(node) {
14629							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
14630								child = node;
14631								return FALSE; // break loop
14632							}
14633						});
14634
14635						// If child was found and of the same type as the current node
14636						if (child && matchName(child, format)) {
14637							clone = dom.clone(child, FALSE);
14638							setElementFormat(clone);
14639
14640							dom.replace(clone, node, TRUE);
14641							dom.remove(child, 1);
14642						}
14643
14644						return clone || node;
14645					}
14646
14647					childCount = getChildCount(node);
14648
14649					// Remove empty nodes but only if there is multiple wrappers and they are not block
14650					// elements so never remove single <h1></h1> since that would remove the
14651					// currrent empty block element where the caret is at
14652					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
14653						dom.remove(node, 1);
14654						return;
14655					}
14656
14657					if (format.inline || format.wrapper) {
14658						// Merges the current node with it's children of similar type to reduce the number of elements
14659						if (!format.exact && childCount === 1) {
14660							node = mergeStyles(node);
14661						}
14662
14663						// Remove/merge children
14664						each(formatList, function(format) {
14665							// Merge all children of similar type will move styles from child to parent
14666							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
14667							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
14668							each(dom.select(format.inline, node), function(child) {
14669								var parent;
14670
14671								// When wrap_links is set to false we don't want
14672								// to remove the format on children within links
14673								if (format.wrap_links === false) {
14674									parent = child.parentNode;
14675
14676									do {
14677										if (parent.nodeName === 'A') {
14678											return;
14679										}
14680									} while ((parent = parent.parentNode));
14681								}
14682
14683								removeFormat(format, vars, child, format.exact ? child : null);
14684							});
14685						});
14686
14687						// Remove child if direct parent is of same type
14688						if (matchNode(node.parentNode, name, vars)) {
14689							dom.remove(node, 1);
14690							node = 0;
14691							return TRUE;
14692						}
14693
14694						// Look for parent with similar style format
14695						if (format.merge_with_parents) {
14696							dom.getParent(node.parentNode, function(parent) {
14697								if (matchNode(parent, name, vars)) {
14698									dom.remove(node, 1);
14699									node = 0;
14700									return TRUE;
14701								}
14702							});
14703						}
14704
14705						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
14706						if (node && format.merge_siblings !== false) {
14707							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
14708							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
14709						}
14710					}
14711				});
14712			}
14713
14714			if (format) {
14715				if (node) {
14716					if (node.nodeType) {
14717						rng = dom.createRng();
14718						rng.setStartBefore(node);
14719						rng.setEndAfter(node);
14720						applyRngStyle(expandRng(rng, formatList), null, true);
14721					} else {
14722						applyRngStyle(node, null, true);
14723					}
14724				} else {
14725					if (!isCollapsed || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
14726						// Obtain selection node before selection is unselected by applyRngStyle()
14727						var curSelNode = ed.selection.getNode();
14728
14729						// If the formats have a default block and we can't find a parent block then
14730						// start wrapping it with a DIV this is for forced_root_blocks: false
14731						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
14732						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
14733							apply(formatList[0].defaultBlock);
14734						}
14735
14736						// Apply formatting to selection
14737						ed.selection.setRng(adjustSelectionToVisibleSelection());
14738						bookmark = selection.getBookmark();
14739						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
14740
14741						// Colored nodes should be underlined so that the color of the underline matches the text color.
14742						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
14743							walk(curSelNode, processUnderlineAndColor, 'childNodes');
14744							processUnderlineAndColor(curSelNode);
14745						}
14746
14747						selection.moveToBookmark(bookmark);
14748						moveStart(selection.getRng(TRUE));
14749						ed.nodeChanged();
14750					} else {
14751						performCaretAction('apply', name, vars);
14752					}
14753				}
14754			}
14755		}
14756
14757		/**
14758		 * Removes the specified format from the current selection or specified node.
14759		 *
14760		 * @method remove
14761		 * @param {String} name Name of format to remove.
14762		 * @param {Object} vars Optional list of variables to replace within format before removing it.
14763		 * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
14764		 */
14765		function remove(name, vars, node) {
14766			var formatList = get(name), format = formatList[0], bookmark, rng, contentEditable = true;
14767
14768			// Merges the styles for each node
14769			function process(node) {
14770				var children, i, l, lastContentEditable, hasContentEditableState;
14771
14772				// Node has a contentEditable value
14773				if (node.nodeType === 1 && getContentEditable(node)) {
14774					lastContentEditable = contentEditable;
14775					contentEditable = getContentEditable(node) === "true";
14776					hasContentEditableState = true; // We don't want to wrap the container only it's children
14777				}
14778
14779				// Grab the children first since the nodelist might be changed
14780				children = grep(node.childNodes);
14781
14782				// Process current node
14783				if (contentEditable && !hasContentEditableState) {
14784					for (i = 0, l = formatList.length; i < l; i++) {
14785						if (removeFormat(formatList[i], vars, node, node)) {
14786							break;
14787						}
14788					}
14789				}
14790
14791				// Process the children
14792				if (format.deep) {
14793					if (children.length) {
14794						for (i = 0, l = children.length; i < l; i++) {
14795							process(children[i]);
14796						}
14797
14798						if (hasContentEditableState) {
14799							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
14800						}
14801					}
14802				}
14803			}
14804
14805			function findFormatRoot(container) {
14806				var formatRoot;
14807
14808				// Find format root
14809				each(getParents(container.parentNode).reverse(), function(parent) {
14810					var format;
14811
14812					// Find format root element
14813					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
14814						// Is the node matching the format we are looking for
14815						format = matchNode(parent, name, vars);
14816						if (format && format.split !== false) {
14817							formatRoot = parent;
14818						}
14819					}
14820				});
14821
14822				return formatRoot;
14823			}
14824
14825			function wrapAndSplit(format_root, container, target, split) {
14826				var parent, clone, lastClone, firstClone, i, formatRootParent;
14827
14828				// Format root found then clone formats and split it
14829				if (format_root) {
14830					formatRootParent = format_root.parentNode;
14831
14832					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
14833						clone = dom.clone(parent, FALSE);
14834
14835						for (i = 0; i < formatList.length; i++) {
14836							if (removeFormat(formatList[i], vars, clone, clone)) {
14837								clone = 0;
14838								break;
14839							}
14840						}
14841
14842						// Build wrapper node
14843						if (clone) {
14844							if (lastClone) {
14845								clone.appendChild(lastClone);
14846							}
14847
14848							if (!firstClone) {
14849								firstClone = clone;
14850							}
14851
14852							lastClone = clone;
14853						}
14854					}
14855
14856					// Never split block elements if the format is mixed
14857					if (split && (!format.mixed || !isBlock(format_root))) {
14858						container = dom.split(format_root, container);
14859					}
14860
14861					// Wrap container in cloned formats
14862					if (lastClone) {
14863						target.parentNode.insertBefore(lastClone, target);
14864						firstClone.appendChild(target);
14865					}
14866				}
14867
14868				return container;
14869			}
14870
14871			function splitToFormatRoot(container) {
14872				return wrapAndSplit(findFormatRoot(container), container, container, true);
14873			}
14874
14875			function unwrap(start) {
14876				var node = dom.get(start ? '_start' : '_end'),
14877					out = node[start ? 'firstChild' : 'lastChild'];
14878
14879				// If the end is placed within the start the result will be removed
14880				// So this checks if the out node is a bookmark node if it is it
14881				// checks for another more suitable node
14882				if (isBookmarkNode(out)) {
14883					out = out[start ? 'firstChild' : 'lastChild'];
14884				}
14885
14886				dom.remove(node, true);
14887
14888				return out;
14889			}
14890
14891			function removeRngStyle(rng) {
14892				var startContainer, endContainer;
14893				var commonAncestorContainer = rng.commonAncestorContainer;
14894
14895				rng = expandRng(rng, formatList, TRUE);
14896
14897				if (format.split) {
14898					startContainer = getContainer(rng, TRUE);
14899					endContainer = getContainer(rng);
14900
14901					if (startContainer != endContainer) {
14902						// WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN
14903						// so let's see if we can use the first child instead
14904						// This will happen if you triple click a table cell and use remove formatting
14905						if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
14906							if (startContainer.nodeName == "TR") {
14907								startContainer = startContainer.firstChild.firstChild || startContainer;
14908							} else {
14909								startContainer = startContainer.firstChild || startContainer;
14910							}
14911						}
14912
14913						// Try to adjust endContainer as well if cells on the same row were selected - bug #6410
14914						if (commonAncestorContainer &&
14915							/^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) &&
14916							/^(TH|TD)$/.test(endContainer.nodeName) && endContainer.firstChild) {
14917							endContainer = endContainer.firstChild || endContainer;
14918						}
14919
14920						// Wrap start/end nodes in span element since these might be cloned/moved
14921						startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
14922						endContainer = wrap(endContainer, 'span', {id: '_end', 'data-mce-type': 'bookmark'});
14923
14924						// Split start/end
14925						splitToFormatRoot(startContainer);
14926						splitToFormatRoot(endContainer);
14927
14928						// Unwrap start/end to get real elements again
14929						startContainer = unwrap(TRUE);
14930						endContainer = unwrap();
14931					} else {
14932						startContainer = endContainer = splitToFormatRoot(startContainer);
14933					}
14934
14935					// Update range positions since they might have changed after the split operations
14936					rng.startContainer = startContainer.parentNode;
14937					rng.startOffset = nodeIndex(startContainer);
14938					rng.endContainer = endContainer.parentNode;
14939					rng.endOffset = nodeIndex(endContainer) + 1;
14940				}
14941
14942				// Remove items between start/end
14943				rangeUtils.walk(rng, function(nodes) {
14944					each(nodes, function(node) {
14945						process(node);
14946
14947						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
14948						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' &&
14949							node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
14950							removeFormat({
14951								'deep': false,
14952								'exact': true,
14953								'inline': 'span',
14954								'styles': {
14955									'textDecoration': 'underline'
14956								}
14957							}, null, node);
14958						}
14959					});
14960				});
14961			}
14962
14963			// Handle node
14964			if (node) {
14965				if (node.nodeType) {
14966					rng = dom.createRng();
14967					rng.setStartBefore(node);
14968					rng.setEndAfter(node);
14969					removeRngStyle(rng);
14970				} else {
14971					removeRngStyle(node);
14972				}
14973
14974				return;
14975			}
14976
14977			if (!selection.isCollapsed() || !format.inline || dom.select('td.mce
14977-item-selected,th.mce-item-selected').length) {
14978				bookmark = selection.getBookmark();
14979				removeRngStyle(selection.getRng(TRUE));
14980				selection.moveToBookmark(bookmark);
14981
14982				// Check if start element still has formatting then we are at: "<b>text|</b>text"
14983				// and need to move the start into the next text node
14984				if (format.inline && match(name, vars, selection.getStart())) {
14985					moveStart(selection.getRng(true));
14986				}
14987
14988				ed.nodeChanged();
14989			} else {
14990				performCaretAction('remove', name, vars);
14991			}
14992		}
14993
14994		/**
14995		 * Toggles the specified format on/off.
14996		 *
14997		 * @method toggle
14998		 * @param {String} name Name of format to apply/remove.
14999		 * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
15000		 * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
15001		 */
15002		function toggle(name, vars, node) {
15003			var fmt = get(name);
15004
15005			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
15006				remove(name, vars, node);
15007			} else {
15008				apply(name, vars, node);
15009			}
15010		}
15011
15012		/**
15013		 * Return true/false if the specified node has the specified format.
15014		 *
15015		 * @method matchNode
15016		 * @param {Node} node Node to check the format on.
15017		 * @param {String} name Format name to check.
15018		 * @param {Object} vars Optional list of variables to replace before checking it.
15019		 * @param {Boolean} similar Match format that has similar properties.
15020		 * @return {Object} Returns the format object it matches or undefined if it doesn't match.
15021		 */
15022		function matchNode(node, name, vars, similar) {
15023			var formatList = get(name), format, i, classes;
15024
15025			function matchItems(node, format, item_name) {
15026				var key, value, items = format[item_name], i;
15027
15028				// Custom match
15029				if (format.onmatch) {
15030					return format.onmatch(node, format, item_name);
15031				}
15032
15033				// Check all items
15034				if (items) {
15035					// Non indexed object
15036					if (items.length === undef) {
15037						for (key in items) {
15038							if (items.hasOwnProperty(key)) {
15039								if (item_name === 'attributes') {
15040									value = dom.getAttrib(node, key);
15041								} else {
15042									value = getStyle(node, key);
15043								}
15044
15045								if (similar && !value && !format.exact) {
15046									return;
15047								}
15048
15049								if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) {
15050									return;
15051								}
15052							}
15053						}
15054					} else {
15055						// Only one match needed for indexed arrays
15056						for (i = 0; i < items.length; i++) {
15057							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) {
15058								return format;
15059							}
15060						}
15061					}
15062				}
15063
15064				return format;
15065			}
15066
15067			if (formatList && node) {
15068				// Check each format in list
15069				for (i = 0; i < formatList.length; i++) {
15070					format = formatList[i];
15071
15072					// Name name, attributes, styles and classes
15073					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
15074						// Match classes
15075						if ((classes = format.classes)) {
15076							for (i = 0; i < classes.length; i++) {
15077								if (!dom.hasClass(node, classes[i])) {
15078									return;
15079								}
15080							}
15081						}
15082
15083						return format;
15084					}
15085				}
15086			}
15087		}
15088
15089		/**
15090		 * Matches the current selection or specified node against the specified format name.
15091		 *
15092		 * @method match
15093		 * @param {String} name Name of format to match.
15094		 * @param {Object} vars Optional list of variables to replace before checking it.
15095		 * @param {Node} node Optional node to check.
15096		 * @return {boolean} true/false if the specified selection/node matches the format.
15097		 */
15098		function match(name, vars, node) {
15099			var startNode;
15100
15101			function matchParents(node) {
15102				var root = dom.getRoot();
15103
15104				if (node === root) {
15105					return false;
15106				}
15107
15108				// Find first node with similar format settings
15109				node = dom.getParent(node, function(node) {
15110					return node.parentNode === root || !!matchNode(node, name, vars, true);
15111				});
15112
15113				// Do an exact check on the similar format element
15114				return matchNode(node, name, vars);
15115			}
15116
15117			// Check specified node
15118			if (node) {
15119				return matchParents(node);
15120			}
15121
15122			// Check selected node
15123			node = selection.getNode();
15124			if (matchParents(node)) {
15125				return TRUE;
15126			}
15127
15128			// Check start node if it's different
15129			startNode = selection.getStart();
15130			if (startNode != node) {
15131				if (matchParents(startNode)) {
15132					return TRUE;
15133				}
15134			}
15135
15136			return FALSE;
15137		}
15138
15139		/**
15140		 * Matches the current selection against the array of formats and returns a new array with matching formats.
15141		 *
15142		 * @method matchAll
15143		 * @param {Array} names Name of format to match.
15144		 * @param {Object} vars Optional list of variables to replace before checking it.
15145		 * @return {Array} Array with matched formats.
15146		 */
15147		function matchAll(names, vars) {
15148			var startElement, matchedFormatNames = [], checkedMap = {};
15149
15150			// Check start of selection for formats
15151			startElement = selection.getStart();
15152			dom.getParent(startElement, function(node) {
15153				var i, name;
15154
15155				for (i = 0; i < names.length; i++) {
15156					name = names[i];
15157
15158					if (!checkedMap[name] && matchNode(node, name, vars)) {
15159						checkedMap[name] = true;
15160						matchedFormatNames.push(name);
15161					}
15162				}
15163			}, dom.getRoot());
15164
15165			return matchedFormatNames;
15166		}
15167
15168		/**
15169		 * Returns true/false if the specified format can be applied to the current selection or not. It
15170		 * will currently only check the state for selector formats, it returns true on all other format types.
15171		 *
15172		 * @method canApply
15173		 * @param {String} name Name of format to check.
15174		 * @return {boolean} true/false if the specified format can be applied to the current selection/node.
15175		 */
15176		function canApply(name) {
15177			var formatList = get(name), startNode, parents, i, x, selector;
15178
15179			if (formatList) {
15180				startNode = selection.getStart();
15181				parents = getParents(startNode);
15182
15183				for (x = formatList.length - 1; x >= 0; x--) {
15184					selector = formatList[x].selector;
15185
15186					// Format is not selector based then always return TRUE
15187					// Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
15188					if (!selector || formatList[x].defaultBlock) {
15189						return TRUE;
15190					}
15191
15192					for (i = parents.length - 1; i >= 0; i--) {
15193						if (dom.is(parents[i], selector)) {
15194							return TRUE;
15195						}
15196					}
15197				}
15198			}
15199
15200			return FALSE;
15201		}
15202
15203		/**
15204		 * Executes the specified callback when the current selection matches the formats or not.
15205		 *
15206		 * @method formatChanged
15207		 * @param {String} formats Comma separated list of formats to check for.
15208		 * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
15209		 * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
15210		 */
15211		function formatChanged(formats, callback, similar) {
15212			var currentFormats;
15213
15214			// Setup format node change logic
15215			if (!formatChangeData) {
15216				formatChangeData = {};
15217				currentFormats = {};
15218
15219				ed.on('NodeChange', function(e) {
15220					var parents = getParents(e.element), matchedFormats = {};
15221
15222					// Check for new formats
15223					each(formatChangeData, function(callbacks, format) {
15224						each(parents, function(node) {
15225							if (matchNode(node, format, {}, callbacks.similar)) {
15226								if (!currentFormats[format]) {
15227									// Execute callbacks
15228									each(callbacks, function(callback) {
15229										callback(true, {node: node, format: format, parents: parents});
15230									});
15231
15232									currentFormats[format] = callbacks;
15233								}
15234
15235								matchedFormats[format] = callbacks;
15236								return false;
15237							}
15238						});
15239					});
15240
15241					// Check if current formats still match
15242					each(currentFormats, function(callbacks, format) {
15243						if (!matchedFormats[format]) {
15244							delete currentFormats[format];
15245
15246							each(callbacks, function(callback) {
15247								callback(false, {node: e.element, format: format, parents: parents});
15248							});
15249						}
15250					});
15251				});
15252			}
15253
15254			// Add format listeners
15255			each(formats.split(','), function(format) {
15256				if (!formatChangeData[format]) {
15257					formatChangeData[format] = [];
15258					formatChangeData[format].similar = similar;
15259				}
15260
15261				formatChangeData[format].push(callback);
15262			});
15263
15264			return this;
15265		}
15266
15267		// Expose to public
15268		extend(this, {
15269			get: get,
15270			register: register,
15271			apply: apply,
15272			remove: remove,
15273			toggle: toggle,
15274			match: match,
15275			matchAll: matchAll,
15276			matchNode: matchNode,
15277			canApply: canApply,
15278			formatChanged: formatChanged
15279		});
15280
15281		// Initialize
15282		defaultFormats();
15283		addKeyboardShortcuts();
15284		ed.on('BeforeGetContent', function() {
15285			if (markCaretContainersBogus) {
15286				markCaretContainersBogus();
15287			}
15288		});
15289		ed.on('mouseup keydown', function(e) {
15290			if (disableCaretContainer) {
15291				disableCaretContainer(e);
15292			}
15293		});
15294
15295		// Private functions
15296
15297		/**
15298		 * Checks if the specified nodes name matches the format inline/block or selector.
15299		 *
15300		 * @private
15301		 * @param {Node} node Node to match against the specified format.
15302		 * @param {Object} format Format object o match with.
15303		 * @return {boolean} true/false if the format matches.
15304		 */
15305		function matchName(node, format) {
15306			// Check for inline match
15307			if (isEq(node, format.inline)) {
15308				return TRUE;
15309			}
15310
15311			// Check for block match
15312			if (isEq(node, format.block)) {
15313				return TRUE;
15314			}
15315
15316			// Check for selector match
15317			if (format.selector) {
15318				return node.nodeType == 1 && dom.is(node, format.selector);
15319			}
15320		}
15321
15322		/**
15323		 * Compares two string/nodes regardless of their case.
15324		 *
15325		 * @private
15326		 * @param {String/Node} Node or string to compare.
15327		 * @param {String/Node} Node or string to compare.
15328		 * @return {boolean} True/false if they match.
15329		 */
15330		function isEq(str1, str2) {
15331			str1 = str1 || '';
15332			str2 = str2 || '';
15333
15334			str1 = '' + (str1.nodeName || str1);
15335			str2 = '' + (str2.nodeName || str2);
15336
15337			return str1.toLowerCase() == str2.toLowerCase();
15338		}
15339
15340		/**
15341		 * Returns the style by name on the specified node. This method modifies the style
15342		 * contents to make it more easy to match. This will resolve a few browser issues.
15343		 *
15344		 * @private
15345		 * @param {Node} node to get style from.
15346		 * @param {String} name Style name to get.
15347		 * @return {String} Style item value.
15348		 */
15349		function getStyle(node, name) {
15350			return normalizeStyleValue(dom.getStyle(node, name), name);
15351		}
15352
15353		/**
15354		 * Normalize style value by name. This method modifies the style contents
15355		 * to make it more easy to match. This will resolve a few browser issues.
15356		 *
15357		 * @private
15358		 * @param {Node} node to get style from.
15359		 * @param {String} name Style name to get.
15360		 * @return {String} Style item value.
15361		 */
15362		function normalizeStyleValue(value, name) {
15363			// Force the format to hex
15364			if (name == 'color' || name == 'backgroundColor') {
15365				value = dom.toHex(value);
15366			}
15367
15368			// Opera will return bold as 700
15369			if (name == 'fontWeight' && value == 700) {
15370				value = 'bold';
15371			}
15372
15373			// Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font"
15374			if (name == 'fontFamily') {
15375				value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
15376			}
15377
15378			return '' + value;
15379		}
15380
15381		/**
15382		 * Replaces variables in the value. The variable format is %var.
15383		 *
15384		 * @private
15385		 * @param {String} value Value to replace variables in.
15386		 * @param {Object} vars Name/value array with variables to replace.
15387		 * @return {String} New value with replaced variables.
15388		 */
15389		function replaceVars(value, vars) {
15390			if (typeof(value) != "string") {
15391				value = value(vars);
15392			} else if (vars) {
15393				value = value.replace(/%(\w+)/g, function(str, name) {
15394					return vars[name] || str;
15395				});
15396			}
15397
15398			return value;
15399		}
15400
15401		function isWhiteSpaceNode(node) {
15402			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
15403		}
15404
15405		function wrap(node, name, attrs) {
15406			var wrapper = dom.create(name, attrs);
15407
15408			node.parentNode.insertBefore(wrapper, node);
15409			wrapper.appendChild(node);
15410
15411			return wrapper;
15412		}
15413
15414		/**
15415		 * Expands the specified range like object to depending on format.
15416		 *
15417		 * For example on block formats it will move the start/end position
15418		 * to the beginning of the current block.
15419		 *
15420		 * @private
15421		 * @param {Object} rng Range like object.
15422		 * @param {Array} formats Array with formats to expand by.
15423		 * @return {Object} Expanded range like object.
15424		 */
15425		function expandRng(rng, format, remove) {
15426			var lastIdx, leaf, endPoint,
15427				startContainer = rng.startContainer,
15428				startOffset = rng.startOffset,
15429				endContainer = rng.endContainer,
15430				endOffset = rng.endOffset;
15431
15432			// This function walks up the tree if there is no siblings before/after the node
15433			function findParentContainer(start) {
15434				var container, parent, sibling, siblingName, root;
15435
15436				container = parent = start ? startContainer : endContainer;
15437				siblingName = start ? 'previousSibling' : 'nextSibling';
15438				root = dom.getRoot();
15439
15440				function isBogusBr(node) {
15441					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
15442				}
15443
15444				// If it's a text node and the offset is inside the text
15445				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
15446					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
15447						return container;
15448					}
15449				}
15450
15451				for (;;) {
15452					// Stop expanding on block elements
15453					if (!format[0].block_expand && isBlock(parent)) {
15454						return parent;
15455					}
15456
15457					// Walk left/right
15458					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
15459						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
15460							return parent;
15461						}
15462					}
15463
15464					// Check if we can move up are we at root level or body level
15465					if (parent.parentNode == root) {
15466						container = parent;
15467						break;
15468					}
15469
15470					parent = parent.parentNode;
15471				}
15472
15473				return container;
15474			}
15475
15476			// This function walks down the tree to find the leaf at the selection.
15477			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
15478			function findLeaf(node, offset) {
15479				if (offset === undef) {
15480					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
15481				}
15482
15483				while (node && node.hasChildNodes()) {
15484					node = node.childNodes[offset];
15485					if (node) {
15486						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
15487					}
15488				}
15489				return { node: node, offset: offset };
15490			}
15491
15492			// If index based start position then resolve it
15493			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
15494				lastIdx = startContainer.childNodes.length - 1;
15495				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
15496
15497				if (startContainer.nodeType == 3) {
15498					startOffset = 0;
15499				}
15500			}
15501
15502			// If index based end position then resolve it
15503			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
15504				lastIdx = endContainer.childNodes.length - 1;
15505				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
15506
15507				if (endContainer.nodeType == 3) {
15508					endOffset = endContainer.nodeValue.length;
15509				}
15510			}
15511
15512			// Expands the node to the closes contentEditable false element if it exists
15513			function findParentContentEditable(node) {
15514				var parent = node;
15515
15516				while (parent) {
15517					if (parent.nodeType === 1 && getContentEditable(parent)) {
15518						return getContentEditable(parent) === "false" ? parent : node;
15519					}
15520
15521					parent = parent.parentNode;
15522				}
15523
15524				return node;
15525			}
15526
15527			function findWordEndPoint(container, offset, start) {
15528				var walker, node, pos, lastTextNode;
15529
15530				function findSpace(node, offset) {
15531					var pos, pos2, str = node.nodeValue;
15532
15533					if (typeof(offset) == "undefined") {
15534						offset = start ? str.length : 0;
15535					}
15536
15537					if (start) {
15538						pos = str.lastIndexOf(' ', offset);
15539						pos2 = str.lastIndexOf('\u00a0', offset);
15540						pos = pos > pos2 ? pos : pos2;
15541
15542						// Include the space on remove to avoid tag soup
15543						if (pos !== -1 && !remove) {
15544							pos++;
15545						}
15546					} else {
15547						pos = str.indexOf(' ', offset);
15548						pos2 = str.indexOf('\u00a0', offset);
15549						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
15550					}
15551
15552					return pos;
15553				}
15554
15555				if (container.nodeType === 3) {
15556					pos = findSpace(container, offset);
15557
15558					if (pos !== -1) {
15559						return {container: container, offset: pos};
15560					}
15561
15562					lastTextNode = container;
15563				}
15564
15565				// Walk the nodes inside the block
15566				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
15567				while ((node = walker[start ? 'prev' : 'next']())) {
15568					if (node.nodeType === 3) {
15569						lastTextNode = node;
15570						pos = findSpace(node);
15571
15572						if (pos !== -1) {
15573							return {container: node, offset: pos};
15574						}
15575					} else if (isBlock(node)) {
15576						break;
15577					}
15578				}
15579
15580				if (lastTextNode) {
15581					if (start) {
15582						offset = 0;
15583					} else {
15584						offset = lastTextNode.length;
15585					}
15586
15587					return {container: lastTextNode, offset: offset};
15588				}
15589			}
15590
15591			function findSelectorEndPoint(container, sibling_name) {
15592				var parents, i, y, curFormat;
15593
15594				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name]) {
15595					container = container[sibling_name];
15596				}
15597
15598				parents = getParents(container);
15599				for (i = 0; i < parents.length; i++) {
15600					for (y = 0; y < format.length; y++) {
15601						curFormat = format[y];
15602
15603						// If collapsed state is set then skip formats that doesn't match that
15604						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) {
15605							continue;
15606						}
15607
15608						if (dom.is(parents[i], curFormat.selector)) {
15609							return parents[i];
15610						}
15611					}
15612				}
15613
15614				return container;
15615			}
15616
15617			function findBlockEndPoint(container, sibling_name) {
15618				var node, root = dom.getRoot();
15619
15620				// Expand to block of similar type
15621				if (!format[0].wrapper) {
15622					node = dom.getParent(container, format[0].block, root);
15623				}
15624
15625				// Expand to first wrappable block element or any block element
15626				if (!node) {
15627					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
15628						// Fixes #6183 where it would expand to editable parent element in inline mode
15629						return node != root && isTextBlock(node);
15630					});
15631				}
15632
15633				// Exclude inner lists from wrapping
15634				if (node && format[0].wrapper) {
15635					node = getParents(node, 'ul,ol').reverse()[0] || node;
15636				}
15637
15638				// Didn't find a block element look for first/last wrappable element
15639				if (!node) {
15640					node = container;
15641
15642					while (node[sibling_name] && !isBlock(node[sibling_name])) {
15643						node = node[sibling_name];
15644
15645						// Break on BR but include it will be removed later on
15646						// we can't remove it now since we need to check if it can be wrapped
15647						if (isEq(node, 'br')) {
15648							break;
15649						}
15650					}
15651				}
15652
15653				return node || container;
15654			}
15655
15656			// Expand to closest contentEditable element
15657			startContainer = findParentContentEditable(startContainer);
15658			endContainer = findParentContentEditable(endContainer);
15659
15660			// Exclude bookmark nodes if possible
15661			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
15662				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
15663				startContainer = startContainer.nextSibling || startContainer;
15664
15665				if (startContainer.nodeType == 3) {
15666					startOffset = 0;
15667				}
15668			}
15669
15670			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
15671				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
15672				endContainer = endContainer.previousSibling || endContainer;
15673
15674				if (endContainer.nodeType == 3) {
15675					endOffset = endContainer.length;
15676				}
15677			}
15678
15679			if (format[0].inline) {
15680				if (rng.collapsed) {
15681					// Expand left to closest word boundary
15682					endPoint = findWordEndPoint(startContainer, startOffset, true);
15683					if (endPoint) {
15684						startContainer = endPoint.container;
15685						startOffset = endPoint.offset;
15686					}
15687
15688					// Expand right to closest word boundary
15689					endPoint = findWordEndPoint(endContainer, endOffset);
15690					if (endPoint) {
15691						endContainer = endPoint.container;
15692						endOffset = endPoint.offset;
15693					}
15694				}
15695
15696				// Avoid applying formatting to a trailing space.
15697				leaf = findLeaf(endContainer, endOffset);
15698				if (leaf.node) {
15699					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) {
15700						leaf = findLeaf(leaf.node.previousSibling);
15701					}
15702
15703					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
15704							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
15705
15706						if (leaf.offset > 1) {
15707							endContainer = leaf.node;
15708							endContainer.splitText(leaf.offset - 1);
15709						}
15710					}
15711				}
15712			}
15713
15714			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
15715			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
15716			// This will reduce the number of wrapper elements that needs to be created
15717			// Move start point up the tree
15718			if (format[0].inline || format[0].block_expand) {
15719				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
15720					startContainer = findParentContainer(true);
15721				}
15722
15723				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
15724					endContainer = findParentContainer();
15725				}
15726			}
15727
15728			// Expand start/end container to matching selector
15729			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
15730				// Find new startContainer/endContainer if there is better one
15731				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
15732				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
15733			}
15734
15735			// Expand start/end container to matching block element or text node
15736			if (format[0].block || format[0].selector) {
15737				// Find new startContainer/endContainer if there is better one
15738				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
15739				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
15740
15741				// Non block element then try to expand up the leaf
15742				if (format[0].block) {
15743					if (!isBlock(startContainer)) {
15744						startContainer = findParentContainer(true);
15745					}
15746
15747					if (!isBlock(endContainer)) {
15748						endContainer = findParentContainer();
15749					}
15750				}
15751			}
15752
15753			// Setup index for startContainer
15754			if (startContainer.nodeType == 1) {
15755				startOffset = nodeIndex(startContainer);
15756				startContainer = startContainer.parentNode;
15757			}
15758
15759			// Setup index for endContainer
15760			if (endContainer.nodeType == 1) {
15761				endOffset = nodeIndex(endContainer) + 1;
15762				endContainer = endContainer.parentNode;
15763			}
15764
15765			// Return new range like object
15766			return {
15767				startContainer: startContainer,
15768				startOffset: startOffset,
15769				endContainer: endContainer,
15770				endOffset: endOffset
15771			};
15772		}
15773
15774		/**
15775		 * Removes the specified format for the specified node. It will also remove the node if it doesn't have
15776		 * any attributes if the format specifies it to do so.
15777		 *
15778		 * @private
15779		 * @param {Object} format Format object with items to remove from node.
15780		 * @param {Object} vars Name/value object with variables to apply to format.
15781		 * @param {Node} node Node to remove the format styles on.
15782		 * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
15783		 * @return {Boolean} True/false if the node was removed or not.
15784		 */
15785		function removeFormat(format, vars, node, compare_node) {
15786			var i, attrs, stylesModified;
15787
15788			// Check if node matches format
15789			if (!matchName(node, format)) {
15790				return FALSE;
15791			}
15792
15793			// Should we compare with format attribs and styles
15794			if (format.remove != 'all') {
15795				// Remove styles
15796				each(format.styles, function(value, name) {
15797					value = normalizeStyleValue(replaceVars(value, vars), name);
15798
15799					// Indexed array
15800					if (typeof(name) === 'number') {
15801						name = value;
15802						compare_node = 0;
15803					}
15804
15805					if (!compare_node || isEq(getStyle(compare_node, name), value)) {
15806						dom.setStyle(node, name, '');
15807					}
15808
15809					stylesModified = 1;
15810				});
15811
15812				// Remove style attribute if it's empty
15813				if (stylesModified && dom.getAttrib(node, 'style') === '') {
15814					node.removeAttribute('style');
15815					node.removeAttribute('data-mce-style');
15816				}
15817
15818				// Remove attributes
15819				each(format.attributes, function(value, name) {
15820					var valueOut;
15821
15822					value = replaceVars(value, vars);
15823
15824					// Indexed array
15825					if (typeof(name) === 'number') {
15826						name = value;
15827						compare_node = 0;
15828					}
15829
15830					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
15831						// Keep internal classes
15832						if (name == 'class') {
15833							value = dom.getAttrib(node, name);
15834							if (value) {
15835								// Build new class value where everything is removed except the internal prefixed classes
15836								valueOut = '';
15837								each(value.split(/\s+/), function(cls) {
15838									if (/mce\w+/.test(cls)) {
15839										valueOut += (valueOut ? ' ' : '') + cls;
15840									}
15841								});
15842
15843								// We got some internal classes left
15844								if (valueOut) {
15845									dom.setAttrib(node, name, valueOut);
15846									return;
15847								}
15848							}
15849						}
15850
15851						// IE6 has a bug where the attribute doesn't get removed correctly
15852						if (name == "class") {
15853							node.removeAttribute('className');
15854						}
15855
15856						// Remove mce prefixed attributes
15857						if (MCE_ATTR_RE.test(name)) {
15858							node.removeAttribute('data-mce-' + name);
15859						}
15860
15861						node.removeAttribute(name);
15862					}
15863				});
15864
15865				// Remove classes
15866				each(format.classes, function(value) {
15867					value = replaceVars(value, vars);
15868
15869					if (!compare_node || dom.hasClass(compare_node, value)) {
15870						dom.removeClass(node, value);
15871					}
15872				});
15873
15874				// Check for non internal attributes
15875				attrs = dom.getAttribs(node);
15876				for (i = 0; i < attrs.length; i++) {
15877					if (attrs[i].nodeName.indexOf('_') !== 0) {
15878						return FALSE;
15879					}
15880				}
15881			}
15882
15883			// Remove the inline child if it's empty for example <b> or <span>
15884			if (format.remove != 'none') {
15885				removeNode(node, format);
15886				return TRUE;
15887			}
15888		}
15889
15890		/**
15891		 * Removes the node and wrap it's children in paragraphs before doing so or
15892		 * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled.
15893		 *
15894		 * If the div in the node below gets removed:
15895		 *  text<div>text</div>text
15896		 *
15897		 * Output becomes:
15898		 *  text<div><br />text<br /></div>text
15899		 *
15900		 * So when the div is removed the result is:
15901		 *  text<br />text<br />text
15902		 *
15903		 * @private
15904		 * @param {Node} node Node to remove + apply BR/P elements to.
15905		 * @param {Object} format Format rule.
15906		 * @return {Node} Input node.
15907		 */
15908		function removeNode(node, format) {
15909			var parentNode = node.parentNode, rootBlockElm;
15910
15911			function find(node, next, inc) {
15912				node = getNonWhiteSpaceSibling(node, next, inc);
15913
15914				return !node || (node.nodeName == 'BR' || isBlock(node));
15915			}
15916
15917			if (format.block) {
15918				if (!forcedRootBlock) {
15919					// Append BR elements if needed before we remove the block
15920					if (isBlock(node) && !isBlock(parentNode)) {
15921						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) {
15922							node.insertBefore(dom.create('br'), node.firstChild);
15923						}
15924
15925						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) {
15926							node.appendChild(dom.create('br'));
15927						}
15928					}
15929				} else {
15930					// Wrap the block in a forcedRootBlock if we are at the root of document
15931					if (parentNode == dom.getRoot()) {
15932						if (!format.list_block || !isEq(node, format.list_block)) {
15933							each(grep(node.childNodes), function(node) {
15934								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
15935									if (!rootBlockElm) {
15936										rootBlockElm = wrap(node, forcedRootBlock);
15937										dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs);
15938									} else {
15939										rootBlockElm.appendChild(node);
15940									}
15941								} else {
15942									rootBlockElm = 0;
15943								}
15944							});
15945						}
15946					}
15947				}
15948			}
15949
15950			// Never remove nodes that isn't the specified inline element if a selector is specified too
15951			if (format.selector && format.inline && !isEq(format.inline, node)) {
15952				return;
15953			}
15954
15955			dom.remove(node, 1);
15956		}
15957
15958		/**
15959		 * Returns the next/previous non whitespace node.
15960		 *
15961		 * @private
15962		 * @param {Node} node Node to start at.
15963		 * @param {boolean} next (Optional) Include next or previous node defaults to previous.
15964		 * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false.
15965		 * @return {Node} Next or previous node or undefined if it wasn't found.
15966		 */
15967		function getNonWhiteSpaceSibling(node, next, inc) {
15968			if (node) {
15969				next = next ? 'nextSibling' : 'previousSibling';
15970
15971				for (node = inc ? node : node[next]; node; node = node[next]) {
15972					if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
15973						return node;
15974					}
15975				}
15976			}
15977		}
15978
15979		/**
15980		 * Checks if the specified node is a bookmark node or not.
15981		 *
15982		 * @private
15983		 * @param {Node} node Node to check if it's a bookmark node or not.
15984		 * @return {Boolean} true/false if the node is a bookmark node.
15985		 */
15986		function isBookmarkNode(node) {
15987			return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';
15988		}
15989
15990		/**
15991		 * Merges the next/previous sibling element if they match.
15992		 *
15993		 * @private
15994		 * @param {Node} prev Previous node to compare/merge.
15995		 * @param {Node} next Next node to compare/merge.
15996		 * @return {Node} Next node if we didn't merge and prev node if we did.
15997		 */
15998		function mergeSiblings(prev, next) {
15999			var sibling, tmpSibling;
16000
16001			/**
16002			 * Compares two nodes and checks if it's attributes and styles matches.
16003			 * This doesn't compare classes as items since their order is significant.
16004			 *
16005			 * @private
16006			 * @param {Node} node1 First node to compare with.
16007			 * @param {Node} node2 Second node to compare with.
16008			 * @return {boolean} True/false if the nodes are the same or not.
16009			 */
16010			function compareElements(node1, node2) {
16011				// Not the same name
16012				if (node1.nodeName != node2.nodeName) {
16013					return FALSE;
16014				}
16015
16016				/**
16017				 * Returns all the nodes attributes excluding internal ones, styles and classes.
16018				 *
16019				 * @private
16020				 * @param {Node} node Node to get attributes from.
16021				 * @return {Object} Name/value object with attributes and attribute values.
16022				 */
16023				function getAttribs(node) {
16024					var attribs = {};
16025
16026					each(dom.getAttribs(node), function(attr) {
16027						var name = attr.nodeName.toLowerCase();
16028
16029						// Don't compare internal attributes or style
16030						if (name.indexOf('_') !== 0 && name !== 'style') {
16031							attribs[name] = dom.getAttrib(node, name);
16032						}
16033					});
16034
16035					return attribs;
16036				}
16037
16038				/**
16039				 * Compares two objects checks if it's key + value exists in the other one.
16040				 *
16041				 * @private
16042				 * @param {Object} obj1 First object to compare.
16043				 * @param {Object} obj2 Second object to compare.
16044				 * @return {boolean} True/false if the objects matches or not.
16045				 */
16046				function compareObjects(obj1, obj2) {
16047					var value, name;
16048
16049					for (name in obj1) {
16050						// Obj1 has item obj2 doesn't have
16051						if (obj1.hasOwnProperty(name)) {
16052							value = obj2[name];
16053
16054							// Obj2 doesn't have obj1 item
16055							if (value === undef) {
16056								return FALSE;
16057							}
16058
16059							// Obj2 item has a different value
16060							if (obj1[name] != value) {
16061								return FALSE;
16062							}
16063
16064							// Delete similar value
16065							delete obj2[name];
16066						}
16067					}
16068
16069					// Check if obj 2 has something obj 1 doesn't have
16070					for (name in obj2) {
16071						// Obj2 has item obj1 doesn't have
16072						if (obj2.hasOwnProperty(name)) {
16073							return FALSE;
16074						}
16075					}
16076
16077					return TRUE;
16078				}
16079
16080				// Attribs are not the same
16081				if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
16082					return FALSE;
16083				}
16084
16085				// Styles are not the same
16086				if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
16087					return FALSE;
16088				}
16089
16090				return TRUE;
16091			}
16092
16093			function findElementSibling(node, sibling_name) {
16094				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
16095					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) {
16096						return node;
16097					}
16098
16099					if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) {
16100						return sibling;
16101					}
16102				}
16103
16104				return node;
16105			}
16106
16107			// Check if next/prev exists and that they are elements
16108			if (prev && next) {
16109				// If previous sibling is empty then jump over it
16110				prev = findElementSibling(prev, 'previousSibling');
16111				next = findElementSibling(next, 'nextSibling');
16112
16113				// Compare next and previous nodes
16114				if (compareElements(prev, next)) {
16115					// Append nodes between
16116					for (sibling = prev.nextSibling; sibling && sibling != next;) {
16117						tmpSibling = sibling;
16118						sibling = sibling.nextSibling;
16119						prev.appendChild(tmpSibling);
16120					}
16121
16122					// Remove next node
16123					dom.remove(next);
16124
16125					// Move children into prev node
16126					each(grep(next.childNodes), function(node) {
16127						prev.appendChild(node);
16128					});
16129
16130					return prev;
16131				}
16132			}
16133
16134			return next;
16135		}
16136
16137		function getContainer(rng, start) {
16138			var container, offset, lastIdx;
16139
16140			container = rng[start ? 'startContainer' : 'endContainer'];
16141			offset = rng[start ? 'startOffset' : 'endOffset'];
16142
16143			if (container.nodeType == 1) {
16144				lastIdx = container.childNodes.length - 1;
16145
16146				if (!start && offset) {
16147					offset--;
16148				}
16149
16150				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
16151			}
16152
16153			// If start text node is excluded then walk to the next node
16154			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
16155				container = new TreeWalker(container, ed.getBody()).next() || container;
16156			}
16157
16158			// If end text node is excluded then walk to the previous node
16159			if (container.nodeType === 3 && !start && offset === 0) {
16160				container = new TreeWalker(container, ed.getBody()).prev() || container;
16161			}
16162
16163			return container;
16164		}
16165
16166		function performCaretAction(type, name, vars) {
16167			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
16168
16169			// Creates a caret container bogus element
16170			function createCaretContainer(fill) {
16171				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
16172
16173				if (fill) {
16174					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
16175				}
16176
16177				return caretContainer;
16178			}
16179
16180			function isCaretContainerEmpty(node, nodes) {
16181				while (node) {
16182					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
16183						return false;
16184					}
16185
16186					// Collect nodes
16187					if (nodes && node.nodeType === 1) {
16188						nodes.push(node);
16189					}
16190
16191					node = node.firstChild;
16192				}
16193
16194				return true;
16195			}
16196
16197			// Returns any parent caret container element
16198			function getParentCaretContainer(node) {
16199				while (node) {
16200					if (node.id === caretContainerId) {
16201						return node;
16202					}
16203
16204					node = node.parentNode;
16205				}
16206			}
16207
16208			// Finds the first text node in the specified node
16209			function findFirstTextNode(node) {
16210				var walker;
16211
16212				if (node) {
16213					walker = new TreeWalker(node, node);
16214
16215					for (node = walker.current(); node; node = walker.next()) {
16216						if (node.nodeType === 3) {
16217							return node;
16218						}
16219					}
16220				}
16221			}
16222
16223			// Removes the caret container for the specified node or all on the current document
16224			function removeCaretContainer(node, move_caret) {
16225				var child, rng;
16226
16227				if (!node) {
16228					node = getParentCaretContainer(selection.getStart());
16229
16230					if (!node) {
16231						while ((node = dom.get(caretContainerId))) {
16232							removeCaretContainer(node, false);
16233						}
16234					}
16235				} else {
16236					rng = selection.getRng(true);
16237
16238					if (isCaretContainerEmpty(node)) {
16239						if (move_caret !== false) {
16240							rng.setStartBefore(node);
16241							rng.setEndBefore(node);
16242						}
16243
16244						dom.remove(node);
16245					} else {
16246						child = findFirstTextNode(node);
16247
16248						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
16249							child = child.deleteData(0, 1);
16250						}
16251
16252						dom.remove(node, 1);
16253					}
16254
16255					selection.setRng(rng);
16256				}
16257			}
16258
16259			// Applies formatting to the caret postion
16260			function applyCaretFormat() {
16261				var rng, caretContainer, textNode, offset, bookmark, container, text;
16262
16263				rng = selection.getRng(true);
16264				offset = rng.startOffset;
16265				container = rng.startContainer;
16266				text = container.nodeValue;
16267
16268				caretContainer = getParentCaretContainer(selection.getStart());
16269				if (caretContainer) {
16270					textNode = findFirstTextNode(caretContainer);
16271				}
16272
16273				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
16274				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
16275					// Get bookmark of caret position
16276					bookmark = selection.getBookmark();
16277
16278					// Collapse bookmark range (WebKit)
16279					rng.collapse(true);
16280
16281					// Expand the range to the closest word and split it at those points
16282					rng = expandRng(rng, get(name));
16283					rng = rangeUtils.split(rng);
16284
16285					// Apply the format to the range
16286					apply(name, vars, rng);
16287
16288					// Move selection back to caret position
16289					selection.moveToBookmark(bookmark);
16290				} else {
16291					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
16292						caretContainer = createCaretContainer(true);
16293						textNode = caretContainer.firstChild;
16294
16295						rng.insertNode(caretContainer);
16296						offset = 1;
16297
16298						apply(name, vars, caretContainer);
16299					} else {
16300						apply(name, vars, caretContainer);
16301					}
16302
16303					// Move selection to text node
16304					selection.setCursorLocation(textNode, offset);
16305				}
16306			}
16307
16308			function removeCaretFormat() {
16309				var rng = selection.getRng(true), container, offset, bookmark,
16310					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
16311
16312				container = rng.startContainer;
16313				offset = rng.startOffset;
16314				node = container;
16315
16316				if (container.nodeType == 3) {
16317					if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
16318						hasContentAfter = true;
16319					}
16320
16321					node = node.parentNode;
16322				}
16323
16324				while (node) {
16325					if (matchNode(node, name, vars)) {
16326						formatNode = node;
16327						break;
16328					}
16329
16330					if (node.nextSibling) {
16331						hasContentAfter = true;
16332					}
16333
16334					parents.push(node);
16335					node = node.parentNode;
16336				}
16337
16338				// Node doesn't have the specified format
16339				if (!formatNode) {
16340					return;
16341				}
16342
16343				// Is there contents after the caret then remove the format on the element
16344				if (hasContentAfter) {
16345					// Get bookmark of caret position
16346					bookmark = selection.getBookmark();
16347
16348					// Collapse bookmark range (WebKit)
16349					rng.collapse(true);
16350
16351					// Expand the range to the closest word and split it at those points
16352					rng = expandRng(rng, get(name), true);
16353					rng = rangeUtils.split(rng);
16354
16355					// Remove the format from the range
16356					remove(name, vars, rng);
16357
16358					// Move selection back to caret position
16359					selection.moveToBookmark(bookmark);
16360				} else {
16361					caretContainer = createCaretContainer();
16362
16363					node = caretContainer;
16364					for (i = parents.length - 1; i >= 0; i--) {
16365						node.appendChild(dom.clone(parents[i], false));
16366						node = node.firstChild;
16367					}
16368
16369					// Insert invisible character into inner most format element
16370					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
16371					node = node.firstChild;
16372
16373					var block = dom.getParent(formatNode, isTextBlock);
16374
16375					if (block && dom.isEmpty(block)) {
16376						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
16377						formatNode.parentNode.replaceChild(caretContainer, formatNode);
16378					} else {
16379						// Insert caret container after the formated node
16380						dom.insertAfter(caretContainer, formatNode);
16381					}
16382
16383					// Move selection to text node
16384					selection.setCursorLocation(node, 1);
16385
16386					// If the formatNode is empty, we can remove it safely. 
16387					if (dom.isEmpty(formatNode)) {
16388						dom.remove(formatNode);
16389					}
16390				}
16391			}
16392
16393			// Checks if the parent caret container node isn't empty if that is the case it
16394			// will remove the bogus state on all children that isn't empty
16395			function unmarkBogusCaretParents() {
16396				var caretContainer;
16397
16398				caretContainer = getParentCaretContainer(selection.getStart());
16399				if (caretContainer && !dom.isEmpty(caretContainer)) {
16400					walk(caretContainer, function(node) {
16401						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
16402							dom.setAttrib(node, 'data-mce-bogus', null);
16403						}
16404					}, 'childNodes');
16405				}
16406			}
16407
16408			// Only bind the caret events once
16409			if (!ed._hasCaretEvents) {
16410				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
16411				markCaretContainersBogus = function() {
16412					var nodes = [], i;
16413
16414					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
16415						// Mark children
16416						i = nodes.length;
16417						while (i--) {
16418							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
16419						}
16420					}
16421				};
16422
16423				disableCaretContainer = function(e) {
16424					var keyCode = e.keyCode;
16425
16426					removeCaretContainer();
16427
16428					// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
16429					if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
16430						removeCaretContainer(getParentCaretContainer(selection.getStart()));
16431					}
16432
16433					unmarkBogusCaretParents();
16434				};
16435
16436				// Remove bogus state if they got filled by contents using editor.selection.setContent
16437				ed.on('SetContent', function(e) {
16438					if (e.selection) {
16439						unmarkBogusCaretParents();
16440					}
16441				});
16442				ed._hasCaretEvents = true;
16443			}
16444
16445			// Do apply or remove caret format
16446			if (type == "apply") {
16447				applyCaretFormat();
16448			} else {
16449				removeCaretFormat();
16450			}
16451		}
16452
16453		/**
16454		 * Moves the start to the first suitable text node.
16455		 */
16456		function moveStart(rng) {
16457			var container = rng.startContainer,
16458					offset = rng.startOffset, isAtEndOfText,
16459					walker, node, nodes, tmpNode;
16460
16461			// Convert text node into index if possible
16462			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
16463				// Get the parent container location and walk from there
16464				offset = nodeIndex(container);
16465				container = container.parentNode;
16466				isAtEndOfText = true;
16467			}
16468
16469			// Move startContainer/startOffset in to a suitable node
16470			if (container.nodeType == 1) {
16471				nodes = container.childNodes;
16472				container = nodes[Math.min(offset, nodes.length - 1)];
16473				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
16474
16475				// If offset is at end of the parent node walk to the next one
16476				if (offset >
16476 nodes.length - 1 || isAtEndOfText) {
16477					walker.next();
16478				}
16479
16480				for (node = walker.current(); node; node = walker.next()) {
16481					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
16482						// IE has a "neat" feature where it moves the start node into the closest element
16483						// we can avoid this by inserting an element before it and then remove it after we set the selection
16484						tmpNode = dom.create('a', null, INVISIBLE_CHAR);
16485						node.parentNode.insertBefore(tmpNode, node);
16486
16487						// Set selection and remove tmpNode
16488						rng.setStart(node, 0);
16489						selection.setRng(rng);
16490						dom.remove(tmpNode);
16491
16492						return;
16493					}
16494				}
16495			}
16496		}
16497	};
16498});
16499
16500// Included from: js/tinymce/classes/UndoManager.js
16501
16502/**
16503 * UndoManager.js
16504 *
16505 * Copyright, Moxiecode Systems AB
16506 * Released under LGPL License.
16507 *
16508 * License: http://www.tinymce.com/license
16509 * Contributing: http://www.tinymce.com/contributing
16510 */
16511
16512/**
16513 * 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.
16514 *
16515 * @class tinymce.UndoManager
16516 */
16517define("tinymce/UndoManager", [
16518	"tinymce/Env",
16519	"tinymce/util/Tools"
16520], function(Env, Tools) {
16521	var trim = Tools.trim, trimContentRegExp;
16522
16523	trimContentRegExp = new RegExp([
16524		'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
16525		'<div[^>]+data-mce-bogus[^>]+><\\/div>', // Trim bogus divs like resize handles
16526		'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
16527	].join('|'), 'gi');
16528
16529	return function(editor) {
16530		var self, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, lock;
16531
16532		// Returns a trimmed version of the current editor contents
16533		function getContent() {
16534			return trim(editor.getContent({format: 'raw', no_events: 1}).replace(trimContentRegExp, ''));
16535		}
16536
16537		function addNonTypingUndoLevel() {
16538			self.typing = false;
16539			self.add();
16540		}
16541
16542		// Add initial undo level when the editor is initialized
16543		editor.on('init', function() {
16544			self.add();
16545		});
16546
16547		// Get position before an execCommand is processed
16548		editor.on('BeforeExecCommand', function(e) {
16549			var cmd = e.command;
16550
16551			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
16552				self.beforeChange();
16553			}
16554		});
16555
16556		// Add undo level after an execCommand call was made
16557		editor.on('ExecCommand', function(e) {
16558			var cmd = e.command;
16559
16560			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
16561				self.add();
16562			}
16563		});
16564
16565		editor.on('ObjectResizeStart', function() {
16566			self.beforeChange();
16567		});
16568
16569		editor.on('SaveContent ObjectResized', addNonTypingUndoLevel);
16570		editor.dom.bind(editor.dom.getRoot(), 'dragend', addNonTypingUndoLevel);
16571		editor.dom.bind(editor.getBody(), 'focusout', function() {
16572			if (!editor.removed && self.typing) {
16573				addNonTypingUndoLevel();
16574			}
16575		});
16576
16577		editor.on('KeyUp', function(e) {
16578			var keyCode = e.keyCode;
16579
16580			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
16581				addNonTypingUndoLevel();
16582				editor.nodeChanged();
16583			}
16584
16585			if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
16586				editor.nodeChanged();
16587			}
16588
16589			// Fire a TypingUndo event on the first character entered
16590			if (isFirstTypedCharacter && self.typing) {
16591				// Make the it dirty if the content was changed after typing the first character
16592				if (!editor.isDirty()) {
16593					editor.isNotDirty = !data[0] || getContent() == data[0].content;
16594
16595					// Fire initial change event
16596					if (!editor.isNotDirty) {
16597						editor.fire('change', {level: data[0], lastLevel: null});
16598					}
16599				}
16600
16601				editor.fire('TypingUndo');
16602				isFirstTypedCharacter = false;
16603				editor.nodeChanged();
16604			}
16605		});
16606
16607		editor.on('KeyDown', function(e) {
16608			var keyCode = e.keyCode;
16609
16610			// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
16611			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
16612				if (self.typing) {
16613					addNonTypingUndoLevel();
16614				}
16615
16616				return;
16617			}
16618
16619			// If key isn't shift,ctrl,alt,capslock,metakey
16620			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
16621				self.beforeChange();
16622				self.typing = true;
16623				self.add();
16624				isFirstTypedCharacter = true;
16625			}
16626		});
16627
16628		editor.on('MouseDown', function() {
16629			if (self.typing) {
16630				addNonTypingUndoLevel();
16631			}
16632		});
16633
16634		// Add keyboard shortcuts for undo/redo keys
16635		editor.addShortcut('ctrl+z', '', 'Undo');
16636		editor.addShortcut('ctrl+y,ctrl+shift+z', '', 'Redo');
16637
16638		editor.on('AddUndo Undo Redo ClearUndos MouseUp', function(e) {
16639			if (!e.isDefaultPrevented()) {
16640				editor.nodeChanged();
16641			}
16642		});
16643
16644		self = {
16645			// Explose for debugging reasons
16646			data: data,
16647
16648			/**
16649			 * State if the user is currently typing or not. This will add a typing operation into one undo
16650			 * level instead of one new level for each keystroke.
16651			 *
16652			 * @field {Boolean} typing
16653			 */
16654			typing: false,
16655
16656			/**
16657			 * Stores away a bookmark to be used when performing an undo action so that the selection is before
16658			 * the change has been made.
16659			 *
16660			 * @method beforeChange
16661			 */
16662			beforeChange: function() {
16663				if (!lock) {
16664					beforeBookmark = editor.selection.getBookmark(2, true);
16665				}
16666			},
16667
16668			/**
16669			 * Adds a new undo level/snapshot to the undo list.
16670			 *
16671			 * @method add
16672			 * @param {Object} l Optional undo level object to add.
16673			 * @return {Object} Undo level that got added or null it a level wasn't needed.
16674			 */
16675			add: function(level) {
16676				var i, settings = editor.settings, lastLevel;
16677
16678				level = level || {};
16679				level.content = getContent();
16680
16681				if (lock || editor.fire('BeforeAddUndo', {level: level}).isDefaultPrevented()) {
16682					return null;
16683				}
16684
16685				// Add undo level if needed
16686				lastLevel = data[index];
16687				if (lastLevel && lastLevel.content == level.content) {
16688					return null;
16689				}
16690
16691				// Set before bookmark on previous level
16692				if (data[index]) {
16693					data[index].beforeBookmark = beforeBookmark;
16694				}
16695
16696				// Time to compress
16697				if (settings.custom_undo_redo_levels) {
16698					if (data.length > settings.custom_undo_redo_levels) {
16699						for (i = 0; i < data.length - 1; i++) {
16700							data[i] = data[i + 1];
16701						}
16702
16703						data.length--;
16704						index = data.length;
16705					}
16706				}
16707
16708				// Get a non intrusive normalized bookmark
16709				level.bookmark = editor.selection.getBookmark(2, true);
16710
16711				// Crop array if needed
16712				if (index < data.length - 1) {
16713					data.length = index + 1;
16714				}
16715
16716				data.push(level);
16717				index = data.length - 1;
16718
16719				var args = {level: level, lastLevel: lastLevel};
16720
16721				editor.fire('AddUndo', args);
16722
16723				if (index > 0) {
16724					editor.fire('change', args);
16725					editor.isNotDirty = false;
16726				}
16727
16728				return level;
16729			},
16730
16731			/**
16732			 * Undoes the last action.
16733			 *
16734			 * @method undo
16735			 * @return {Object} Undo level or null if no undo was performed.
16736			 */
16737			undo: function() {
16738				var level;
16739
16740				if (self.typing) {
16741					self.add();
16742					self.typing = false;
16743				}
16744
16745				if (index > 0) {
16746					level = data[--index];
16747
16748					// Undo to first index then set dirty state to false
16749					if (index === 0) {
16750						editor.isNotDirty = true;
16751					}
16752
16753					editor.setContent(level.content, {format: 'raw'});
16754					editor.selection.moveToBookmark(level.beforeBookmark);
16755
16756					editor.fire('undo', {level: level});
16757				}
16758
16759				return level;
16760			},
16761
16762			/**
16763			 * Redoes the last action.
16764			 *
16765			 * @method redo
16766			 * @return {Object} Redo level or null if no redo was performed.
16767			 */
16768			redo: function() {
16769				var level;
16770
16771				if (index < data.length - 1) {
16772					level = data[++index];
16773
16774					editor.setContent(level.content, {format: 'raw'});
16775					editor.selection.moveToBookmark(level.bookmark);
16776
16777					editor.fire('redo', {level: level});
16778				}
16779
16780				return level;
16781			},
16782
16783			/**
16784			 * Removes all undo levels.
16785			 *
16786			 * @method clear
16787			 */
16788			clear: function() {
16789				data = [];
16790				index = 0;
16791				self.typing = false;
16792				editor.fire('ClearUndos');
16793			},
16794
16795			/**
16796			 * Returns true/false if the undo manager has any undo levels.
16797			 *
16798			 * @method hasUndo
16799			 * @return {Boolean} true/false if the undo manager has any undo levels.
16800			 */
16801			hasUndo: function() {
16802				// Has undo levels or typing and content isn't the same as the initial level
16803				return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
16804			},
16805
16806			/**
16807			 * Returns true/false if the undo manager has any redo levels.
16808			 *
16809			 * @method hasRedo
16810			 * @return {Boolean} true/false if the undo manager has any redo levels.
16811			 */
16812			hasRedo: function() {
16813				return index < data.length - 1 && !this.typing;
16814			},
16815
16816			/**
16817			 * Executes the specified function in an undo transation. The selection
16818			 * before the modification will be stored to the undo stack and if the DOM changes
16819			 * it will add a new undo level. Any methods within the transation that adds undo levels will
16820			 * be ignored. So a transation can include calls to execCommand or editor.insertContent.
16821			 *
16822			 * @method transact
16823			 * @param {function} callback Function to execute dom manipulation logic in.
16824			 */
16825			transact: function(callback) {
16826				self.beforeChange();
16827
16828				lock = true;
16829				callback();
16830				lock = false;
16831
16832				self.add();
16833			}
16834		};
16835
16836		return self;
16837	};
16838});
16839
16840// Included from: js/tinymce/classes/EnterKey.js
16841
16842/**
16843 * EnterKey.js
16844 *
16845 * Copyright, Moxiecode Systems AB
16846 * Released under LGPL License.
16847 *
16848 * License: http://www.tinymce.com/license
16849 * Contributing: http://www.tinymce.com/contributing
16850 */
16851
16852/**
16853 * Contains logic for handling the enter key to split/generate block elements.
16854 */
16855define("tinymce/EnterKey", [
16856	"tinymce/dom/TreeWalker",
16857	"tinymce/Env"
16858], function(TreeWalker, Env) {
16859	var isIE = Env.ie && Env.ie < 11;
16860
16861	return function(editor) {
16862		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
16863		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();
16864
16865		function handleEnterKey(evt) {
16866			var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
16867				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
16868
16869			// Returns true if the block can be split into two blocks or not
16870			function canSplitBlock(node) {
16871				return node &&
16872					dom.isBlock(node) &&
16873					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
16874					!/^(fixed|absolute)/i.test(node.style.position) &&
16875					dom.getContentEditable(node) !== "true";
16876			}
16877
16878			// Renders empty block on IE
16879			function renderBlockOnIE(block) {
16880				var oldRng;
16881
16882				if (dom.isBlock(block)) {
16883					oldRng = selection.getRng();
16884					block.appendChild(dom.create('span', null, '\u00a0'));
16885					selection.select(block);
16886					block.lastChild.outerHTML = '';
16887					selection.setRng(oldRng);
16888				}
16889			}
16890
16891			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>
16891x</p>
16892			function trimInlineElementsOnLeftSideOfBlock(block) {
16893				var node = block, firstChilds = [], i;
16894
16895				// Find inner most first child ex: <p><i><b>*</b></i></p>
16896				while ((node = node.firstChild)) {
16897					if (dom.isBlock(node)) {
16898						return;
16899					}
16900
16901					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
16902						firstChilds.push(node);
16903					}
16904				}
16905
16906				i = firstChilds.length;
16907				while (i--) {
16908					node = firstChilds[i];
16909					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
16910						dom.remove(node);
16911					} else {
16912						// Remove <a> </a> see #5381
16913						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
16914							dom.remove(node);
16915						}
16916					}
16917				}
16918			}
16919
16920			// Moves the caret to a suitable position within the root for example in the first non
16921			// pure whitespace text node or before an image
16922			function moveToCaretPosition(root) {
16923				var walker, node, rng, lastNode = root, tempElm;
16924
16925				function firstNonWhiteSpaceNodeSibling(node) {
16926					while (node) {
16927						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
16928							return node;
16929						}
16930
16931						node = node.nextSibling;
16932					}
16933				}
16934
16935				// Old IE versions doesn't properly render blocks with br elements in them
16936				// For example <p><br></p> wont be rendered correctly in a contentEditable area
16937				// until you remove the br producing <p></p>
16938				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
16939					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
16940						dom.remove(parentBlock.firstChild);
16941					}
16942				}
16943
16944				if (root.nodeName == 'LI') {
16945					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
16946
16947					if (firstChild && /^(UL|OL)$/.test(firstChild.nodeName)) {
16948						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
16949					}
16950				}
16951
16952				rng = dom.createRng();
16953
16954				if (root.hasChildNodes()) {
16955					walker = new TreeWalker(root, root);
16956
16957					while ((node = walker.current())) {
16958						if (node.nodeType == 3) {
16959							rng.setStart(node, 0);
16960							rng.setEnd(node, 0);
16961							break;
16962						}
16963
16964						if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
16965							rng.setStartBefore(node);
16966							rng.setEndBefore(node);
16967							break;
16968						}
16969
16970						lastNode = node;
16971						node = walker.next();
16972					}
16973
16974					if (!node) {
16975						rng.setStart(lastNode, 0);
16976						rng.setEnd(lastNode, 0);
16977					}
16978				} else {
16979					if (root.nodeName == 'BR') {
16980						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
16981							// Trick on older IE versions to render the caret before the BR between two lists
16982							if (!documentMode || documentMode < 9) {
16983								tempElm = dom.create('br');
16984								root.parentNode.insertBefore(tempElm, root);
16985							}
16986
16987							rng.setStartBefore(root);
16988							rng.setEndBefore(root);
16989						} else {
16990							rng.setStartAfter(root);
16991							rng.setEndAfter(root);
16992						}
16993					} else {
16994						rng.setStart(root, 0);
16995						rng.setEnd(root, 0);
16996					}
16997				}
16998
16999				selection.setRng(rng);
17000
17001				// Remove tempElm created for old IE:s
17002				dom.remove(tempElm);
17003				selection.scrollIntoView(root);
17004			}
17005
17006			function setForcedBlockAttrs(node) {
17007				var forcedRootBlockName = settings.forced_root_block;
17008
17009				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
17010					dom.setAttribs(node, settings.forced_root_block_attrs);
17011				}
17012			}
17013
17014			// Creates a new block element by cloning the current one or creating a new one if the name is specified
17015			// This function will also copy any text formatting from the parent block and add it to the new one
17016			function createNewBlock(name) {
17017				var node = container, block, clonedNode, caretNode;
17018
17019				if (name || parentBlockName == "TABLE") {
17020					block = dom.create(name || newBlockName);
17021					setForcedBlockAttrs(block);
17022				} else {
17023					block = parentBlock.cloneNode(false);
17024				}
17025
17026				caretNode = block;
17027
17028				// Clone any parent styles
17029				if (settings.keep_styles !== false) {
17030					do {
17031						if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
17032							// Never clone a caret containers
17033							if (node.id == '_mce_caret') {
17034								continue;
17035							}
17036
17037							clonedNode = node.cloneNode(false);
17038							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
17039
17040							if (block.hasChildNodes()) {
17041								clonedNode.appendChild(block.firstChild);
17042								block.appendChild(clonedNode);
17043							} else {
17044								caretNode = clonedNode;
17045								block.appendChild(clonedNode);
17046							}
17047						}
17048					} while ((node = node.parentNode));
17049				}
17050
17051				// BR is needed in empty blocks on non IE browsers
17052				if (!isIE) {
17053					caretNode.innerHTML = '<br data-mce-bogus="1">';
17054				}
17055
17056				return block;
17057			}
17058
17059			// Returns true/false if the caret is at the start/end of the parent block element
17060			function isCaretAtStartOrEndOfBlock(start) {
17061				var walker, node, name;
17062
17063				// Caret is in the middle of a text node like "a|b"
17064				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
17065					return false;
17066				}
17067
17068				// If after the last element in block node edge case for #5091
17069				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
17070					return true;
17071				}
17072
17073				// If the caret if before the first element in parentBlock
17074				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
17075					return true;
17076				}
17077
17078				// Caret can be before/after a table
17079				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
17080					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
17081				}
17082
17083				// Walk the DOM and look for text nodes or non empty elements
17084				walker = new TreeWalker(container, parentBlock);
17085
17086				// If caret is in beginning or end of a text block then jump to the next/previous node
17087				if (container.nodeType == 3) {
17088					if (start && offset === 0) {
17089						walker.prev();
17090					} else if (!start && offset == container.nodeValue.length) {
17091						walker.next();
17092					}
17093				}
17094
17095				while ((node = walker.current())) {
17096					if (node.nodeType === 1) {
17097						// Ignore bogus elements
17098						if (!node.getAttribute('data-mce-bogus')) {
17099							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
17100							name = node.nodeName.toLowerCase();
17101							if (nonEmptyElementsMap[name] && name !== 'br') {
17102								return false;
17103							}
17104						}
17105					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
17106						return false;
17107					}
17108
17109					if (start) {
17110						walker.prev();
17111					} else {
17112						walker.next();
17113					}
17114				}
17115
17116				return true;
17117			}
17118
17119			// Wraps any text nodes or inline elements in the specified forced root block name
17120			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
17121				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
17122
17123				// Not in a block element or in a table cell or caption
17124				parentBlock = dom.getParent(container, dom.isBlock);
17125				rootBlockName = editor.getBody().nodeName.toLowerCase();
17126				if (!parentBlock || !canSplitBlock(parentBlock)) {
17127					parentBlock = parentBlock || editableRoot;
17128
17129					if (!parentBlock.hasChildNodes()) {
17130						newBlock = dom.create(blockName);
17131						setForcedBlockAttrs(newBlock);
17132						parentBlock.appendChild(newBlock);
17133						rng.setStart(newBlock, 0);
17134						rng.setEnd(newBlock, 0);
17135						return newBlock;
17136					}
17137
17138					// Find parent that is the first child of parentBlock
17139					node = container;
17140					while (node.parentNode != parentBlock) {
17141						node = node.parentNode;
17142					}
17143
17144					// Loop left to find start node start wrapping at
17145					while (node && !dom.isBlock(node)) {
17146						startNode = node;
17147						node = node.previousSibling;
17148					}
17149
17150					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
17151						newBlock = dom.create(blockName);
17152						setForcedBlockAttrs(newBlock);
17153						startNode.parentNode.insertBefore(newBlock, startNode);
17154
17155						// Start wrapping until we hit a block
17156						node = startNode;
17157						while (node && !dom.isBlock(node)) {
17158							next = node.nextSibling;
17159							newBlock.appendChild(node);
17160							node = next;
17161						}
17162
17163						// Restore range to it's past location
17164						rng.setStart(container, offset);
17165						rng.setEnd(container, offset);
17166					}
17167				}
17168
17169				return container;
17170			}
17171
17172			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
17173			function handleEmptyListItem() {
17174				function isFirstOrLastLi(first) {
17175					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
17176
17177					// Find first/last element since there might be whitespace there
17178					while (node) {
17179						if (node.nodeType == 1) {
17180							break;
17181						}
17182
17183						node = node[first ? 'nextSibling' : 'previousSibling'];
17184					}
17185
17186					return node === parentBlock;
17187				}
17188
17189				function getContainerBlock() {
17190					var containerBlockParent = containerBlock.parentNode;
17191
17192					if (containerBlockParent.nodeName == 'LI') {
17193						return containerBlockParent;
17194					}
17195
17196					return containerBlock;
17197				}
17198
17199				// Check if we are in an nested list
17200				var containerBlockParentName = containerBlock.parentNode.nodeName;
17201				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
17202					newBlockName = 'LI';
17203				}
17204
17205				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
17206
17207				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
17208					if (containerBlockParentName == 'LI') {
17209						// Nested list is inside a LI
17210						dom.insertAfter(newBlock, getContainerBlock());
17211					} else {
17212						// Is first and last list item then replace the OL/UL with a text block
17213						dom.replace(newBlock, containerBlock);
17214					}
17215				} else if (isFirstOrLastLi(true)) {
17216					if (containerBlockParentName == 'LI') {
17217						// List nested in an LI then move the list to a new sibling LI
17218						dom.insertAfter(newBlock, getContainerBlock());
17219						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
17220						newBlock.appendChild(containerBlock);
17221					} else {
17222						// First LI in list then remove LI and add text block before list
17223						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
17224					}
17225				} else if (isFirstOrLastLi()) {
17226					// Last LI in list then remove LI and add text block after list
17227					dom.insertAfter(newBlock, getContainerBlock());
17228					renderBlockOnIE(newBlock);
17229				} else {
17230					// Middle LI in list the split the list and insert a text block in the middle
17231					// Extract after fragment and insert it after the current block
17232					containerBlock = getContainerBlock();
17233					tmpRng = rng.cloneRange();
17234					tmpRng.setStartAfter(parentBlock);
17235					tmpRng.setEndAfter(containerBlock);
17236					fragment = tmpRng.extractContents();
17237
17238					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
17239						newBlock = fragment.firstChild;
17240						dom.insertAfter(fragment, containerBlock);
17241					} else {
17242						dom.insertAfter(fragment, containerBlock);
17243						dom.insertAfter(newBlock, containerBlock);
17244					}
17245				}
17246
17247				dom.remove(parentBlock);
17248				moveToCaretPosition(newBlock);
17249				undoManager.add();
17250			}
17251
17252			// Walks the parent block to the right and look for BR elements
17253			function hasRightSideContent() {
17254				var walker = new TreeWalker(container, parentBlock), node;
17255
17256				while ((node = walker.next())) {
17257					if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
17258						return true;
17259					}
17260				}
17261			}
17262
17263			// Inserts a BR element if the forced_root_block option is set to false or empty string
17264			function insertBr() {
17265				var brElm, extraBr, marker;
17266
17267				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
17268					// Insert extra BR element at the end block elements
17269					if (!isIE && !hasRightSideContent()) {
17270						brElm = dom.create('br');
17271						rng.insertNode(brElm);
17272						rng.setStartAfter(brElm);
17273						rng.setEndAfter(brElm);
17274						extraBr = true;
17275					}
17276				}
17277
17278				brElm = dom.create('br');
17279				rng.insertNode(brElm);
17280
17281				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
17282				if (isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
17283					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
17284				}
17285
17286				// Insert temp marker and scroll to that
17287				marker = dom.create('span', {}, '&nbsp;');
17288				brElm.parentNode.insertBefore(marker, brElm);
17289				selection.scrollIntoView(marker);
17290				dom.remove(marker);
17291
17292				if (!extraBr) {
17293					rng.setStartAfter(brElm);
17294					rng.setEndAfter(brElm);
17295				} else {
17296					rng.setStartBefore(brElm);
17297					rng.setEndBefore(brElm);
17298				}
17299
17300				selection.setRng(rng);
17301				undoManager.add();
17302			}
17303
17304			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
17305			function trimLeadingLineBreaks(node) {
17306				do {
17307					if (node.nodeType === 3) {
17308						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
17309					}
17310
17311					node = node.firstChild;
17312				} while (node);
17313			}
17314
17315			function getEditableRoot(node) {
17316				var root = dom.getRoot(), parent, editableRoot;
17317
17318				// Get all parents until we hit a non editable parent or the root
17319				parent = node;
17320				while (parent !== root && dom.getContentEditable(parent) !== "false") {
17321					if (dom.getContentEditable(parent) === "true") {
17322						editableRoot = parent;
17323					}
17324
17325					parent = parent.parentNode;
17326				}
17327
17328				return parent !== root ? editableRoot : root;
17329			}
17330
17331			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
17332			// these might be floated and then they won't expand the block
17333			function addBrToBlockIfNeeded(block) {
17334				var lastChild;
17335
17336				// IE will render the blocks correctly other browsers needs a BR
17337				if (!isIE) {
17338					block.normalize(); // Remove empty text nodes that got left behind by the extract
17339
17340					// Check if the block is empty or contains a floated last child
17341					lastChild = block.lastChild;
17342					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
17343						dom.add(block, 'br');
17344					}
17345				}
17346			}
17347
17348			// Delete any selected contents
17349			if (!rng.collapsed) {
17350				editor.execCommand('Delete');
17351				return;
17352			}
17353
17354			// Event is blocked by some other handler for example the lists plugin
17355			if (evt.isDefaultPrevented()) {
17356				return;
17357			}
17358
17359			// Setup range items and newBlockName
17360			container = rng.startContainer;
17361			offset = rng.startOffset;
17362			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
17363			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
17364			documentMode = dom.doc.documentMode;
17365			shiftKey = evt.shiftKey;
17366
17367			// Resolve node index
17368			if (container.nodeType == 1 && container.hasChildNodes()) {
17369				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
17370				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
17371				if (isAfterLastNodeInContainer && container.nodeType == 3) {
17372					offset = container.nodeValue.length;
17373				} else {
17374					offset = 0;
17375				}
17376			}
17377
17378			// Get editable root node normaly the body element but sometimes a div or span
17379			editableRoot = getEditableRoot(container);
17380
17381			// If there is no editable root then enter is done inside a contentEditable false element
17382			if (!editableRoot) {
17383				return;
17384			}
17385
17386			undoManager.beforeChange();
17387
17388			// If editable root isn't block nor the root of the editor
17389			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
17390				if (!newBlockName || shiftKey) {
17391					insertBr();
17392				}
17393
17394				return;
17395			}
17396
17397			// Wrap the current node and it's sibling in a default block if it's needed.
17398			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
17399			// This won't happen if root blocks are disabled or the shiftKey is pressed
17400			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
17401				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
17402			}
17403
17404			// Find parent block and setup empty block paddings
17405			parentBlock = dom.getParent(container, dom.isBlock);
17406			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
17407
17408			// Setup block names
17409			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
17410			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
17411
17412			// Enter inside block contained within a LI then split or insert before/after LI
17413			if (containerBlockName == 'LI' && !evt.ctrlKey) {
17414				parentBlock = containerBlock;
17415				parentBlockName = containerBlockName;
17416			}
17417
17418			// Handle enter in LI
17419			if (parentBlockName == 'LI') {
17420				if (!newBlockName && shiftKey) {
17421					insertBr();
17422					return;
17423				}
17424
17425				// Handle enter inside an empty list item
17426				if (dom.isEmpty(parentBlock)) {
17427					handleEmptyListItem();
17428					return;
17429				}
17430			}
17431
17432			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
17433			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
17434				if (!shiftKey) {
17435					insertBr();
17436					return;
17437				}
17438			} else {
17439				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
17440				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
17441					insertBr();
17442					return;
17443				}
17444			}
17445
17446			// If parent block is root then never insert new blocks
17447			if (newBlockName && parentBlock === editor.getBody()) {
17448				return;
17449			}
17450
17451			// Default block name if it's not configured
17452			newBlockName = newBlockName || 'P';
17453
17454			// Insert new block before/after the parent block depending on caret location
17455			if (isCaretAtStartOrEndOfBlock()) {
17456				// 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
17457				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
17458					newBlock = createNewBlock(newBlockName);
17459				} else {
17460					newBlock = createNewBlock();
17461				}
17462
17463				// Split the current container block element if enter is pressed inside an empty inner block element
17464				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
17465					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
17466					newBlock = dom.split(containerBlock, parentBlock);
17467				} else {
17468					dom.insertAfter(newBlock, parentBlock);
17469				}
17470
17471				moveToCaretPosition(newBlock);
17472			} else if (isCaretAtStartOrEndOfBlock(true)) {
17473				// Insert new block before
17474				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
17475				renderBlockOnIE(newBlock);
17476				moveToCaretPosition(parentBlock);
17477			} else {
17478				// Extract after fragment and insert it after the current block
17479				tmpRng = rng.cloneRange();
17480				tmpRng.setEndAfter(parentBlock);
17481				fragment = tmpRng.extractContents();
17482				trimLeadingLineBreaks(fragment);
17483				newBlock = fragment.firstChild;
17484				dom.insertAfter(fragment, parentBlock);
17485				trimInlineElementsOnLeftSideOfBlock(newBlock);
17486				addBrToBlockIfNeeded(parentBlock);
17487				moveToCaretPosition(newBlock);
17488			}
17489
17490			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
17491
17492			// Allow custom handling of new blocks
17493			editor.fire('NewBlock', { newBlock: newBlock });
17494
17495			undoManager.add();
17496		}
17497
17498		editor.on('keydown', function(evt) {
17499			if (evt.keyCode == 13) {
17500				if (handleEnterKey(evt) !== false) {
17501					evt.preventDefault();
17502				}
17503			}
17504		});
17505	};
17506});
17507
17508// Included from: js/tinymce/classes/ForceBlocks.js
17509
17510/**
17511 * ForceBlocks.js
17512 *
17513 * Copyright, Moxiecode Systems AB
17514 * Released under LGPL License.
17515 *
17516 * License: http://www.tinymce.com/license
17517 * Contributing: http://www.tinymce.com/contributing
17518 */
17519
17520define("tinymce/ForceBlocks", [], function() {
17521	return function(editor) {
17522		var settings = editor.settings, dom = editor.dom, selection = editor.selection;
17523		var schema = editor.schema, blockElements = schema.getBlockElements();
17524
17525		function addRootBlocks() {
17526			var node = selection.getStart(), rootNode = editor.getBody(), rng;
17527			var startContainer, startOffset, endContainer, endOffset, rootBlockNode;
17528			var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection;
17529			var tmpRng, rootNodeName, forcedRootBlock;
17530
17531			forcedRootBlock = settings.forced_root_block;
17532
17533			if (!node || node.nodeType !== 1 || !forcedRootBlock) {
17534				return;
17535			}
17536
17537			// Check if node is wrapped in block
17538			while (node && node != rootNode) {
17539				if (blockElements[node.nodeName]) {
17540					return;
17541				}
17542
17543				node = node.parentNode;
17544			}
17545
17546			// Get current selection
17547			rng = selection.getRng();
17548			if (rng.setStart) {
17549				startContainer = rng.startContainer;
17550				startOffset = rng.startOffset;
17551				endContainer = rng.endContainer;
17552				endOffset = rng.endOffset;
17553
17554				try {
17555					restoreSelection = editor.getDoc().activeElement === rootNode;
17556				} catch (ex) {
17557					// IE throws unspecified error here sometimes
17558				}
17559			} else {
17560				// Force control range into text range
17561				if (rng.item) {
17562					node = rng.item(0);
17563					rng = editor.getDoc().body.createTextRange();
17564					rng.moveToElementText(node);
17565				}
17566
17567				restoreSelection = rng.parentElement().ownerDocument === editor.getDoc();
17568				tmpRng = rng.duplicate();
17569				tmpRng.collapse(true);
17570				startOffset = tmpRng.move('character', offset) * -1;
17571
17572				if (!tmpRng.collapsed) {
17573					tmpRng = rng.duplicate();
17574					tmpRng.collapse(false);
17575					endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
17576				}
17577			}
17578
17579			// Wrap non block elements and text nodes
17580			node = rootNode.firstChild;
17581			rootNodeName = rootNode.nodeName.toLowerCase();
17582			while (node) {
17583				// TODO: Break this up, too complex
17584				if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) &&
17585					schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) {
17586					// Remove empty text nodes
17587					if (node.nodeType === 3 && node.nodeValue.length === 0) {
17588						tempNode = node;
17589						node = node.nextSibling;
17590						dom.remove(tempNode);
17591						continue;
17592					}
17593
17594					if (!rootBlockNode) {
17595						rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs);
17596						node.parentNode.insertBefore(rootBlockNode, node);
17597						wrapped = true;
17598					}
17599
17600					tempNode = node;
17601					node = node.nextSibling;
17602					rootBlockNode.appendChild(tempNode);
17603				} else {
17604					rootBlockNode = null;
17605					node = node.nextSibling;
17606				}
17607			}
17608
17609			if (wrapped && restoreSelection) {
17610				if (rng.setStart) {
17611					rng.setStart(startContainer, startOffset);
17612					rng.setEnd(endContainer, endOffset);
17613					selection.setRng(rng);
17614				} else {
17615					// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
17616					try {
17617						rng = editor.getDoc().body.createTextRange();
17618						rng.moveToElementText(rootNode);
17619						rng.collapse(true);
17620						rng.moveStart('character', startOffset);
17621
17622						if (endOffset > 0) {
17623							rng.moveEnd('character', endOffset);
17624						}
17625
17626						rng.select();
17627					} catch (ex) {
17628						// Ignore
17629					}
17630				}
17631
17632				editor.nodeChanged();
17633			}
17634		}
17635
17636		// Force root blocks
17637		if (settings.forced_root_block) {
17638			editor.on('NodeChange', addRootBlocks);
17639		}
17640	};
17641});
17642
17643// Included from: js/tinymce/classes/EditorCommands.js
17644
17645/**
17646 * EditorCommands.js
17647 *
17648 * Copyright, Moxiecode Systems AB
17649 * Released under LGPL License.
17650 *
17651 * License: http://www.tinymce.com/license
17652 * Contributing: http://www.tinymce.com/contributing
17653 */
17654
17655/**
17656 * This class enables you to add custom editor commands and it contains
17657 * overrides for native browser commands to address various bugs and issues.
17658 *
17659 * @class tinymce.EditorCommands
17660 */
17661define("tinymce/EditorCommands", [
17662	"tinymce/html/Serializer",
17663	"tinymce/Env",
17664	"tinymce/util/Tools"
17665], function(Serializer, Env, Tools) {
17666	// Added for compression purposes
17667	var each = Tools.each, extend = Tools.extend;
17668	var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
17669	var isGecko = Env.gecko, isIE = Env.ie;
17670	var TRUE = true, FALSE = false;
17671
17672	return function(editor) {
17673		var dom = editor.dom,
17674			selection = editor.selection,
17675			commands = {state: {}, exec: {}, value: {}},
17676			settings = editor.settings,
17677			formatter = editor.formatter,
17678			bookmark;
17679
17680		/**
17681		 * Executes the specified command.
17682		 *
17683		 * @method execCommand
17684		 * @param {String} command Command to execute.
17685		 * @param {Boolean} ui Optional user interface state.
17686		 * @param {Object} value Optional value for command.
17687		 * @return {Boolean} true/false if the command was found or not.
17688		 */
17689		function execCommand(command, ui, value) {
17690			var func;
17691
17692			command = command.toLowerCase();
17693			if ((func = commands.exec[command])) {
17694				func(command, ui, value);
17695				return TRUE;
17696			}
17697
17698			return FALSE;
17699		}
17700
17701		/**
17702		 * Queries the current state for a command for example if the current selection is "bold".
17703		 *
17704		 * @method queryCommandState
17705		 * @param {String} command Command to check the state of.
17706		 * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
17707		 */
17708		function queryCommandState(command) {
17709			var func;
17710
17711			command = command.toLowerCase();
17712			if ((func = commands.state[command])) {
17713				return func(command);
17714			}
17715
17716			return -1;
17717		}
17718
17719		/**
17720		 * Queries the command value for example the current fontsize.
17721		 *
17722		 * @method queryCommandValue
17723		 * @param {String} command Command to check the value of.
17724		 * @return {Object} Command value of false if it's not found.
17725		 */
17726		function queryCommandValue(command) {
17727			var func;
17728
17729			command = command.toLowerCase();
17730			if ((func = commands.value[command])) {
17731				return func(command);
17732			}
17733
17734			return FALSE;
17735		}
17736
17737		/**
17738		 * Adds commands to the command collection.
17739		 *
17740		 * @method addCommands
17741		 * @param {Object} command_list Name/value collection with commands to add, the names can also be c
17741omma separated.
17742		 * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
17743		 */
17744		function addCommands(command_list, type) {
17745			type = type || 'exec';
17746
17747			each(command_list, function(callback, command) {
17748				each(command.toLowerCase().split(','), function(command) {
17749					commands[type][command] = callback;
17750				});
17751			});
17752		}
17753
17754		// Expose public methods
17755		extend(this, {
17756			execCommand: execCommand,
17757			queryCommandState: queryCommandState,
17758			queryCommandValue: queryCommandValue,
17759			addCommands: addCommands
17760		});
17761
17762		// Private methods
17763
17764		function execNativeCommand(command, ui, value) {
17765			if (ui === undefined) {
17766				ui = FALSE;
17767			}
17768
17769			if (value === undefined) {
17770				value = null;
17771			}
17772
17773			return editor.getDoc().execCommand(command, ui, value);
17774		}
17775
17776		function isFormatMatch(name) {
17777			return formatter.match(name);
17778		}
17779
17780		function toggleFormat(name, value) {
17781			formatter.toggle(name, value ? {value: value} : undefined);
17782			editor.nodeChanged();
17783		}
17784
17785		function storeSelection(type) {
17786			bookmark = selection.getBookmark(type);
17787		}
17788
17789		function restoreSelection() {
17790			selection.moveToBookmark(bookmark);
17791		}
17792
17793		// Add execCommand overrides
17794		addCommands({
17795			// Ignore these, added for compatibility
17796			'mceResetDesignMode,mceBeginUndoLevel': function() {},
17797
17798			// Add undo manager logic
17799			'mceEndUndoLevel,mceAddUndoLevel': function() {
17800				editor.undoManager.add();
17801			},
17802
17803			'Cut,Copy,Paste': function(command) {
17804				var doc = editor.getDoc(), failed;
17805
17806				// Try executing the native command
17807				try {
17808					execNativeCommand(command);
17809				} catch (ex) {
17810					// Command failed
17811					failed = TRUE;
17812				}
17813
17814				// Present alert message about clipboard access not being available
17815				if (failed || !doc.queryCommandSupported(command)) {
17816					var msg = editor.translate(
17817						"Your browser doesn't support direct access to the clipboard. " +
17818						"Please use the Ctrl+X/C/V keyboard shortcuts instead."
17819					);
17820
17821					if (Env.mac) {
17822						msg = msg.replace(/Ctrl\+/g, '\u2318+');
17823					}
17824
17825					editor.windowManager.alert(msg);
17826				}
17827			},
17828
17829			// Override unlink command
17830			unlink: function() {
17831				if (selection.isCollapsed()) {
17832					var elm = selection.getNode();
17833					if (elm.tagName == 'A') {
17834						editor.dom.remove(elm, true);
17835					}
17836
17837					return;
17838				}
17839
17840				formatter.remove("link");
17841			},
17842
17843			// Override justify commands to use the text formatter engine
17844			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
17845				var align = command.substring(7);
17846
17847				if (align == 'full') {
17848					align = 'justify';
17849				}
17850
17851				// Remove all other alignments first
17852				each('left,center,right,justify'.split(','), function(name) {
17853					if (align != name) {
17854						formatter.remove('align' + name);
17855					}
17856				});
17857
17858				toggleFormat('align' + align);
17859				execCommand('mceRepaint');
17860			},
17861
17862			// Override list commands to fix WebKit bug
17863			'InsertUnorderedList,InsertOrderedList': function(command) {
17864				var listElm, listParent;
17865
17866				execNativeCommand(command);
17867
17868				// WebKit produces lists within block elements so we need to split them
17869				// we will replace the native list creation logic to custom logic later on
17870				// TODO: Remove this when the list creation logic is removed
17871				listElm = dom.getParent(selection.getNode(), 'ol,ul');
17872				if (listElm) {
17873					listParent = listElm.parentNode;
17874
17875					// If list is within a text block then split that block
17876					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
17877						storeSelection();
17878						dom.split(listParent, listElm);
17879						restoreSelection();
17880					}
17881				}
17882			},
17883
17884			// Override commands to use the text formatter engine
17885			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
17886				toggleFormat(command);
17887			},
17888
17889			// Override commands to use the text formatter engine
17890			'ForeColor,HiliteColor,FontName': function(command, ui, value) {
17891				toggleFormat(command, value);
17892			},
17893
17894			FontSize: function(command, ui, value) {
17895				var fontClasses, fontSizes;
17896
17897				// Convert font size 1-7 to styles
17898				if (value >= 1 && value <= 7) {
17899					fontSizes = explode(settings.font_size_style_values);
17900					fontClasses = explode(settings.font_size_classes);
17901
17902					if (fontClasses) {
17903						value = fontClasses[value - 1] || value;
17904					} else {
17905						value = fontSizes[value - 1] || value;
17906					}
17907				}
17908
17909				toggleFormat(command, value);
17910			},
17911
17912			RemoveFormat: function(command) {
17913				formatter.remove(command);
17914			},
17915
17916			mceBlockQuote: function() {
17917				toggleFormat('blockquote');
17918			},
17919
17920			FormatBlock: function(command, ui, value) {
17921				return toggleFormat(value || 'p');
17922			},
17923
17924			mceCleanup: function() {
17925				var bookmark = selection.getBookmark();
17926
17927				editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
17928
17929				selection.moveToBookmark(bookmark);
17930			},
17931
17932			mceRemoveNode: function(command, ui, value) {
17933				var node = value || selection.getNode();
17934
17935				// Make sure that the body node isn't removed
17936				if (node != editor.getBody()) {
17937					storeSelection();
17938					editor.dom.remove(node, TRUE);
17939					restoreSelection();
17940				}
17941			},
17942
17943			mceSelectNodeDepth: function(command, ui, value) {
17944				var counter = 0;
17945
17946				dom.getParent(selection.getNode(), function(node) {
17947					if (node.nodeType == 1 && counter++ == value) {
17948						selection.select(node);
17949						return FALSE;
17950					}
17951				}, editor.getBody());
17952			},
17953
17954			mceSelectNode: function(command, ui, value) {
17955				selection.select(value);
17956			},
17957
17958			mceInsertContent: function(command, ui, value) {
17959				var parser, serializer, parentNode, rootNode, fragment, args;
17960				var marker, rng, node, node2, bookmarkHtml;
17961
17962				function trimOrPaddLeftRight(html) {
17963					var rng, container, offset;
17964
17965					rng = selection.getRng(true);
17966					container = rng.startContainer;
17967					offset = rng.startOffset;
17968
17969					function hasSiblingText(siblingName) {
17970						return container[siblingName] && container[siblingName].nodeType == 3;
17971					}
17972
17973					if (container.nodeType == 3) {
17974						if (offset > 0) {
17975							html = html.replace(/^&nbsp;/, ' ');
17976						} else if (!hasSiblingText('previousSibling')) {
17977							html = html.replace(/^ /, '&nbsp;');
17978						}
17979
17980						if (offset < container.length) {
17981							html = html.replace(/&nbsp;(<br>|)$/, ' ');
17982						} else if (!hasSiblingText('nextSibling')) {
17983							html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
17984						}
17985					}
17986
17987					return html;
17988				}
17989
17990				// Check for whitespace before/after value
17991				if (/^ | $/.test(value)) {
17992					value = trimOrPaddLeftRight(value);
17993				}
17994
17995				// Setup parser and serializer
17996				parser = editor.parser;
17997				serializer = new Serializer({}, editor.schema);
17998				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">
17998&#xFEFF;&#200B;</span>';
17999
18000				// Run beforeSetContent handlers on the HTML to be inserted
18001				args = {content: value, format: 'html', selection: true};
18002				editor.fire('BeforeSetContent', args);
18003				value = args.content;
18004
18005				// Add caret at end of contents if it's missing
18006				if (value.indexOf('{$caret}') == -1) {
18007					value += '{$caret}';
18008				}
18009
18010				// Replace the caret marker with a span bookmark element
18011				value = value.replace(/\{\$caret\}/, bookmarkHtml);
18012
18013				// If selection is at <body>|<p></p> then move it into <body><p>|</p>
18014				rng = selection.getRng();
18015				var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
18016				var body = editor.getBody();
18017				if (caretElement === body && selection.isCollapsed()) {
18018					if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
18019						rng = dom.createRng();
18020						rng.setStart(body.firstChild, 0);
18021						rng.setEnd(body.firstChild, 0);
18022						selection.setRng(rng);
18023					}
18024				}
18025
18026				// Insert node maker where we will insert the new HTML and get it's parent
18027				if (!selection.isCollapsed()) {
18028					editor.getDoc().execCommand('Delete', false, null);
18029				}
18030
18031				parentNode = selection.getNode();
18032
18033				// Parse the fragment within the context of the parent node
18034				var parserArgs = {context: parentNode.nodeName.toLowerCase()};
18035				fragment = parser.parse(value, parserArgs);
18036
18037				// Move the caret to a more suitable location
18038				node = fragment.lastChild;
18039				if (node.attr('id') == 'mce_marker') {
18040					marker = node;
18041
18042					for (node = node.prev; node; node = node.walk(true)) {
18043						if (node.type == 3 || !dom.isBlock(node.name)) {
18044							node.parent.insert(marker, node, node.name === 'br');
18045							break;
18046						}
18047					}
18048				}
18049
18050				// If parser says valid we can insert the contents into that parent
18051				if (!parserArgs.invalid) {
18052					value = serializer.serialize(fragment);
18053
18054					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
18055					node = parentNode.firstChild;
18056					node2 = parentNode.lastChild;
18057					if (!node || (node === node2 && node.nodeName === 'BR')) {
18058						dom.setHTML(parentNode, value);
18059					} else {
18060						selection.setContent(value);
18061					}
18062				} else {
18063					// If the fragment was invalid within that context then we need
18064					// to parse and process the parent it's inserted into
18065
18066					// Insert bookmark node and get the parent
18067					selection.setContent(bookmarkHtml);
18068					parentNode = selection.getNode();
18069					rootNode = editor.getBody();
18070
18071					// Opera will return the document node when selection is in root
18072					if (parentNode.nodeType == 9) {
18073						parentNode = node = rootNode;
18074					} else {
18075						node = parentNode;
18076					}
18077
18078					// Find the ancestor just before the root element
18079					while (node !== rootNode) {
18080						parentNode = node;
18081						node = node.parentNode;
18082					}
18083
18084					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
18085					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
18086					value = serializer.serialize(
18087						parser.parse(
18088							// Need to replace by using a function since $ in the contents would otherwise be a problem
18089							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
18090								return serializer.serialize(fragment);
18091							})
18092						)
18093					);
18094
18095					// Set the inner/outer HTML depending on if we are in the root or not
18096					if (parentNode == rootNode) {
18097						dom.setHTML(rootNode, value);
18098					} else {
18099						dom.setOuterHTML(parentNode, value);
18100					}
18101				}
18102
18103				marker = dom.get('mce_marker');
18104				selection.scrollIntoView(marker);
18105
18106				// Move selection before marker and remove it
18107				rng = dom.createRng();
18108
18109				// If previous sibling is a text node set the selection to the end of that node
18110				node = marker.previousSibling;
18111				if (node && node.nodeType == 3) {
18112					rng.setStart(node, node.nodeValue.length);
18113
18114					// TODO: Why can't we normalize on IE
18115					if (!isIE) {
18116						node2 = marker.nextSibling;
18117						if (node2 && node2.nodeType == 3) {
18118							node.appendData(node2.data);
18119							node2.parentNode.removeChild(node2);
18120						}
18121					}
18122				} else {
18123					// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
18124					rng.setStartBefore(marker);
18125					rng.setEndBefore(marker);
18126				}
18127
18128				// Remove the marker node and set the new range
18129				dom.remove(marker);
18130				selection.setRng(rng);
18131
18132				// Dispatch after event and add any visual elements needed
18133				editor.fire('SetContent', args);
18134				editor.addVisual();
18135			},
18136
18137			mceInsertRawHTML: function(command, ui, value) {
18138				selection.setContent('tiny_mce_marker');
18139				editor.setContent(
18140					editor.getContent().replace(/tiny_mce_marker/g, function() {
18141						return value;
18142					})
18143				);
18144			},
18145
18146			mceToggleFormat: function(command, ui, value) {
18147				toggleFormat(value);
18148			},
18149
18150			mceSetContent: function(command, ui, value) {
18151				editor.setContent(value);
18152			},
18153
18154			'Indent,Outdent': function(command) {
18155				var intentValue, indentUnit, value;
18156
18157				// Setup indent level
18158				intentValue = settings.indentation;
18159				indentUnit = /[a-z%]+$/i.exec(intentValue);
18160				intentValue = parseInt(intentValue, 10);
18161
18162				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
18163					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
18164					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
18165						formatter.apply('div');
18166					}
18167
18168					each(selection.getSelectedBlocks(), function(element) {
18169						var indentStyleName;
18170
18171						if (element.nodeName != "LI") {
18172							indentStyleName = dom.getStyle(element, 'direction', true) == 'rtl' ? 'paddingRight' : 'paddingLeft';
18173
18174							if (command == 'outdent') {
18175								value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
18176								dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
18177							} else {
18178								value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
18179								dom.setStyle(element, indentStyleName, value);
18180							}
18181						}
18182					});
18183				} else {
18184					execNativeCommand(command);
18185				}
18186			},
18187
18188			mceRepaint: function() {
18189				if (isGecko) {
18190					try {
18191						storeSelection(TRUE);
18192
18193						if (selection.getSel()) {
18194							selection.getSel().selectAllChildren(editor.getBody());
18195						}
18196
18197						selection.collapse(TRUE);
18198						restoreSelection();
18199					} catch (ex) {
18200						// Ignore
18201					}
18202				}
18203			},
18204
18205			InsertHorizontalRule: function() {
18206				editor.execCommand('mceInsertContent', false, '<hr />');
18207			},
18208
18209			mceToggleVisualAid: function() {
18210				editor.hasVisual = !editor.hasVisual;
18211				editor.addVisual();
18212			},
18213
18214			mceReplaceContent: function(command, ui, value) {
18215				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
18216			},
18217
18218			mceInsertLink: function(command, ui, value) {
18219				var anchor;
18220
18221				if (typeof(value) == 'string') {
18222					value = {href: value};
18223				}
18224
18225				anchor = dom.getParent(selection.getNode(), 'a');
18226
18227				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
18228				value.href = value.href.replace(' ', '%20');
18229
18230				// Remove existing links if there could be child links or that the href isn't specified
18231				if (!anchor || !value.href) {
18232					formatter.remove('link');
18233				}
18234
18235				// Apply new link to selection
18236				if (value.href) {
18237					formatter.apply('link', value, anchor);
18238				}
18239			},
18240
18241			selectAll: function() {
18242				var root = dom.getRoot(), rng;
18243
18244				if (selection.getRng().setStart) {
18245					rng = dom.createRng();
18246					rng.setStart(root, 0);
18247					rng.setEnd(root, root.childNodes.length);
18248					selection.setRng(rng);
18249				} else {
18250					// IE will render it's own root level block elements and sometimes
18251					// even put font elements in them when the user starts typing. So we need to
18252					// move the selection to a more suitable element from this:
18253					// <body>|<p></p></body> to this: <body><p>|</p></body>
18254					rng = selection.getRng();
18255					if (!rng.item) {
18256						rng.moveToElementText(root);
18257						rng.select();
18258					}
18259				}
18260			},
18261
18262			"delete": function() {
18263				execNativeCommand("Delete");
18264
18265				// Check if body is empty after the delete call if so then set the contents
18266				// to an empty string and move the caret to any block produced by that operation
18267				// this fixes the issue with root blocks not being properly produced after a delete call on IE
18268				var body = editor.getBody();
18269
18270				if (dom.isEmpty(body)) {
18271					editor.setContent('');
18272
18273					if (body.firstChild && dom.isBlock(body.firstChild)) {
18274						editor.selection.setCursorLocation(body.firstChild, 0);
18275					} else {
18276						editor.selection.setCursorLocation(body, 0);
18277					}
18278				}
18279			},
18280
18281			mceNewDocument: function() {
18282				editor.setContent('');
18283			}
18284		});
18285
18286		// Add queryCommandState overrides
18287		addCommands({
18288			// Override justify commands
18289			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
18290				var name = 'align' + command.substring(7);
18291				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
18292				var matches = map(nodes, function(node) {
18293					return !!formatter.matchNode(node, name);
18294				});
18295				return inArray(matches, TRUE) !== -1;
18296			},
18297
18298			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
18299				return isFormatMatch(command);
18300			},
18301
18302			mceBlockQuote: function() {
18303				return isFormatMatch('blockquote');
18304			},
18305
18306			Outdent: function() {
18307				var node;
18308
18309				if (settings.inline_styles) {
18310					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
18311						return TRUE;
18312					}
18313
18314					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
18315						return TRUE;
18316					}
18317				}
18318
18319				return (
18320					queryCommandState('InsertUnorderedList') ||
18321					queryCommandState('InsertOrderedList') ||
18322					(!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
18323				);
18324			},
18325
18326			'InsertUnorderedList,InsertOrderedList': function(command) {
18327				var list = dom.getParent(selection.getNode(), 'ul,ol');
18328
18329				return list &&
18330					(
18331						command === 'insertunorderedlist' && list.tagName === 'UL' ||
18332						command === 'insertorderedlist' && list.tagName === 'OL'
18333					);
18334			}
18335		}, 'state');
18336
18337		// Add queryCommandValue overrides
18338		addCommands({
18339			'FontSize,FontName': function(command) {
18340				var value = 0, parent;
18341
18342				if ((parent = dom.getParent(selection.getNode(), 'span'))) {
18343					if (command == 'fontsize') {
18344						value = parent.style.fontSize;
18345					} else {
18346						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
18347					}
18348				}
18349
18350				return value;
18351			}
18352		}, 'value');
18353
18354		// Add undo manager logic
18355		addCommands({
18356			Undo: function() {
18357				editor.undoManager.undo();
18358			},
18359
18360			Redo: function() {
18361				editor.undoManager.redo();
18362			}
18363		});
18364	};
18365});
18366
18367// Included from: js/tinymce/classes/util/URI.js
18368
18369/**
18370 * URI.js
18371 *
18372 * Copyright, Moxiecode Systems AB
18373 * Released under LGPL License.
18374 *
18375 * License: http://www.tinymce.com/license
18376 * Contributing: http://www.tinymce.com/contributing
18377 */
18378
18379/**
18380 * This class handles parsing, modification and serialization of URI/URL strings.
18381 * @class tinymce.util.URI
18382 */
18383define("tinymce/util/URI", [
18384	"tinymce/util/Tools"
18385], function(Tools) {
18386	var each = Tools.each, trim = Tools.trim;
18387
18388	/**
18389	 * Constructs a new URI instance.
18390	 *
18391	 * @constructor
18392	 * @method URI
18393	 * @param {String} url URI string to parse.
18394	 * @param {Object} settings Optional settings object.
18395	 */
18396	function URI(url, settings) {
18397		var self = this, baseUri, base_url;
18398
18399		// Trim whitespace
18400		url = trim(url);
18401
18402		// Default settings
18403		settings = self.settings = settings || {};
18404
18405		// Strange app protocol that isn't http/https or local anchor
18406		// For example: mailto,skype,tel etc.
18407		if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
18408			self.source = url;
18409			return;
18410		}
18411
18412		var isProtocolRelative = url.indexOf('//') === 0;
18413
18414		// Absolute path with no host, fake host and protocol
18415		if (url.indexOf('/') === 0 && !isProtocolRelative) {
18416			url = (settings.base_uri ? settings.base_uri.protocol || 'http' : 'http') + '://mce_host' + url;
18417		}
18418
18419		// Relative path http:// or protocol relative //path
18420		if (!/^[\w\-]*:?\/\//.test(url)) {
18421			base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
18422			if (settings.base_uri.protocol === "") {
18423				url = '//mce_host' + self.toAbsPath(base_url, url);
18424			} else {
18425				url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
18426			}
18427		}
18428
18429		// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
18430		url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
18431
18432		/*jshint maxlen: 255 */
18433		url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
18434
18435		each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
18436			var part = url[i];
18437
18438			// Zope 3 workaround, they use @@something
18439			if (part) {
18440				part = part.replace(/\(mce_at\)/g, '@@');
18441			}
18442
18443			self[v] = part;
18444		});
18445
18446		baseUri = settings.base_uri;
18447		if (baseUri) {
18448			if (!self.protocol) {
18449				self.protocol = baseUri.protocol;
18450			}
18451
18452			if (!self.userInfo) {
18453				self.userInfo = baseUri.userInfo;
18454			}
18455
18456			if (!self.port && self.host === 'mce_host') {
18457				self.port = baseUri.port;
18458			}
18459
18460			if (!self.host || self.host === 'mce_host') {
18461				self.host = baseUri.host;
18462			}
18463
18464			self.source = '';
18465		}
18466
18467		if (isProtocolRelative) {
18468			self.protocol = '';
18469		}
18470
18471		//t.path = t.path || '/';
18472	}
18473
18474	URI.prototype = {
18475		/**
18476		 * Sets the internal path part of the URI.
18477		 *
18478		 * @method setPath
18479		 * @param {string} path Path string to set.
18480		 */
18481		setPath: function(path) {
18482			var self = this;
18483
18484			path = /^(.*?)\/?(\w+)?$/.exec(path);
18485
18486			// Update path parts
18487			self.path = path[0];
18488			self.directory = path[1];
18489			self.file = path[2];
18490
18491			// Rebuild source
18492			self.source = '';
18493			self.getURI();
18494		},
18495
18496		/**
18497		 * Converts the specified URI into a relative URI based on the current URI instance location.
18498		 *
18499		 * @method toRelative
18500		 * @param {String} uri URI to convert into a relative path/URI.
18501		 * @return {String} Relative URI from the point specified in the current URI instance.
18502		 * @example
18503		 * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
18504		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
18505		 */
18506		toRelative: function(uri) {
18507			var self = this, output;
18508
18509			if (uri === "./") {
18510				return uri;
18511			}
18512
18513			uri = new URI(uri, {base_uri: self});
18514
18515			// Not on same domain/port or protocol
18516			if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
18517				(self.protocol != uri.protocol && uri.protocol !== "")) {
18518				return uri.getURI();
18519			}
18520
18521			var tu = self.getURI(), uu = uri.getURI();
18522
18523			// Allow usage of the base_uri when relative_urls = true
18524			if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
18525				return tu;
18526			}
18527
18528			output = self.toRelPath(self.path, uri.path);
18529
18530			// Add query
18531			if (uri.query) {
18532				output += '?' + uri.query;
18533			}
18534
18535			// Add anchor
18536			if (uri.anchor) {
18537				output += '#' + uri.anchor;
18538			}
18539
18540			return output;
18541		},
18542
18543		/**
18544		 * Converts the specified URI into a absolute URI based on the current URI instance location.
18545		 *
18546		 * @method toAbsolute
18547		 * @param {String} uri URI to convert into a relative path/URI.
18548		 * @param {Boolean} noHost No host and protocol prefix.
18549		 * @return {String} Absolute URI from the point specified in the current URI instance.
18550		 * @example
18551		 * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
18552		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
18553		 */
18554		toAbsolute: function(uri, noHost) {
18555			uri = new URI(uri, {base_uri: this});
18556
18557			return uri.getURI(this.host == uri.host && this.protocol == uri.protocol ? noHost : 0);
18558		},
18559
18560		/**
18561		 * Converts a absolute path into a relative path.
18562		 *
18563		 * @method toRelPath
18564		 * @param {String} base Base point to convert the path from.
18565		 * @param {String} path Absolute path to convert into a relative path.
18566		 */
18567		toRelPath: function(base, path) {
18568			var items, breakPoint = 0, out = '', i, l;
18569
18570			// Split the paths
18571			base = base.substring(0, base.lastIndexOf('/'));
18572			base = base.split('/');
18573			items = path.split('/');
18574
18575			if (base.length >= items.length) {
18576				for (i = 0, l = base.length; i < l; i++) {
18577					if (i >= items.length || base[i] != items[i]) {
18578						breakPoint = i + 1;
18579						break;
18580					}
18581				}
18582			}
18583
18584			if (base.length < items.length) {
18585				for (i = 0, l = items.length; i < l; i++) {
18586					if (i >= base.length || base[i] != items[i]) {
18587						breakPoint = i + 1;
18588						break;
18589					}
18590				}
18591			}
18592
18593			if (breakPoint === 1) {
18594				return path;
18595			}
18596
18597			for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) {
18598				out += "../";
18599			}
18600
18601			for (i = breakPoint - 1, l = items.length; i < l; i++) {
18602				if (i != breakPoint - 1) {
18603					out += "/" + items[i];
18604				} else {
18605					out += items[i];
18606				}
18607			}
18608
18609			return out;
18610		},
18611
18612		/**
18613		 * Converts a relative path into a absolute path.
18614		 *
18615		 * @method toAbsPath
18616		 * @param {String} base Base point to convert the path from.
18617		 * @param {String} path Relative path to convert into an absolute path.
18618		 */
18619		toAbsPath: function(base, path) {
18620			var i, nb = 0, o = [], tr, outPath;
18621
18622			// Split paths
18623			tr = /\/$/.test(path) ? '/' : '';
18624			base = base.split('/');
18625			path = path.split('/');
18626
18627			// Remove empty chunks
18628			each(base, function(k) {
18629				if (k) {
18630					o.push(k);
18631				}
18632			});
18633
18634			base = o;
18635
18636			// Merge relURLParts chunks
18637			for (i = path.length - 1, o = []; i >= 0; i--) {
18638				// Ignore empty or .
18639				if (path[i].length === 0 || path[i] === ".") {
18640					continue;
18641				}
18642
18643				// Is parent
18644				if (path[i] === '..') {
18645					nb++;
18646					continue;
18647				}
18648
18649				// Move up
18650				if (nb > 0) {
18651					nb--;
18652					continue;
18653				}
18654
18655				o.push(path[i]);
18656			}
18657
18658			i = base.length - nb;
18659
18660			// If /a/b/c or /
18661			if (i <= 0) {
18662				outPath = o.reverse().join('/');
18663			} else {
18664				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
18665			}
18666
18667			// Add front / if it's needed
18668			if (outPath.indexOf('/') !== 0) {
18669				outPath = '/' + outPath;
18670			}
18671
18672			// Add traling / if it's needed
18673			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {
18674				outPath += tr;
18675			}
18676
18677			return outPath;
18678		},
18679
18680		/**
18681		 * Returns the full URI of the internal structure.
18682		 *
18683		 * @method getURI
18684		 * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
18685		 */
18686		getURI: function(noProtoHost) {
18687			var s, self = this;
18688
18689			// Rebuild source
18690			if (!self.source || noProtoHost) {
18691				s = '';
18692
18693				if (!noProtoHost) {
18694					if (self.protocol) {
18695						s += self.protocol + '://';
18696					} else {
18697						s += '//';
18698					}
18699
18700					if (self.userInfo) {
18701						s += self.userInfo + '@';
18702					}
18703
18704					if (self.host) {
18705						s += self.host;
18706					}
18707
18708					if (self.port) {
18709						s += ':' + self.port;
18710					}
18711				}
18712
18713				if (self.path) {
18714					s += self.path;
18715				}
18716
18717				if (self.query) {
18718					s += '?' + self.query;
18719				}
18720
18721				if (self.anchor) {
18722					s += '#' + self.anchor;
18723				}
18724
18725				self.source = s;
18726			}
18727
18728			return self.source;
18729		}
18730	};
18731
18732	return URI;
18733});
18734
18735// Included from: js/tinymce/classes/util/Class.js
18736
18737/**
18738 * Class.js
18739 *
18740 * Copyright 2003-2012, Moxiecode Systems AB, All rights reserved.
18741 */
18742
18743/**
18744 * This utilitiy class is used for easier inheritage.
18745 *
18746 * Features:
18747 * * Exposed super functions: this._super();
18748 * * Mixins
18749 * * Dummy functions
18750 * * Property functions: var value = object.value(); and object.value(newValue);
18751 * * Static functions
18752 * * Defaults settings
18753 */
18754define("tinymce/util/Class", [
18755	"tinymce/util/Tools"
18756], function(Tools) {
18757	var each = Tools.each, extend = Tools.extend;
18758
18759	var extendClass, initializing;
18760
18761	function Class() {
18762	}
18763
18764	// Provides classical inheritance, based on code made by John Resig
18765	Class.extend = extendClass = function(prop) {
18766		var Self = this, _super = Self.prototype, prototype, name, member;
18767
18768		// The dummy class constructor
18769		function Class() {
18770			var i, mixins, mixin, self;
18771
18772			// All construction is actually done in the init method
18773			if (!initializing) {
18774				self = this;
18775
18776				// Run class constuctor
18777				if (self.init) {
18778					self.init.apply(self, arguments);
18779				}
18780
18781				// Run mixin constructors
18782				mixins = self.Mixins;
18783				if (mixins) {
18784					i = mixins.length;
18785					while (i--) {
18786						mixin = mixins[i];
18787						if (mixin.init) {
18788							mixin.init.apply(self, arguments);
18789						}
18790					}
18791				}
18792			}
18793		}
18794
18795		// Dummy function, needs to be extended in order to provide functionality
18796		function dummy() {
18797			return this;
18798		}
18799
18800		// Creates a overloaded method for the class
18801		// this enables you to use this._super(); to call the super function
18802		function createMethod(name, fn) {
18803			return function(){
18804				var self = this, tmp = self._super, ret;
18805
18806				self._super = _super[name];
18807				ret = fn.apply(self, arguments);
18808				self._super = tmp;
18809
18810				return ret;
18811			};
18812		}
18813
18814		// Instantiate a base class (but only create the instance,
18815		// don't run the init constructor)
18816		initializing = true;
18817		prototype = new Self();
18818		initializing = false;
18819
18820		// Add mixins
18821		if (prop.Mixins) {
18822			each(prop.Mixins, function(mixin) {
18823				mixin = mixin;
18824
18825				for (var name in mixin) {
18826					if (name !== "init") {
18827						prop[name] = mixin[name];
18828					}
18829				}
18830			});
18831
18832			if (_super.Mixins) {
18833				prop.Mixins = _super.Mixins.concat(prop.Mixins);
18834			}
18835		}
18836
18837		// Generate dummy methods
18838		if (prop.Methods) {
18839			each(prop.Methods.split(','), function(name) {
18840				prop[name] = dummy;
18841			});
18842		}
18843
18844		// Generate property methods
18845		if (prop.Properties) {
18846			each(prop.Properties.split(','), function(name) {
18847				var fieldName = '_' + name;
18848
18849				prop[name] = function(value) {
18850					var self = this, undef;
18851
18852					// Set value
18853					if (value !== undef) {
18854						self[fieldName] = value;
18855
18856						return self;
18857					}
18858
18859					// Get value
18860					return self[fieldName];
18861				};
18862			});
18863		}
18864
18865		// Static functions
18866		if (prop.Statics) {
18867			each(prop.Statics, function(func, name) {
18868				Class[name] = func;
18869			});
18870		}
18871
18872		// Default settings
18873		if (prop.Defaults && _super.Defaults) {
18874			prop.Defaults = extend({}, _super.Defaults, prop.Defaults);
18875		}
18876
18877		// Copy the properties over onto the new prototype
18878		for (name in prop) {
18879			member = prop[name];
18880
18881			if (typeof member == "function" && _super[name]) {
18882				prototype[name] = createMethod(name, member);
18883			} else {
18884				prototype[name] = member;
18885			}
18886		}
18887
18888		// Populate our constructed prototype object
18889		Class.prototype = prototype;
18890
18891		// Enforce the constructor to be what we expect
18892		Class.constructor = Class;
18893
18894		// And make this class extendible
18895		Class.extend = extendClass;
18896
18897		return Class;
18898	};
18899
18900	return Class;
18901});
18902
18903// Included from: js/tinymce/classes/ui/Selector.js
18904
18905/**
18906 * Selector.js
18907 *
18908 * Copyright, Moxiecode Systems AB
18909 * Released under LGPL License.
18910 *
18911 * License: http://www.tinymce.com/license
18912 * Contributing: http://www.tinymce.com/contributing
18913 */
18914
18915/**
18916 * Selector engine, enables you to select controls by using CSS like expressions.
18917 * We currently only support basic CSS expressions to reduce the size of the core
18918 * and the ones we support should be enough for most cases.
18919 *
18920 * @example
18921 * Supported expressions:
18922 *  element
18923 *  element#name
18924 *  element.class
18925 *  element[attr]
18926 *  element[attr*=value]
18927 *  element[attr~=value]
18928 *  element[attr!=value]
18929 *  element[attr^=value]
18930 *  element[attr$=value]
18931 *  element:<state>
18932 *  element:not(<expression>)
18933 *  element:first
18934 *  element:last
18935 *  element:odd
18936 *  element:even
18937 *  element element
18938 *  element > element
18939 *
18940 * @class tinymce.ui.Selector
18941 */
18942define("tinymce/ui/Selector", [
18943	"tinymce/util/Class"
18944], function(Class) {
18945	"use strict";
18946
18947	/**
18948	 * Produces an array with a unique set of objects. It will not compare the values
18949	 * but the references of the objects.
18950	 *
18951	 * @private
18952	 * @method unqiue
18953	 * @param {Array} array Array to make into an array with unique items.
18954	 * @return {Array} Array with unique items.
18955	 */
18956	function unique(array) {
18957		var uniqueItems = [], i = array.length, item;
18958
18959		while (i--) {
18960			item = array[i];
18961
18962			if (!item.__checked) {
18963				uniqueItems.push(item);
18964				item.__checked = 1;
18965			}
18966		}
18967
18968		i = uniqueItems.length;
18969		while (i--) {
18970			delete uniqueItems[i].__checked;
18971		}
18972
18973		return uniqueItems;
18974	}
18975
18976	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
18977
18978	/*jshint maxlen:255 */
18979	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
18980		whiteSpace = /^\s*|\s*$/g,
18981		Collection;
18982
18983	var Selector = Class.extend({
18984		/**
18985		 * Constructs a new Selector instance.
18986		 *
18987		 * @constructor
18988		 * @method init
18989		 * @param {String} selector CSS like selector expression.
18990		 */
18991		init: function(selector) {
18992			var match = this.match;
18993
18994			function compileNameFilter(name) {
18995				if (name) {
18996					name = name.toLowerCase();
18997
18998					return function(item) {
18999						return name === '*' || item.type === name;
19000					};
19001				}
19002			}
19003
19004			function compileIdFilter(id) {
19005				if (id) {
19006					return function(item) {
19007						return item._name === id;
19008					};
19009				}
19010			}
19011
19012			function compileClassesFilter(classes) {
19013				if (classes) {
19014					classes = classes.split('.');
19015
19016					return function(item) {
19017						var i = classes.length;
19018
19019						while (i--) {
19020							if (!item.hasClass(classes[i])) {
19021								return false;
19022							}
19023						}
19024
19025						return true;
19026					};
19027				}
19028			}
19029
19030			function compileAttrFilter(name, cmp, check) {
19031				if (name) {
19032					return function(item) {
19033						var value = item[name] ? item[name]() : '';
19034
19035						return !cmp ? !!check :
19036							cmp === "=" ? value === check :
19037							cmp === "*=" ? value.indexOf(check) >= 0 :
19038							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
19039							cmp === "!=" ? value != check :
19040							cmp === "^=" ? value.indexOf(check) === 0 :
19041							cmp === "$=" ? value.substr(value.length - check.length) === check :
19042							false;
19043					};
19044				}
19045			}
19046
19047			function compilePsuedoFilter(name) {
19048				var notSelectors;
19049
19050				if (name) {
19051					name = /(?:not\((.+)\))|(.+)/i.exec(name);
19052
19053					if (!name[1]) {
19054						name = name[2];
19055
19056						return function(item, index, length) {
19057							return name === 'first' ? index === 0 :
19058								name === 'last' ? index === length - 1 :
19059								name === 'even' ? index % 2 === 0 :
19060								name === 'odd' ? index % 2 === 1 :
19061								item[name] ? item[name]() :
19062								false;
19063						};
19064					} else {
19065						// Compile not expression
19066						notSelectors = parseChunks(name[1], []);
19067
19068						return function(item) {
19069							return !match(item, notSelectors);
19070						};
19071					}
19072				}
19073			}
19074
19075			function compile(selector, filters, direct) {
19076				var parts;
19077
19078				function add(filter) {
19079					if (filter) {
19080						filters.push(filter);
19081					}
19082				}
19083
19084				// Parse expression into parts
19085				parts = expression.exec(selector.replace(whiteSpace, ''));
19086
19087				add(compileNameFilter(parts[1]));
19088				add(compileIdFilter(parts[2]));
19089				add(compileClassesFilter(parts[3]));
19090				add(compileAttrFilter(parts[4], parts[5], parts[6]));
19091				add(compilePsuedoFilter(parts[7]));
19092
19093				// Mark the filter with psuedo for performance
19094				filters.psuedo = !!parts[7];
19095				filters.direct = direct;
19096
19097				return filters;
19098			}
19099
19100			// Parser logic based on Sizzle by John Resig
19101			function parseChunks(selector, selectors) {
19102				var parts = [], extra, matches, i;
19103
19104				do {
19105					chunker.exec("");
19106					matches = chunker.exec(selector);
19107
19108					if (matches) {
19109						selector = matches[3];
19110						parts.push(matches[1]);
19111
19112						if (matches[2]) {
19113							extra = matches[3];
19114							break;
19115						}
19116					}
19117				} while (matches);
19118
19119				if (extra) {
19120					parseChunks(extra, selectors);
19121				}
19122
19123				selector = [];
19124				for (i = 0; i < parts.length; i++) {
19125					if (parts[i] != '>') {
19126						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
19127					}
19128				}
19129
19130				selectors.push(selector);
19131
19132				return selectors;
19133			}
19134
19135			this._selectors = parseChunks(selector, []);
19136		},
19137
19138		/**
19139		 * Returns true/false if the selector matches the specified control.
19140		 *
19141		 * @method match
19142		 * @param {tinymce.ui.Control} control Control to match agains the selector.
19143		 * @param {Array} selectors Optional array of selectors, mostly used internally.
19144		 * @return {Boolean} true/false state if the control matches or not.
19145		 */
19146		match: function(control, selectors) {
19147			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
19148
19149			selectors = selectors || this._selectors;
19150			for (i = 0, l = selectors.length; i < l; i++) {
19151				selector = selectors[i];
19152				sl = selector.length;
19153				item = control;
19154				count = 0;
19155
19156				for (si = sl - 1; si >= 0; si--) {
19157					filters = selector[si];
19158
19159					while (item) {
19160						// Find the index and length since a psuedo filter like :first needs it
19161						if (filters.psuedo) {
19162							siblings = item.parent().items();
19163							index = length = siblings.length;
19164							while (index--) {
19165								if (siblings[index] === item) {
19166									break;
19167								}
19168							}
19169						}
19170
19171						for (fi = 0, fl = filters.length; fi < fl; fi++) {
19172							if (!filters[fi](item, index, length)) {
19173								fi = fl + 1;
19174								break;
19175							}
19176						}
19177
19178						if (fi === fl) {
19179							count++;
19180							break;
19181						} else {
19182							// If it didn't match the right most expression then
19183							// break since it's no point looking at the parents
19184							if (si === sl - 1) {
19185								break;
19186							}
19187						}
19188
19189						item = item.parent();
19190					}
19191				}
19192
19193				// If we found all selectors then return true otherwise continue looking
19194				if (count === sl) {
19195					return true;
19196				}
19197			}
19198
19199			return false;
19200		},
19201
19202		/**
19203		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
19204		 *
19205		 * @method find
19206		 * @param {tinymce.ui.Control} container Container to look for items in.
19207		 * @return {tinymce.ui.Collection} Collection with matched elements.
19208		 */
19209		find: function(container) {
19210			var matches = [], i, l, selectors = this._selectors;
19211
19212			function collect(items, selector, index) {
19213				var i, l, fi, fl, item, filters = selector[index];
19214
19215				for (i = 0, l = items.length; i < l; i++) {
19216					item = items[i];
19217
19218					// Run each filter agains the item
19219					for (fi = 0, fl = filters.length; fi < fl; fi++) {
19220						if (!filters[fi](item, i, l)) {
19221							fi = fl + 1;
19222							break;
19223						}
19224					}
19225
19226					// All filters matched the item
19227					if (fi === fl) {
19228						// Matched item is on the last expression like: panel toolbar [button]
19229						if (index == selector.length - 1) {
19230							matches.push(item);
19231						} else {
19232							// Collect next expression type
19233							if (item.items) {
19234								collect(item.items(), selector, index + 1);
19235							}
19236						}
19237					} else if (filters.direct) {
19238						return;
19239					}
19240
19241					// Collect child items
19242					if (item.items) {
19243						collect(item.items(), selector, index);
19244					}
19245				}
19246			}
19247
19248			if (container.items) {
19249				for (i = 0, l = selectors.length; i < l; i++) {
19250					collect(container.items(), selectors[i], 0);
19251				}
19252
19253				// Unique the matches if needed
19254				if (l > 1) {
19255					matches = unique(matches);
19256				}
19257			}
19258
19259			// Fix for circular reference
19260			if (!Collection) {
19261				// TODO: Fix me!
19262				Collection = Selector.Collection;
19263			}
19264
19265			return new Collection(matches);
19266		}
19267	});
19268
19269	return Selector;
19270});
19271
19272// Included from: js/tinymce/classes/ui/Collection.js
19273
19274/**
19275 * Collection.js
19276 *
19277 * Copyright, Moxiecode Systems AB
19278 * Released under LGPL License.
19279 *
19280 * License: http://www.tinymce.com/license
19281 * Contributing: http://www.tinymce.com/contributing
19282 */
19283
19284/**
19285 * Control collection, this class contains control instances and it enables you to
19286 * perform actions on all the contained items. This is very similar to how jQuery works.
19287 *
19288 * @example
19289 * someCollection.show().disabled(true);
19290 *
19291 * @class tinymce.ui.Collection
19292 */
19293define("tinymce/ui/Collection", [
19294	"tinymce/util/Tools",
19295	"tinymce/ui/Selector",
19296	"tinymce/util/Class"
19297], function(Tools, Selector, Class) {
19298	"use strict";
19299
19300	var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice;
19301
19302	proto = {
19303		/**
19304		 * Current number of contained control instances.
19305		 *
19306		 * @field length
19307		 * @type Number
19308		 */
19309		length: 0,
19310
19311		/**
19312		 * Constructor for the collection.
19313		 *
19314		 * @constructor
19315		 * @method init
19316		 * @param {Array} items Optional array with items to add.
19317		 */
19318		init: function(items) {
19319			if (items) {
19320				this.add(items);
19321			}
19322		},
19323
19324		/**
19325		 * Adds new items to the control collection.
19326		 *
19327		 * @method add
19328		 * @param {Array} items Array if items to add to collection.
19329		 * @return {tinymce.ui.Collection} Current collection instance.
19330		 */
19331		add: function(items) {
19332			var self = this;
19333
19334			// Force single item into array
19335			if (!Tools.isArray(items)) {
19336				if (items instanceof Collection) {
19337					self.add(items.toArray());
19338				} else {
19339					push.call(self, items);
19340				}
19341			} else {
19342				push.apply(self, items);
19343			}
19344
19345			return self;
19346		},
19347
19348		/**
19349		 * Sets the contents of the collection. This will remove any existing items
19350		 * and replace them with the ones specified in the input array.
19351		 *
19352		 * @method set
19353		 * @param {Array} items Array with items to set into the Collection.
19354		 * @return {tinymce.ui.Collection} Collection instance.
19355		 */
19356		set: function(items) {
19357			var self = this, len = self.length, i;
19358
19359			self.length = 0;
19360			self.add(items);
19361
19362			// Remove old entries
19363			for (i = self.length; i < len; i++) {
19364				delete self[i];
19365			}
19366
19367			return self;
19368		},
19369
19370		/**
19371		 * Filters the collection item based on the specified selector expression or selector function.
19372		 *
19373		 * @method filter
19374		 * @param {String} selector Selector expression to filter items by.
19375		 * @return {tinymce.ui.Collection} Collection containing the filtered items.
19376		 */
19377		filter: function(selector) {
19378			var self = this, i, l, matches = [], item, match;
19379
19380			// Compile string into selector expression
19381			if (typeof(selector) === "string") {
19382				selector = new Selector(selector);
19383
19384				match = function(item) {
19385					return selector.match(item);
19386				};
19387			} else {
19388				// Use selector as matching function
19389				match = selector;
19390			}
19391
19392			for (i = 0, l = self.length; i < l; i++) {
19393				item = self[i];
19394
19395				if (match(item)) {
19396					matches.push(item);
19397				}
19398			}
19399
19400			return new Collection(matches);
19401		},
19402
19403		/**
19404		 * Slices the items within the collection.
19405		 *
19406		 * @method slice
19407		 * @param {Number} index Index to slice at.
19408		 * @param {Number} len Optional length to slice.
19409		 * @return {tinymce.ui.Collection} Current collection.
19410		 */
19411		slice: function() {
19412			return new Collection(slice.apply(this, arguments));
19413		},
19414
19415		/**
19416		 * Makes the current collection equal to the specified index.
19417		 *
19418		 * @method eq
19419		 * @param {Number} index Index of the item to set the collection to.
19420		 * @return {tinymce.ui.Collection} Current collection.
19421		 */
19422		eq: function(index) {
19423			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
19424		},
19425
19426		/**
19427		 * Executes the specified callback on each item in collection.
19428		 *
19429		 * @method each
19430		 * @param {function} callback Callback to execute for each item in collection.
19431		 * @return {tinymce.ui.Collection} Current collection instance.
19432		 */
19433		each: function(callback) {
19434			Tools.each(this, callback);
19435
19436			return this;
19437		},
19438
19439		/**
19440		 * Returns an JavaScript array object of the contents inside the collection.
19441		 *
19442		 * @method toArray
19443		 * @return {Array} Array with all items from collection.
19444		 */
19445		toArray: function() {
19446			return Tools.toArray(this);
19447		},
19448
19449		/**
19450		 * Finds the index of the specified control or return -1 if it isn't in the collection.
19451		 *
19452		 * @method indexOf
19453		 * @param {Control} ctrl Control instance to look for.
19454		 * @return {Number} Index of the specified control or -1.
19455		 */
19456		indexOf: function(ctrl) {
19457			var self = this, i = self.length;
19458
19459			while (i--) {
19460				if (self[i] === ctrl) {
19461					break;
19462				}
19463			}
19464
19465			return i;
19466		},
19467
19468		/**
19469		 * Returns a new collection of the contents in reverse order.
19470		 *
19471		 * @method reverse
19472		 * @return {tinymce.ui.Collection} Collection instance with reversed items.
19473		 */
19474		reverse: function() {
19475			return new Collection(Tools.toArray(this).reverse());
19476		},
19477
19478		/**
19479		 * Returns true/false if the class exists or not.
19480		 *
19481		 * @method hasClass
19482		 * @param {String} cls Class to check for.
19483		 * @return {Boolean} true/false state if the class exists or not.
19484		 */
19485		hasClass: function(cls) {
19486			return this[0] ? this[0].hasClass(cls) : false;
19487		},
19488
19489		/**
19490		 * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>);
19491		 *
19492		 * @method prop
19493		 * @param {String} name Property name to get/set.
19494		 * @param {Object} value Optional object value to set.
19495		 * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation.
19496		 */
19497		prop: function(name, value) {
19498			var self = this, undef, item;
19499
19500			if (value !== undef) {
19501				self.each(function(item) {
19502					if (item[name]) {
19503						item[name](value);
19504					}
19505				});
19506
19507				return self;
19508			}
19509
19510			item = self[0];
19511
19512			if (item && item[name]) {
19513				return item[name]();
19514			}
19515		},
19516
19517		/**
19518		 * Executes the specific function name with optional arguments an all items in collection if it exists.
19519		 *
19520		 * @example collection.exec("myMethod", arg1, arg2, arg3);
19521		 * @method exec
19522		 * @param {String} name Name of the function to execute.
19523		 * @param {Object} ... Multiple arguments to pass to each function.
19524		 * @return {tinymce.ui.Collection} Current collection.
19525		 */
19526		exec: function(name) {
19527			var self = this, args = Tools.toArray(arguments).slice(1);
19528
19529			self.each(function(item) {
19530				if (item[name]) {
19531					item[name].apply(item, args);
19532				}
19533			});
19534
19535			return self;
19536		},
19537
19538		/**
19539		 * Remove all items from collection and DOM.
19540		 *
19541		 * @method remove
19542		 * @return {tinymce.ui.Collection} Current collection.
19543		 */
19544		remove: function() {
19545			var i = this.length;
19546
19547			while (i--) {
19548				this[i].remove();
19549			}
19550
19551			return this;
19552		}
19553
19554		/**
19555		 * Fires the specified event by name and arguments on the control. This will execute all
19556		 * bound event handlers.
19557		 *
19558		 * @method fire
19559		 * @param {String} name Name of the event to fire.
19560		 * @param {Object} args Optional arguments to pass to the event.
19561		 * @return {tinymce.ui.Collection} Current collection instance.
19562		 */
19563		// fire: function(event, args) {}, -- Generated by code below
19564
19565		/**
19566		 * Binds a callback to the specified event. This event can both be
19567		 * native browser events like "click" or custom ones like PostRender.
19568		 *
19569		 * The callback function will have two parameters the first one being the control that received the event
19570		 * the second one will be the event object either the browsers native event object or a custom JS object.
19571		 *
19572		 * @method on
19573		 * @param {String} name Name of the event to bind. For example "click".
19574		 * @param {String/function} callback Callback function to execute ones the event occurs.
19575		 * @return {tinymce.ui.Collection} Current collection instance.
19576		 */
19577		// on: function(name, callback) {}, -- Generated by code below
19578
19579		/**
19580		 * Unbinds the specified event and optionally a specific callback. If you omit the name
19581		 * parameter all event handlers will be removed. If you omit the callback all event handles
19582		 * by the specified name will be removed.
19583		 *
19584		 * @method off
19585		 * @param {String} name Optional name for the event to unbind.
19586		 * @param {function} callback Optional callback function to unbind.
19587		 * @return {tinymce.ui.Collection} Current collection instance.
19588		 */
19589		// off: function(name, callback) {}, -- Generated by code below
19590
19591		/**
19592		 * Shows the items in the current collection.
19593		 *
19594		 * @method show
19595		 * @return {tinymce.ui.Collection} Current collection instance.
19596		 */
19597		// show: function() {}, -- Generated by code below
19598
19599		/**
19600		 * Hides the items in the current collection.
19601		 *
19602		 * @method hide
19603		 * @return {tinymce.ui.Collection} Current collection instance.
19604		 */
19605		// hide: function() {}, -- Generated by code below
19606
19607		/**
19608		 * Sets/gets the text contents of the items in the current collection.
19609		 *
19610		 * @method text
19611		 * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation.
19612		 */
19613		// text: function(value) {}, -- Generated by code below
19614
19615		/**
19616		 * Sets/gets the name contents of the items in the current collection.
19617		 *
19618		 * @method name
19619		 * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation.
19620		 */
19621		// name: function(value) {}, -- Generated by code below
19622
19623		/**
19624		 * Sets/gets the disabled state on the items in the current collection.
19625		 *
19626		 * @method disabled
19627		 * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation.
19628		 */
19629		// disabled: function(state) {}, -- Generated by code below
19630
19631		/**
19632		 * Sets/gets the active state on the items in the current collection.
19633		 *
19634		 * @method active
19635		 * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation.
19636		 */
19637		// active: function(state) {}, -- Generated by code below
19638
19639		/**
19640		 * Sets/gets the selected state on the items in the current collection.
19641		 *
19642		 * @method selected
19643		 * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation.
19644		 */
19645		// selected: function(state) {}, -- Generated by code below
19646
19647		/**
19648		 * Sets/gets the selected state on the items in the current collection.
19649		 *
19650		 * @method visible
19651		 * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation.
19652		 */
19653		// visible: function(state) {}, -- Generated by code below
19654
19655		/**
19656		 * Adds a class to all items in the collection.
19657		 *
19658		 * @method addClass
19659		 * @param {String} cls Class to add to each item.
19660		 * @return {tinymce.ui.Collection} Current collection instance.
19661		 */
19662		// addClass: function(cls) {}, -- Generated by code below
19663
19664		/**
19665		 * Removes the specified class from all items in collection.
19666		 *
19667		 * @method removeClass
19668		 * @param {String} cls Class to remove from each item.
19669		 * @return {tinymce.ui.Collection} Current collection instance.
19670		 */
19671		// removeClass: function(cls) {}, -- Generated by code below
19672	};
19673
19674	// Extend tinymce.ui.Collection prototype with some generated control specific methods
19675	Tools.each('fire on off show hide addClass removeClass append prepend before after reflow'.split(' '), function(name) {
19676		proto[name] = function() {
19677			var args = Tools.toArray(arguments);
19678
19679			this.each(function(ctrl) {
19680				if (name in ctrl) {
19681					ctrl[name].apply(ctrl, args);
19682				}
19683			});
19684
19685			return this;
19686		};
19687	});
19688
19689	// Extend tinymce.ui.Collection prototype with some property methods
19690	Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) {
19691		proto[name] = function(value) {
19692			return this.prop(name, value);
19693		};
19694	});
19695
19696	// Create class based on the new prototype
19697	Collection = Class.extend(proto);
19698
19699	// Stick Collection into Selector to prevent circual references
19700	Selector.Collection = Collection;
19701
19702	return Collection;
19703});
19704
19705// Included from: js/tinymce/classes/ui/DomUtils.js
19706
19707/**
19708 * DOMUtils.js
19709 *
19710 * Copyright, Moxiecode Systems AB
19711 * Released under LGPL License.
19712 *
19713 * License: http://www.tinymce.com/license
19714 * Contributing: http://www.tinymce.com/contributing
19715 */
19716
19717define("tinymce/ui/DomUtils", [
19718	"tinymce/util/Tools",
19719	"tinymce/dom/DOMUtils"
19720], function(Tools, DOMUtils) {
19721	"use strict";
19722
19723	return {
19724		id: function() {
19725			return DOMUtils.DOM.uniqueId();
19726		},
19727
19728		createFragment: function(html) {
19729			return DOMUtils.DOM.createFragment(html);
19730		},
19731
19732		getWindowSize: function() {
19733			return DOMUtils.DOM.getViewPort();
19734		},
19735
19736		getSize: function(elm) {
19737			var width, height;
19738
19739			if (elm.getBoundingClientRect) {
19740				var rect = elm.getBoundingClientRect();
19741
19742				width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth);
19743				height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight);
19744			} else {
19745				width = elm.offsetWidth;
19746				height = elm.offsetHeight;
19747			}
19748
19749			return {width: width, height: height};
19750		},
19751
19752		getPos: function(elm, root) {
19753			return DOMUtils.DOM.getPos(elm, root);
19754		},
19755
19756		getViewPort: function(win) {
19757			return DOMUtils.DOM.getViewPort(win);
19758		},
19759
19760		get: function(id) {
19761			return document.getElementById(id);
19762		},
19763
19764		addClass : function(elm, cls) {
19765			return DOMUtils.DOM.addClass(elm, cls);
19766		},
19767
19768		removeClass : function(elm, cls) {
19769			return DOMUtils.DOM.removeClass(elm, cls);
19770		},
19771
19772		hasClass : function(elm, cls) {
19773			return DOMUtils.DOM.hasClass(elm, cls);
19774		},
19775
19776		toggleClass: function(elm, cls, state) {
19777			return DOMUtils.DOM.toggleClass(elm, cls, state);
19778		},
19779
19780		css: function(elm, name, value) {
19781			return DOMUtils.DOM.setStyle(elm, name, value);
19782		},
19783
19784		on: function(target, name, callback, scope) {
19785			return DOMUtils.DOM.bind(target, name, callback, scope);
19786		},
19787
19788		off: function(target, name, callback) {
19789			return DOMUtils.DOM.unbind(target, name, callback);
19790		},
19791
19792		fire: function(target, name, args) {
19793			return DOMUtils.DOM.fire(target, name, args);
19794		},
19795
19796		innerHtml: function(elm, html) {
19797			// Workaround for <div> in <p> bug on IE 8 #6178
19798			DOMUtils.DOM.setHTML(elm, html);
19799		}
19800	};
19801});
19802
19803// Included from: js/tinymce/classes/ui/Control.js
19804
19805/**
19806 * Control.js
19807 *
19808 * Copyright, Moxiecode Systems AB
19809 * Released under LGPL License.
19810 *
19811 * License: http://www.tinymce.com/license
19812 * Contributing: http://www.tinymce.com/contributing
19813 */
19814
19815/**
19816 * This is the base class for all controls and containers. All UI control instances inherit
19817 * from this one as it has the base logic needed by all of them.
19818 *
19819 * @class tinymce.ui.Control
19820 */
19821define("tinymce/ui/Control", [
19822	"tinymce/util/Class",
19823	"tinymce/util/Tools",
19824	"tinymce/ui/Collection",
19825	"tinymce/ui/DomUtils"
19826], function(Class, Tools, Collection, DomUtils) {
19827	"use strict";
19828
19829	var nativeEvents = Tools.makeMap("focusin focusout scroll click dblclick mousedown mouseup mousemove mouseover" +
19830								" mouseout mouseenter mouseleave wheel keydown keypress keyup contextmenu", " ");
19831
19832	var elementIdCache = {};
19833	var hasMouseWheelEventSupport = "onmousewheel" in document;
19834	var hasWheelEventSupport = false;
19835
19836	var Control = Class.extend({
19837		Statics: {
19838			controlIdLookup: {},
19839			elementIdCache: elementIdCache
19840		},
19841
19842		isRtl: function() {
19843			return Control.rtl;
19844		},
19845
19846		/**
19847		 * Class/id prefix to use for all controls.
19848		 *
19849		 * @final
19850		 * @field {String} classPrefix
19851		 */
19852		classPrefix: "mce-",
19853
19854		/**
19855		 * Constructs a new control instance with the specified settings.
19856		 *
19857		 * @constructor
19858		 * @param {Object} settings Name/value object with settings.
19859		 * @setting {String} style Style CSS properties to add.
19860		 * @setting {String} border Border box values example: 1 1 1 1
19861		 * @setting {String} padding Padding box values example: 1 1 1 1
19862		 * @setting {String} margin Margin box values example: 1 1 1 1
19863		 * @setting {Number} minWidth Minimal width for the control.
19864		 * @setting {Number} minHeight Minimal height for the control.
19865		 * @setting {String} classes Space separated list of classes to add.
19866		 * @setting {String} role WAI-ARIA role to use for control.
19867		 * @setting {Boolean} hidden Is the control hidden by default.
19868		 * @setting {Boolean} disabled Is the control disabled by default.
19869		 * @setting {String} name Name of the control instance.
19870		 */
19871		init: function(settings) {
19872			var self = this, classes, i;
19873
19874			self.settings = settings = Tools.extend({}, self.Defaults, settings);
19875
19876			// Initial states
19877			self._id = DomUtils.id();
19878			self._text = self._name = '';
19879			self._width = self._height = 0;
19880			self._aria = {role: settings.role};
19881
19882			// Setup classes
19883			classes = settings.classes;
19884			if (classes) {
19885				classes = classes.split(' ');
19886				classes.map = {};
19887				i = classes.length;
19888				while (i--) {
19889					classes.map[classes[i]] = true;
19890				}
19891			}
19892
19893			self._classes = classes || [];
19894			self.visible(true);
19895
19896			// Set some properties
19897			Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
19898				var value = settings[name], undef;
19899
19900				if (value !== undef) {
19901					self[name](value);
19902				} else if (self['_' + name] === undef) {
19903					self['_' + name] = false;
19904				}
19905			});
19906
19907			self.on('click', function() {
19908				if (self.disabled()) {
19909					return false;
19910				}
19911			});
19912
19913			// TODO: Is this needed duplicate code see above?
19914			if (settings.classes) {
19915				Tools.each(settings.classes.split(' '), function(cls) {
19916					self.addClass(cls);
19917				});
19918			}
19919
19920			/**
19921			 * Name/value object with settings for the current control.
19922			 *
19923			 * @field {Object} settings
19924			 */
19925			self.settings = settings;
19926
19927			self._borderBox = self.parseBox(settings.border);
19928			self._paddingBox = self.parseBox(settings.padding);
19929			self._marginBox = self.parseBox(settings.margin);
19930
19931			if (settings.hidden) {
19932				self.hide();
19933			}
19934		},
19935
19936		// Will generate getter/setter methods for these properties
19937		Properties: 'parent,title,text,width,height,disabled,active,name,value',
19938
19939		// Will generate empty dummy functions for these
19940		Methods: 'renderHtml',
19941
19942		/**
19943		 * Returns the root element to render controls into.
19944		 *
19945		 * @method getContainerElm
19946		 * @return {Element} HTML DOM element to render into.
19947		 */
19948		getContainerElm: function() {
19949			return document.body;
19950		},
19951
19952		/**
19953		 * Returns a control instance for the current DOM element.
19954		 *
19955		 * @method getParentCtrl
19956		 * @param {Element} elm HTML dom element to get parent control from.
19957		 * @return {tinymce.ui.Control} Control instance or undefined.
19958		 */
19959		getParentCtrl: function(elm) {
19960			var ctrl;
19961
19962			while (elm) {
19963				ctrl = Control.controlIdLookup[elm.id];
19964				if (ctrl) {
19965					break;
19966				}
19967
19968				elm = elm.parentNode;
19969			}
19970
19971			return ctrl;
19972		},
19973
19974		/**
19975		 * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
19976		 *
19977		 * @method parseBox
19978		 * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
19979		 * @return {Object} Object with top/right/bottom/left properties.
19980		 * @private
19981		 */
19982		parseBox: function(value) {
19983			var len, radix = 10;
19984
19985			if (!value) {
19986				return;
19987			}
19988
19989			if (typeof(value) === "number") {
19990				value = value || 0;
19991
19992				return {
19993					top: value,
19994					left: value,
19995					bottom: value,
19996					right: value
19997				};
19998			}
19999
20000			value = value.split(' ');
20001			len = value.length;
20002
20003			if (len === 1) {
20004				value[1] = value[2] = value[3] = value[0];
20005			} else if (len === 2) {
20006				value[2] = value[0];
20007				value[3] = value[1];
20008			} else if (len === 3) {
20009				value[3] = value[1];
20010			}
20011
20012			return {
20013				top: parseInt(value[0], radix) || 0,
20014				right: parseInt(value[1], radix) || 0,
20015				bottom: parseInt(value[2], radix) || 0,
20016				left: parseInt(value[3], radix) || 0
20017			};
20018		},
20019
20020		borderBox: function() {
20021			return this._borderBox;
20022		},
20023
20024		paddingBox: function() {
20025			return this._paddingBox;
20026		},
20027
20028		marginBox: function() {
20029			return this._marginBox;
20030		},
20031
20032		measureBox: function(elm, prefix) {
20033			function getStyle(name) {
20034				var defaultView = document.defaultView;
20035
20036				if (defaultView) {
20037					// Remove camelcase
20038					name = name.replace(/[A-Z]/g, function(a) {
20039						return '-' + a;
20040					});
20041
20042					return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
20043				}
20044
20045				return elm.currentStyle[name];
20046			}
20047
20048			function getSide(name) {
20049				var val = parseFloat(getStyle(name), 10);
20050
20051				return isNaN(val) ? 0 : val;
20052			}
20053
20054			return {
20055				top: getSide(prefix + "TopWidth"),
20056				right: getSide(prefix + "RightWidth"),
20057				bottom: getSide(prefix + "BottomWidth"),
20058				left: getSide(prefix + "LeftWidth")
20059			};
20060		},
20061
20062		/**
20063		 * Initializes the current controls layout rect.
20064		 * This will be executed by the layout managers to determine the
20065		 * default minWidth/minHeight etc.
20066		 *
20067		 * @method initLayoutRect
20068		 * @return {Object} Layout rect instance.
20069		 */
20070		initLayoutRect: function() {
20071			var self = this, settings = self.settings, borderBox, layoutRect;
20072			var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
20073			var startMinWidth, startMinHeight, initialSize;
20074
20075			// Measure the current element
20076			borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
20077			self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
20078			self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
20079			initialSize = DomUtils.getSize(elm);
20080
20081			// Setup minWidth/minHeight and width/height
20082			startMinWidth = settings.minWidth;
20083			startMinHeight = settings.minHeight;
20084			minWidth = startMinWidth || initialSize.width;
20085			minHeight = startMinHeight || initialSize.height;
20086			width = settings.width;
20087			height = settings.height;
20088			autoResize = settings.autoResize;
20089			autoResize = typeof(autoResize) != "undefined" ? autoResize : !width && !height;
20090
20091			width = width || minWidth;
20092			height = height || minHeight;
20093
20094			var deltaW = borderBox.left + borderBox.right;
20095			var deltaH = borderBox.top + borderBox.bottom;
20096
20097			var maxW = settings.maxWidth || 0xFFFF;
20098			var maxH = settings.maxHeight || 0xFFFF;
20099
20100			// Setup initial layout rect
20101			self._layoutRect = layoutRect = {
20102				x: settings.x || 0,
20103				y: settings.y || 0,
20104				w: width,
20105				h: height,
20106				deltaW: deltaW,
20107				deltaH: deltaH,
20108				contentW: width - deltaW,
20109				contentH: height - deltaH,
20110				innerW: width - deltaW,
20111				innerH: height - deltaH,
20112				startMinWidth: startMinWidth || 0,
20113				startMinHeight: startMinHeight || 0,
20114				minW: Math.min(minWidth, maxW),
20115				minH: Math.min(minHeight, maxH),
20116				maxW: maxW,
20117				maxH: maxH,
20118				autoResize: autoResize,
20119				scrollW: 0
20120			};
20121
20122			self._lastLayoutRect = {};
20123
20124			return layoutRect;
20125		},
20126
20127		/**
20128		 * Getter/setter for the current layout rect.
20129		 *
20130		 * @method layoutRect
20131		 * @param {Object} [newRect] Optional new layout rect.
20132		 * @return {tinymce.ui.Control/Object} Current control or rect object.
20133		 */
20134		layoutRect: function(newRect) {
20135			var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
20136
20137			// Initialize default layout rect
20138			if (!curRect) {
20139				curRect = self.initLayoutRect();
20140			}
20141
20142			// Set new rect values
20143			if (newRect) {
20144				// Calc deltas between inner and outer sizes
20145				deltaWidth = curRect.deltaW;
20146				deltaHeight = curRect.deltaH;
20147
20148				// Set x position
20149				if (newRect.x !== undef) {
20150					curRect.x = newRect.x;
20151				}
20152
20153				// Set y position
20154				if (newRect.y !== undef) {
20155					curRect.y = newRect.y;
20156				}
20157
20158				// Set minW
20159				if (newRect.minW !== undef) {
20160					curRect.minW = newRect.minW;
20161				}
20162
20163				// Set minH
20164				if (newRect.minH !== undef) {
20165					curRect.minH = newRect.minH;
20166				}
20167
20168				// Set new width and calculate inner width
20169				size = newRect.w;
20170				if (size !== undef) {
20171					size = size < curRect.minW ? curRect.minW : size;
20172					size = size > curRect.maxW ? curRect.maxW : size;
20173					curRect.w = size;
20174					curRect.innerW = size - deltaWidth;
20175				}
20176
20177				// Set new height and calculate inner height
20178				size = newRect.h;
20179				if (size !== undef) {
20180					size = size < curRect.minH ? curRect.minH : size;
20181					size = size > curRect.maxH ? curRect.maxH : size;
20182					curRect.h = size;
20183					curRect.innerH = size - deltaHeight;
20184				}
20185
20186				// Set new inner width and calculate width
20187				size = newRect.innerW;
20188				if (size !== undef) {
20189					size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
20190					size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
20191					curRect.innerW = size;
20192					curRect.w = size + deltaWidth;
20193				}
20194
20195				// Set new height and calculate inner height
20196				size = newRect.innerH;
20197				if (size !== undef) {
20198					size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
20199					size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
20200					curRect.innerH = size;
20201					curRect.h = size + deltaHeight;
20202				}
20203
20204				// Set new contentW
20205				if (newRect.contentW !== undef) {
20206					curRect.contentW = newRect.contentW;
20207				}
20208
20209				// Set new contentH
20210				if (newRect.contentH !== undef) {
20211					curRect.contentH = newRect.contentH;
20212				}
20213
20214				// Compare last layout rect with the current one to see if we need to repaint or not
20215				lastLayoutRect = self._lastLayoutRect;
20216				if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
20217					lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
20218					repaintControls = Control.repaintControls;
20219
20220					if (repaintControls) {
20221						if (repaintControls.map && !repaintControls.map[self._id]) {
20222							repaintControls.push(self);
20223							repaintControls.map[self._id] = true;
20224						}
20225					}
20226
20227					lastLayoutRect.x = curRect.x;
20228					lastLayoutRect.y = curRect.y;
20229					lastLayoutRect.w = curRect.w;
20230					lastLayoutRect.h = curRect.h;
20231				}
20232
20233				return self;
20234			}
20235
20236			return curRect;
20237		},
20238
20239		/**
20240		 * Repaints the control after a layout operation.
20241		 *
20242		 * @method repaint
20243		 */
20244		repaint: function() {
20245			var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
20246
20247			// Use Math.round on all values on IE < 9
20248			round = !document.createRange ? Math.round : function(value) {
20249				return value;
20250			};
20251
20252			style = self.getEl().style;
20253			rect = self._layoutRect;
20254			lastRepaintRect = self._lastRepaintRect || {};
20255
20256			borderBox = self._borderBox;
20257			borderW = borderBox.left + borderBox.right;
20258			borderH = borderBox.top + borderBox.bottom;
20259
20260			if (rect.x !== lastRepaintRect.x) {
20261				style.left = round(rect.x) + 'px';
20262				lastRepaintRect.x = rect.x;
20263			}
20264
20265			if (rect.y !== lastRepaintRect.y) {
20266				style.top = round(rect.y) + 'px';
20267				lastRepaintRect.y = rect.y;
20268			}
20269
20270			if (rect.w !== lastRepaintRect.w) {
20271				style.width = round(rect.w - borderW) + 'px';
20272				lastRepaintRect.w = rect.w;
20273			}
20274
20275			if (rect.h !== lastRepaintRect.h) {
20276				style.height = round(rect.h - borderH) + 'px';
20277				lastRepaintRect.h = rect.h;
20278			}
20279
20280			// Update body if needed
20281			if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
20282				bodyStyle = self.getEl('body').style;
20283				bodyStyle.width = round(rect.innerW) + 'px';
20284				lastRepaintRect.innerW = rect.innerW;
20285			}
20286
20287			if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
20288				bodyStyle = bodyStyle || self.getEl('body').style;
20289				bodyStyle.height = round(rect.innerH) + 'px';
20290				lastRepaintRect.innerH = rect.innerH;
20291			}
20292
20293			self._lastRepaintRect = lastRepaintRect;
20294			self.fire('repaint', {}, false);
20295		},
20296
20297		/**
20298		 * Binds a callback to the specified event. This event can both be
20299		 * native browser events like "click" or custom ones like PostRender.
20300		 *
20301		 * The callback function will be passed a DOM event like object that enables yout do stop propagation.
20302		 *
20303		 * @method on
20304		 * @param {String} name Name of the event to bind. For example "click".
20305		 * @param {String/function} callback Callback function to execute ones the event occurs.
20306		 * @return {tinymce.ui.Control} Current control object.
20307		 */
20308		on: function(name, callback) {
20309			var self = this, bindings, handlers, names, i;
20310
20311			function resolveCallbackName(name) {
20312				var callback, scope;
20313
20314				return function(e) {
20315					if (!callback) {
20316						self.parents().each(function(ctrl) {
20317							var callbacks = ctrl.settings.callbacks;
20318
20319							if (callbacks && (callback = callbacks[name])) {
20320								scope = ctrl;
20321								return false;
20322							}
20323						});
20324					}
20325
20326					return callback.call(scope, e);
20327				};
20328			}
20329
20330			if (callback) {
20331				if (typeof(callback) == 'string') {
20332					callback = resolveCallbackName(callback);
20333				}
20334
20335				names = name.toLowerCase().split(' ');
20336				i = names.length;
20337				while (i--) {
20338					name = names[i];
20339
20340					bindings = self._bindings;
20341					if (!bindings) {
20342						bindings = self._bindings = {};
20343					}
20344
20345					handlers = bindings[name];
20346					if (!handlers) {
20347						handlers = bindings[name] = [];
20348					}
20349
20350					handlers.push(callback);
20351
20352					if (nativeEvents[name]) {
20353						if (!self._nativeEvents) {
20354							self._nativeEvents = {name: true};
20355						} else {
20356							self._nativeEvents[name] = true;
20357						}
20358
20359						if (self._rendered) {
20360							self.bindPendingEvents();
20361						}
20362					}
20363				}
20364			}
20365
20366			return self;
20367		},
20368
20369		/**
20370		 * Unbinds the specified event and optionally a specific callback. If you omit the name
20371		 * parameter all event handlers will be removed. If you omit the callback all event handles
20372		 * by the specified name will be removed.
20373		 *
20374		 * @method off
20375		 * @param {String} [name] Name for the event to unbind.
20376		 * @param {function} [callback] Callback function to unbind.
20377		 * @return {mxex.ui.Control} Current control object.
20378		 */
20379		off: function(name, callback) {
20380			var self = this, i, bindings = self._bindings, handlers, bindingName, names, hi;
20381
20382			if (bindings) {
20383				if (name) {
20384					names = name.toLowerCase().split(' ');
20385					i = names.length;
20386					while (i--) {
20387						name = names[i];
20388						handlers = bindings[name];
20389
20390						// Unbind all handlers
20391						if (!name) {
20392							for (bindingName in bindings) {
20393								bindings[bindingName].length = 0;
20394							}
20395
20396							return self;
20397						}
20398
20399						if (handlers) {
20400							// Unbind all by name
20401							if (!callback) {
20402								handlers.length = 0;
20403							} else {
20404								// Unbind specific ones
20405								hi = handlers.length;
20406								while (hi--) {
20407									if (handlers[hi] === callback) {
20408										handlers.splice(hi, 1);
20409									}
20410								}
20411							}
20412						}
20413					}
20414				} else {
20415					self._bindings = [];
20416				}
20417			}
20418
20419			return self;
20420		},
20421
20422		/**
20423		 * Fires the specified event by name and arguments on the control. This will execute all
20424		 * bound event handlers.
20425		 *
20426		 * @method fire
20427		 * @param {String} name Name of the event to fire.
20428		 * @param {Object} [args] Arguments to pass to the event.
20429		 * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
20430		 * @return {Object} Current arguments object.
20431		 */
20432		fire: function(name, args, bubble) {
20433			var self = this, i, l, handlers, parentCtrl;
20434
20435			name = name.toLowerCase();
20436
20437			// Dummy function that gets replaced on the delegation state functions
20438			function returnFalse() {
20439				return false;
20440			}
20441
20442			// Dummy function that gets replaced on the delegation state functions
20443			function returnTrue() {
20444				return true;
20445			}
20446
20447			// Setup empty object if args is omited
20448			args = args || {};
20449
20450			// Stick type into event object
20451			if (!args.type) {
20452				args.type = name;
20453			}
20454
20455			// Stick control into event
20456			if (!args.control) {
20457				args.control = self;
20458			}
20459
20460			// Add event delegation methods if they are missing
20461			if (!args.preventDefault) {
20462				// Add preventDefault method
20463				args.preventDefault = function() {
20464					args.isDefaultPrevented = returnTrue;
20465				};
20466
20467				// Add stopPropagation
20468				args.stopPropagation = function() {
20469					args.isPropagationStopped = returnTrue;
20470				};
20471
20472				// Add stopImmediatePropagation
20473				args.stopImmediatePropagation = function() {
20474					args.isImmediatePropagationStopped = returnTrue;
20475				};
20476
20477				// Add event delegation states
20478				args.isDefaultPrevented = returnFalse;
20479				args.isPropagationStopped = returnFalse;
20480				args.isImmediatePropagationStopped = returnFalse;
20481			}
20482
20483			if (self._bindings) {
20484				handlers = self._bindings[name];
20485
20486				if (handlers) {
20487					for (i = 0, l = handlers.length; i < l; i++) {
20488						// Execute callback and break if the callback returns a fal
20488se
20489						if (!args.isImmediatePropagationStopped() && handlers[i].call(self, args) === false) {
20490							break;
20491						}
20492					}
20493				}
20494			}
20495
20496			// Bubble event up to parent controls
20497			if (bubble !== false) {
20498				parentCtrl = self.parent();
20499				while (parentCtrl && !args.isPropagationStopped()) {
20500					parentCtrl.fire(name, args, false);
20501					parentCtrl = parentCtrl.parent();
20502				}
20503			}
20504
20505			return args;
20506		},
20507
20508		/**
20509		 * Returns true/false if the specified event has any listeners.
20510		 *
20511		 * @method hasEventListeners
20512		 * @param {String} name Name of the event to check for.
20513		 * @return {Boolean} True/false state if the event has listeners.
20514		 */
20515		hasEventListeners: function(name) {
20516			return name in this._bindings;
20517		},
20518
20519		/**
20520		 * Returns a control collection with all parent controls.
20521		 *
20522		 * @method parents
20523		 * @param {String} selector Optional selector expression to find parents.
20524		 * @return {tinymce.ui.Collection} Collection with all parent controls.
20525		 */
20526		parents: function(selector) {
20527			var ctrl = this, parents = new Collection();
20528
20529			// Add each parent to collection
20530			for (ctrl = ctrl.parent(); ctrl; ctrl = ctrl.parent()) {
20531				parents.add(ctrl);
20532			}
20533
20534			// Filter away everything that doesn't match the selector
20535			if (selector) {
20536				parents = parents.filter(selector);
20537			}
20538
20539			return parents;
20540		},
20541
20542		/**
20543		 * Returns the control next to the current control.
20544		 *
20545		 * @method next
20546		 * @return {tinymce.ui.Control} Next control instance.
20547		 */
20548		next: function() {
20549			var parentControls = this.parent().items();
20550
20551			return parentControls[parentControls.indexOf(this) + 1];
20552		},
20553
20554		/**
20555		 * Returns the control previous to the current control.
20556		 *
20557		 * @method prev
20558		 * @return {tinymce.ui.Control} Previous control instance.
20559		 */
20560		prev: function() {
20561			var parentControls = this.parent().items();
20562
20563			return parentControls[parentControls.indexOf(this) - 1];
20564		},
20565
20566		/**
20567		 * Find the common ancestor for two control instances.
20568		 *
20569		 * @method findCommonAncestor
20570		 * @param {tinymce.ui.Control} ctrl1 First control.
20571		 * @param {tinymce.ui.Control} ctrl2 Second control.
20572		 * @return {tinymce.ui.Control} Ancestor control instance.
20573		 */
20574		findCommonAncestor: function(ctrl1, ctrl2) {
20575			var parentCtrl;
20576
20577			while (ctrl1) {
20578				parentCtrl = ctrl2;
20579
20580				while (parentCtrl && ctrl1 != parentCtrl) {
20581					parentCtrl = parentCtrl.parent();
20582				}
20583
20584				if (ctrl1 == parentCtrl) {
20585					break;
20586				}
20587
20588				ctrl1 = ctrl1.parent();
20589			}
20590
20591			return ctrl1;
20592		},
20593
20594		/**
20595		 * Returns true/false if the specific control has the specific class.
20596		 *
20597		 * @method hasClass
20598		 * @param {String} cls Class to check for.
20599		 * @param {String} [group] Sub element group name.
20600		 * @return {Boolean} True/false if the control has the specified class.
20601		 */
20602		hasClass: function(cls, group) {
20603			var classes = this._classes[group || 'control'];
20604
20605			cls = this.classPrefix + cls;
20606
20607			return classes && !!classes.map[cls];
20608		},
20609
20610		/**
20611		 * Adds the specified class to the control
20612		 *
20613		 * @method addClass
20614		 * @param {String} cls Class to check for.
20615		 * @param {String} [group] Sub element group name.
20616		 * @return {tinymce.ui.Control} Current control object.
20617		 */
20618		addClass: function(cls, group) {
20619			var self = this, classes, elm;
20620
20621			cls = this.classPrefix + cls;
20622			classes = self._classes[group || 'control'];
20623
20624			if (!classes) {
20625				classes = [];
20626				classes.map = {};
20627				self._classes[group || 'control'] = classes;
20628			}
20629
20630			if (!classes.map[cls]) {
20631				classes.map[cls] = cls;
20632				classes.push(cls);
20633
20634				if (self._rendered) {
20635					elm = self.getEl(group);
20636
20637					if (elm) {
20638						elm.className = classes.join(' ');
20639					}
20640				}
20641			}
20642
20643			return self;
20644		},
20645
20646		/**
20647		 * Removes the specified class from the control.
20648		 *
20649		 * @method removeClass
20650		 * @param {String} cls Class to remove.
20651		 * @param {String} [group] Sub element group name.
20652		 * @return {tinymce.ui.Control} Current control object.
20653		 */
20654		removeClass: function(cls, group) {
20655			var self = this, classes, i, elm;
20656
20657			cls = this.classPrefix + cls;
20658			classes = self._classes[group || 'control'];
20659			if (classes && classes.map[cls]) {
20660				delete classes.map[cls];
20661
20662				i = classes.length;
20663				while (i--) {
20664					if (classes[i] === cls) {
20665						classes.splice(i, 1);
20666					}
20667				}
20668			}
20669
20670			if (self._rendered) {
20671				elm = self.getEl(group);
20672
20673				if (elm) {
20674					elm.className = classes.join(' ');
20675				}
20676			}
20677
20678			return self;
20679		},
20680
20681		/**
20682		 * Toggles the specified class on the control.
20683		 *
20684		 * @method toggleClass
20685		 * @param {String} cls Class to remove.
20686		 * @param {Boolean} state True/false state to add/remove class.
20687		 * @param {String} [group] Sub element group name.
20688		 * @return {tinymce.ui.Control} Current control object.
20689		 */
20690		toggleClass: function(cls, state, group) {
20691			var self = this;
20692
20693			if (state) {
20694				self.addClass(cls, group);
20695			} else {
20696				self.removeClass(cls, group);
20697			}
20698
20699			return self;
20700		},
20701
20702		/**
20703		 * Returns the class string for the specified group name.
20704		 *
20705		 * @method classes
20706		 * @param {String} [group] Group to get clases by.
20707		 * @return {String} Classes for the specified group.
20708		 */
20709		classes: function(group) {
20710			var classes = this._classes[group || 'control'];
20711
20712			return classes ? classes.join(' ') : '';
20713		},
20714
20715		/**
20716		 * Sets the inner HTML of the control element.
20717		 *
20718		 * @method innerHtml
20719		 * @param {String} html Html string to set as inner html.
20720		 * @return {tinymce.ui.Control} Current control object.
20721		 */
20722		innerHtml: function(html) {
20723			DomUtils.innerHtml(this.getEl(), html);
20724			return this;
20725		},
20726
20727		/**
20728		 * Returns the control DOM element or sub element.
20729		 *
20730		 * @method getEl
20731		 * @param {String} [suffix] Suffix to get element by.
20732		 * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
20733		 * @return {Element} HTML DOM element for the current control or it's children.
20734		 */
20735		getEl: function(suffix, dropCache) {
20736			var elm, id = suffix ? this._id + '-' + suffix : this._id;
20737
20738			elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
20739
20740			return elm;
20741		},
20742
20743		/**
20744		 * Sets/gets the visible for the control.
20745		 *
20746		 * @method visible
20747		 * @param {Boolean} state Value to set to control.
20748		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
20749		 */
20750		visible: function(state) {
20751			var self = this, parentCtrl;
20752
20753			if (typeof(state) !== "undefined") {
20754				if (self._visible !== state) {
20755					if (self._rendered) {
20756						self.getEl().style.display = state ? '' : 'none';
20757					}
20758
20759					self._visible = state;
20760
20761					// Parent container needs to reflow
20762					parentCtrl = self.parent();
20763					if (parentCtrl) {
20764						parentCtrl._lastRect = null;
20765					}
20766
20767					self.fire(state ? 'show' : 'hide');
20768				}
20769
20770				return self;
20771			}
20772
20773			return self._visible;
20774		},
20775
20776		/**
20777		 * Sets the visible state to true.
20778		 *
20779		 * @method show
20780		 * @return {tinymce.ui.Control} Current control instance.
20781		 */
20782		show: function() {
20783			return this.visible(true);
20784		},
20785
20786		/**
20787		 * Sets the visible state to false.
20788		 *
20789		 * @method hide
20790		 * @return {tinymce.ui.Control} Current control instance.
20791		 */
20792		hide: function() {
20793			return this.visible(false);
20794		},
20795
20796		/**
20797		 * Focuses the current control.
20798		 *
20799		 * @method focus
20800		 * @return {tinymce.ui.Control} Current control instance.
20801		 */
20802		focus: function() {
20803			try {
20804				this.getEl().focus();
20805			} catch (ex) {
20806				// Ignore IE error
20807			}
20808
20809			return this;
20810		},
20811
20812		/**
20813		 * Blurs the current control.
20814		 *
20815		 * @method blur
20816		 * @return {tinymce.ui.Control} Current control instance.
20817		 */
20818		blur: function() {
20819			this.getEl().blur();
20820
20821			return this;
20822		},
20823
20824		/**
20825		 * Sets the specified aria property.
20826		 *
20827		 * @method aria
20828		 * @param {String} name Name of the aria property to set.
20829		 * @param {String} value Value of the aria property.
20830		 * @return {tinymce.ui.Control} Current control instance.
20831		 */
20832		aria: function(name, value) {
20833			var self = this, elm = self.getEl();
20834
20835			if (typeof(value) === "undefined") {
20836				return self._aria[name];
20837			} else {
20838				self._aria[name] = value;
20839			}
20840
20841			if (self._rendered) {
20842				if (name == 'label') {
20843					elm.setAttribute('aria-labeledby', self._id);
20844				}
20845
20846				elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
20847			}
20848
20849			return self;
20850		},
20851
20852		/**
20853		 * Encodes the specified string with HTML entities. It will also
20854		 * translate the string to different languages.
20855		 *
20856		 * @method encode
20857		 * @param {String/Object/Array} text Text to entity encode.
20858		 * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
20859		 * @return {String} Encoded and possible traslated string. 
20860		 */
20861		encode: function(text, translate) {
20862			if (translate !== false && Control.translate) {
20863				text = Control.translate(text);
20864			}
20865
20866			return (text || '').replace(/[&<>"]/g, function(match) {
20867				return '&#' + match.charCodeAt(0) + ';';
20868			});
20869		},
20870
20871		/**
20872		 * Adds items before the current control.
20873		 *
20874		 * @method before
20875		 * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
20876		 * @return {tinymce.ui.Control} Current control instance.
20877		 */
20878		before: function(items) {
20879			var self = this, parent = self.parent();
20880
20881			if (parent) {
20882				parent.insert(items, parent.items().indexOf(self), true);
20883			}
20884
20885			return self;
20886		},
20887
20888		/**
20889		 * Adds items after the current control.
20890		 *
20891		 * @method after
20892		 * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
20893		 * @return {tinymce.ui.Control} Current control instance.
20894		 */
20895		after: function(items) {
20896			var self = this, parent = self.parent();
20897
20898			if (parent) {
20899				parent.insert(items, parent.items().indexOf(self));
20900			}
20901
20902			return self;
20903		},
20904
20905		/**
20906		 * Removes the current control from DOM and from UI collections.
20907		 *
20908		 * @method remove
20909		 * @return {tinymce.ui.Control} Current control instance.
20910		 */
20911		remove: function() {
20912			var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
20913
20914			if (self.items) {
20915				var controls = self.items().toArray();
20916				i = controls.length;
20917				while (i--) {
20918					controls[i].remove();
20919				}
20920			}
20921
20922			if (parent && parent.items) {
20923				newItems = [];
20924
20925				parent.items().each(function(item) {
20926					if (item !== self) {
20927						newItems.push(item);
20928					}
20929				});
20930
20931				parent.items().set(newItems);
20932				parent._lastRect = null;
20933			}
20934
20935			if (self._eventsRoot && self._eventsRoot == self) {
20936				DomUtils.off(elm);
20937			}
20938
20939			delete Control.controlIdLookup[self._id];
20940			delete elementIdCache[self._id];
20941
20942			if (elm && elm.parentNode) {
20943				var nodes = elm.getElementsByTagName('*');
20944
20945				i = nodes.length;
20946				while (i--) {
20947					delete elementIdCache[nodes[i].id];
20948				}
20949
20950				elm.parentNode.removeChild(elm);
20951			}
20952
20953			return self;
20954		},
20955
20956		/**
20957		 * Renders the control before the specified element.
20958		 *
20959		 * @method renderBefore
20960		 * @param {Element} elm Element to render before.
20961		 * @return {tinymce.ui.Control} Current control instance.
20962		 */
20963		renderBefore: function(elm) {
20964			var self = this;
20965
20966			elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
20967			self.postRender();
20968
20969			return self;
20970		},
20971
20972		/**
20973		 * Renders the control to the specified element.
20974		 *
20975		 * @method renderBefore
20976		 * @param {Element} elm Element to render to.
20977		 * @return {tinymce.ui.Control} Current control instance.
20978		 */
20979		renderTo: function(elm) {
20980			var self = this;
20981
20982			elm = elm || self.getContainerElm();
20983			elm.appendChild(DomUtils.createFragment(self.renderHtml()));
20984			self.postRender();
20985
20986			return self;
20987		},
20988
20989		/**
20990		 * Post render method. Called after the control has been rendered to the target.
20991		 *
20992		 * @method postRender
20993		 * @return {tinymce.ui.Control} Current control instance.
20994		 */
20995		postRender: function() {
20996			var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
20997
20998			// Bind on<event> settings
20999			for (name in settings) {
21000				if (name.indexOf("on") === 0) {
21001					self.on(name.substr(2), settings[name]);
21002				}
21003			}
21004
21005			if (self._eventsRoot) {
21006				for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
21007					parentEventsRoot = parent._eventsRoot;
21008				}
21009
21010				if (parentEventsRoot) {
21011					for (name in parentEventsRoot._nativeEvents) {
21012						self._nativeEvents[name] = true;
21013					}
21014				}
21015			}
21016
21017			self.bindPendingEvents();
21018
21019			if (settings.style) {
21020				elm = self.getEl();
21021				if (elm) {
21022					elm.setAttribute('style', settings.style);
21023					elm.style.cssText = settings.style;
21024				}
21025			}
21026
21027			if (!self._visible) {
21028				DomUtils.css(self.getEl(), 'display', 'none');
21029			}
21030
21031			if (self.settings.border) {
21032				box = self.borderBox();
21033				DomUtils.css(self.getEl(), {
21034					'border-top-width': box.top,
21035					'border-right-width': box.right,
21036					'border-bottom-width': box.bottom,
21037					'border-left-width': box.left
21038				});
21039			}
21040
21041			// Add instance to lookup
21042			Control.controlIdLookup[self._id] = self;
21043
21044			for (var key in self._aria) {
21045				self.aria(key, self._aria[key]);
21046			}
21047
21048			self.fire('postrender', {}, false);
21049		},
21050
21051		/**
21052		 * Scrolls the current control into view.
21053		 *
21054		 * @method scrollIntoView
21055		 * @param {String} align Alignment in view top|center|bottom.
21056		 * @return {tinymce.ui.Control} Current control instance.
21057		 */
21058		scrollIntoView: function(align) {
21059			function getOffset(elm, rootElm) {
21060				var x, y, parent = elm;
21061
21062				x = y = 0;
21063				while (parent && parent != rootElm && parent.nodeType) {
21064					x += parent.offsetLeft || 0;
21065					y += parent.offsetTop || 0;
21066					parent = parent.offsetParent;
21067				}
21068
21069				return {x: x, y: y};
21070			}
21071
21072			var elm = this.getEl(), parentElm = elm.parentNode;
21073			var x, y, width, height, parentWidth, parentHeight;
21074			var pos = getOffset(elm, parentElm);
21075
21076			x = pos.x;
21077			y = pos.y;
21078			width = elm.offsetWidth;
21079			height = elm.offsetHeight;
21080			parentWidth = parentElm.clientWidth;
21081			parentHeight = parentElm.clientHeight;
21082
21083			if (align == "end") {
21084				x -= parentWidth - width;
21085				y -= parentHeight - height;
21086			} else if (align == "center") {
21087				x -= (parentWidth / 2) - (width / 2);
21088				y -= (parentHeight / 2) - (height / 2);
21089			}
21090
21091			parentElm.scrollLeft = x;
21092			parentElm.scrollTop = y;
21093
21094			return this;
21095		},
21096
21097		/**
21098		 * Binds pending DOM events.
21099		 *
21100		 * @private
21101		 */
21102		bindPendingEvents: function() {
21103			var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
21104
21105			function delegate(e) {
21106				var control = self.getParentCtrl(e.target);
21107
21108				if (control) {
21109					control.fire(e.type, e);
21110				}
21111			}
21112
21113			function mouseLeaveHandler() {
21114				var ctrl = eventRootCtrl._lastHoverCtrl;
21115
21116				if (ctrl) {
21117					ctrl.fire("mouseleave", {target: ctrl.getEl()});
21118
21119					ctrl.parents().each(function(ctrl) {
21120						ctrl.fire("mouseleave", {target: ctrl.getEl()});
21121					});
21122
21123					eventRootCtrl._lastHoverCtrl = null;
21124				}
21125			}
21126
21127			function mouseEnterHandler(e) {
21128				var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
21129
21130				// Over on a new control
21131				if (ctrl !== lastCtrl) {
21132					eventRootCtrl._lastHoverCtrl = ctrl;
21133
21134					parents = ctrl.parents().toArray().reverse();
21135					parents.push(ctrl);
21136
21137					if (lastCtrl) {
21138						lastParents = lastCtrl.parents().toArray().reverse();
21139						lastParents.push(lastCtrl);
21140
21141						for (idx = 0; idx < lastParents.length; idx++) {
21142							if (parents[idx] !== lastParents[idx]) {
21143								break;
21144							}
21145						}
21146
21147						for (i = lastParents.length - 1; i >= idx; i--) {
21148							lastCtrl = lastParents[i];
21149							lastCtrl.fire("mouseleave", {
21150								target : lastCtrl.getEl()
21151							});
21152						}
21153					}
21154
21155					for (i = idx; i < parents.length; i++) {
21156						ctrl = parents[i];
21157						ctrl.fire("mouseenter", {
21158							target : ctrl.getEl()
21159						});
21160					}
21161				}
21162			}
21163
21164			function fixWheelEvent(e) {
21165				e.preventDefault();
21166
21167				if (e.type == "mousewheel") {
21168					e.deltaY = - 1/40 * e.wheelDelta;
21169
21170					if (e.wheelDeltaX) {
21171						e.deltaX = -1/40 * e.wheelDeltaX;
21172					}
21173				} else {
21174					e.deltaX = 0;
21175					e.deltaY = e.detail;
21176				}
21177
21178				e = self.fire("wheel", e);
21179			}
21180
21181			self._rendered = true;
21182
21183			nativeEvents = self._nativeEvents;
21184			if (nativeEvents) {
21185				// Find event root element if it exists
21186				parents = self.parents().toArray();
21187				parents.unshift(self);
21188				for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
21189					eventRootCtrl = parents[i]._eventsRoot;
21190				}
21191
21192				// Event root wasn't found the use the root control
21193				if (!eventRootCtrl) {
21194					eventRootCtrl = parents[parents.length - 1] || self;
21195				}
21196
21197				// Set the eventsRoot property on children that didn't have it
21198				self._eventsRoot = eventRootCtrl;
21199				for (l = i, i = 0; i < l; i++) {
21200					parents[i]._eventsRoot = eventRootCtrl;
21201				}
21202
21203				// Bind native event delegates
21204				for (name in nativeEvents) {
21205					if (!nativeEvents) {
21206						return false;
21207					}
21208
21209					if (name === "wheel" && !hasWheelEventSupport) {
21210						if (hasMouseWheelEventSupport) {
21211							DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
21212						} else {
21213							DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
21214						}
21215
21216						continue;
21217					}
21218
21219					// Special treatment for mousenter/mouseleave since these doesn't bubble
21220					if (name === "mouseenter" || name === "mouseleave") {
21221						// Fake mousenter/mouseleave
21222						if (!eventRootCtrl._hasMouseEnter) {
21223							DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
21224							DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
21225							eventRootCtrl._hasMouseEnter = 1;
21226						}
21227					} else if (!eventRootCtrl[name]) {
21228						DomUtils.on(eventRootCtrl.getEl(), name, delegate);
21229						eventRootCtrl[name] = true;
21230					}
21231
21232					// Remove the event once it's bound
21233					nativeEvents[name] = false;
21234				}
21235			}
21236		},
21237
21238		/**
21239		 * Reflows the current control and it's parents.
21240		 * This should be used after you for example append children to the current control so
21241		 * that the layout managers know that they need to reposition everything.
21242		 *
21243		 * @example
21244		 * container.append({type: 'button', text: 'My button'}).reflow();
21245		 *
21246		 * @method reflow
21247		 * @return {tinymce.ui.Control} Current control instance.
21248		 */
21249		reflow: function() {
21250			this.repaint();
21251
21252			return this;
21253		}
21254
21255		/**
21256		 * Sets/gets the parent container for the control.
21257		 *
21258		 * @method parent
21259		 * @param {tinymce.ui.Container} parent Optional parent to set.
21260		 * @return {tinymce.ui.Control} Parent control or the current control on a set action.
21261		 */
21262		// parent: function(parent) {} -- Generated
21263
21264		/**
21265		 * Sets/gets the text for the control.
21266		 *
21267		 * @method text
21268		 * @param {String} value Value to set to control.
21269		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
21270		 */
21271		// text: function(value) {} -- Generated
21272
21273		/**
21274		 * Sets/gets the width for the control.
21275		 *
21276		 * @method width
21277		 * @param {Number} value Value to set to control.
21278		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
21279		 */
21280		// width: function(value) {} -- Generated
21281
21282		/**
21283		 * Sets/gets the height for the control.
21284		 *
21285		 * @method height
21286		 * @param {Number} value Value to set to control.
21287		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
21288		 */
21289		// height: function(value) {} -- Generated
21290
21291		/**
21292		 * Sets/gets the disabled state on the control.
21293		 *
21294		 * @method disabled
21295		 * @param {Boolean} state Value to set to control.
21296		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
21297		 */
21298		// disabled: function(state) {} -- Generated
21299
21300		/**
21301		 * Sets/gets the active for the control.
21302		 *
21303		 * @method active
21304		 * @param {Boolean} state Value to set to control.
21305		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
21306		 */
21307		// active: function(state) {} -- Generated
21308
21309		/**
21310		 * Sets/gets the name for the control.
21311		 *
21312		 * @method name
21313		 * @param {String} value Value to set to control.
21314		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
21315		 */
21316		// name: function(value) {} -- Generated
21317
21318		/**
21319		 * Sets/gets the title for the control.
21320		 *
21321		 * @method title
21322		 * @param {String} value Value to set to control.
21323		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
21324		 */
21325		// title: function(value) {} -- Generated
21326	});
21327
21328	return Control;
21329});
21330
21331// Included from: js/tinymce/classes/ui/Factory.js
21332
21333/**
21334 * Factory.js
21335 *
21336 * Copyright, Moxiecode Systems AB
21337 * Released under LGPL License.
21338 *
21339 * License: http://www.tinymce.com/license
21340 * Contributing: http://www.tinymce.com/contributing
21341 */
21342
21343/*global tinymce:true */
21344
21345/**
21346 * This class is a factory for control instances. This enables you
21347 * to create instances of controls without having to require the UI controls directly.
21348 *
21349 * It also allow you to override or add new control types.
21350 *
21351 * @class tinymce.ui.Factory
21352 */
21353define("tinymce/ui/Factory", [], function() {
21354	"use strict";
21355
21356	var types = {}, namespaceInit;
21357
21358	return {
21359		/**
21360		 * Adds a new control instance type to the factory.
21361		 *
21362		 * @method add
21363		 * @param {String} type Type name for example "button".
21364		 * @param {function} typeClass Class type function.
21365		 */
21366		add: function(type, typeClass) {
21367			types[type.toLowerCase()] = typeClass;
21368		},
21369
21370		/**
21371		 * Returns true/false if the specified type exists or not.
21372		 *
21373		 * @method has
21374		 * @param {String} type Type to look for.
21375		 * @return {Boolean} true/false if the control by name exists.
21376		 */
21377		has: function(type) {
21378			return !!types[type.toLowerCase()];
21379		},
21380
21381		/**
21382		 * Creates a new control instance based on the settings provided. The instance created will be
21383		 * based on the specified type property it can also create whole structures of components out of
21384		 * the specified JSON object.
21385		 *
21386		 * @example
21387		 * tinymce.ui.Factory.create({
21388		 *     type: 'button',
21389		 *     text: 'Hello world!'
21390		 * });
21391		 *
21392		 * @method create
21393		 * @param {Object/String} settings Name/Value object with items used to create the type.
21394		 * @return {tinymce.ui.Control} Control instance based on the specified type.
21395		 */
21396		create: function(type, settings) {
21397			var ControlType, name, namespace;
21398
21399			// Build type lookup
21400			if (!namespaceInit) {
21401				namespace = tinymce.ui;
21402
21403				for (name in namespace) {
21404					types[name.toLowerCase()] = namespace[name];
21405				}
21406
21407				namespaceInit = true;
21408			}
21409
21410			// If string is specified then use it as the type
21411			if (typeof(type) == 'string') {
21412				settings = settings || {};
21413				settings.type = type;
21414			} else {
21415				settings = type;
21416				type = settings.type;
21417			}
21418
21419			// Find control type
21420			type = type.toLowerCase();
21421			ControlType = types[type];
21422
21423			// #if debug
21424
21425			if (!ControlType) {
21426				throw new Error("Could not find control by type: " + type);
21427			}
21428
21429			// #endif
21430
21431			ControlType = new ControlType(settings);
21432			ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine
21433
21434			return ControlType;
21435		}
21436	};
21437});
21438
21439// Included from: js/tinymce/classes/ui/Container.js
21440
21441/**
21442 * Container.js
21443 *
21444 * Copyright, Moxiecode Systems AB
21445 * Released under LGPL License.
21446 *
21447 * License: http://www.tinymce.com/license
21448 * Contributing: http://www.tinymce.com/contributing
21449 */
21450
21451/**
21452 * Container control. This is extended by all controls that can have
21453 * children such as panels etc. You can also use this class directly as an
21454 * generic container instance. The container doesn't have any specific role or style.
21455 *
21456 * @-x-less Container.less
21457 * @class tinymce.ui.Container
21458 * @extends tinymce.ui.Control
21459 */
21460define("tinymce/ui/Container", [
21461	"tinymce/ui/Control",
21462	"tinymce/ui/Collection",
21463	"tinymce/ui/Selector",
21464	"tinymce/ui/Factory",
21465	"tinymce/util/Tools",
21466	"tinymce/ui/DomUtils"
21467], function(Control, Collection, Selector, Factory, Tools, DomUtils) {
21468	"use strict";
21469
21470	var selectorCache = {};
21471
21472	return Control.extend({
21473		layout: '',
21474		innerClass: 'container-inner',
21475
21476		/**
21477		 * Constructs a new control instance with the specified settings.
21478		 *
21479		 * @constructor
21480		 * @param {Object} settings Name/value object with settings.
21481		 * @setting {Array} items Items to add to container in JSON format or control instances.
21482		 * @setting {String} layout Layout manager by name to use.
21483		 * @setting {Object} defaults Default settings to apply to all items.
21484		 */
21485		init: function(settings) {
21486			var self = this;
21487
21488			self._super(settings);
21489			settings = self.settings;
21490			self._fixed = settings.fixed;
21491			self._items = new Collection();
21492
21493			if (self.isRtl()) {
21494				self.addClass('rtl');
21495			}
21496
21497			self.addClass('container');
21498			self.addClass('container-body', 'body');
21499
21500			if (settings.containerCls) {
21501				self.addClass(settings.containerCls);
21502			}
21503
21504			self._layout = Factory.create((settings.layout || self.layout) + 'layout');
21505
21506			if (self.settings.items) {
21507				self.add(self.settings.items);
21508			}
21509
21510			// TODO: Fix this!
21511			self._hasBody = true;
21512		},
21513
21514		/**
21515		 * Returns a collection of child items that the container currently have.
21516		 *
21517		 * @method items
21518		 * @return {tinymce.ui.Collection} Control collection direct child controls.
21519		 */
21520		items: function() {
21521			return this._items;
21522		},
21523
21524		/**
21525		 * Find child controls by selector.
21526		 *
21527		 * @method find
21528		 * @param {String} selector Selector CSS pattern to find children by.
21529		 * @return {tinymce.ui.Collection} Control collection with child controls.
21530		 */
21531		find: function(selector) {
21532			selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector);
21533
21534			return selector.find(this);
21535		},
21536
21537		/**
21538		 * Adds one or many items to the current container. This will create instances of
21539		 * the object representations if needed.
21540		 *
21541		 * @method add
21542		 * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container.
21543		 * @return {tinymce.ui.Collection} Current collection control.
21544		 */
21545		add: function(items) {
21546			var self = this;
21547
21548			self.items().add(self.create(items)).parent(self);
21549
21550			return self;
21551		},
21552
21553		/**
21554		 * Focuses the current container instance. This will look
21555		 * for the first control in the container and focus that.
21556		 *
21557		 * @method focus
21558		 * @return {tinymce.ui.Collection} Current instance.
21559		 */
21560		focus: function() {
21561			var self = this;
21562
21563			if (self.keyNav) {
21564				self.keyNav.focusFirst();
21565			} else {
21566				self._super();
21567			}
21568
21569			return self;
21570		},
21571
21572		/**
21573		 * Replaces the specified child control with a new control.
21574		 *
21575		 * @method replace
21576		 * @param {tinymce.ui.Control} oldItem Old item to be replaced.
21577		 * @param {tinymce.ui.Control} newItem New item to be inserted.
21578		 */
21579		replace: function(oldItem, newItem) {
21580			var ctrlElm, items = this.items(), i = items.length;
21581
21582			// Replace the item in collection
21583			while (i--) {
21584				if (items[i] === oldItem) {
21585					items[i] = newItem;
21586					break;
21587				}
21588			}
21589
21590			if (i >= 0) {
21591				// Remove new item from DOM
21592				ctrlElm = newItem.getEl();
21593				if (ctrlElm) {
21594					ctrlElm.parentNode.removeChild(ctrlElm);
21595				}
21596
21597				// Remove old item from DOM
21598				ctrlElm = oldItem.getEl();
21599				if (ctrlElm) {
21600					ctrlElm.parentNode.removeChild(ctrlElm);
21601				}
21602			}
21603
21604			// Adopt the item
21605			newItem.parent(this);
21606		},
21607
21608		/**
21609		 * Creates the specified items. If any of the items is plain JSON style objects
21610		 * it will convert these into real tinymce.ui.Control instances.
21611		 *
21612		 * @method create
21613		 * @param {Array} items Array of items to convert into control instances.
21614		 * @return {Array} Array with control instances.
21615		 */
21616		create: function(items) {
21617			var self = this, settings, ctrlItems = [];
21618
21619			// Non array structure, then force it into an array
21620			if (!Tools.isArray(items)) {
21621				items = [items];
21622			}
21623
21624			// Add default type to each child control
21625			Tools.each(items, function(item) {
21626				if (item) {
21627					// Construct item if needed
21628					if (!(item instanceof Control)) {
21629						// Name only then convert it to an object
21630						if (typeof(item) == "string") {
21631							item = {type: item};
21632						}
21633
21634						// Create control instance based on input settings and default settings
21635						settings = Tools.extend({}, self.settings.defaults, item);
21636						item.type = settings.type = settings.type || item.type || self.settings.defaultType ||
21637							(settings.defaults ? settings.defaults.type : null);
21638						item = Factory.create(settings);
21639					}
21640
21641					ctrlItems.push(item);
21642				}
21643			});
21644
21645			return ctrlItems;
21646		},
21647
21648		/**
21649		 * Renders new control instances.
21650		 *
21651		 * @private
21652		 */
21653		renderNew: function() {
21654			var self = this;
21655
21656			// Render any new items
21657			self.items().each(function(ctrl, index) {
21658				var containerElm, fragment;
21659
21660				ctrl.parent(self);
21661
21662				if (!ctrl._rendered) {
21663					containerElm = self.getEl('body');
21664					fragment = DomUtils.createFragment(ctrl.renderHtml());
21665
21666					// Insert or append the item
21667					if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) {
21668						containerElm.insertBefore(fragment, containerElm.childNodes[index]);
21669					} else {
21670						containerElm.appendChild(fragment);
21671					}
21672
21673					ctrl.postRender();
21674				}
21675			});
21676
21677			self._layout.applyClasses(self);
21678			self._lastRect = null;
21679
21680			return self;
21681		},
21682
21683		/**
21684		 * Appends new instances to the current container.
21685		 *
21686		 * @method append
21687		 * @param {Array/tinymce.ui.Collection} items Array if controls to append.
21688		 * @return {tinymce.ui.Container} Current container instance.
21689		 */
21690		append: function(items) {
21691			return this.add(items).renderNew();
21692		},
21693
21694		/**
21695		 * Prepends new instances to the current container.
21696		 *
21697		 * @method prepend
21698		 * @param {Array/tinymce.ui.Collection} items Array if controls to prepend.
21699		 * @return {tinymce.ui.Container} Current container instance.
21700		 */
21701		prepend: function(items) {
21702			var self = this;
21703
21704			self.items().set(self.create(items).concat(self.items().toArray()));
21705
21706			return self.renderNew();
21707		},
21708
21709		/**
21710		 * Inserts an control at a specific index.
21711		 *
21712		 * @method insert
21713		 * @param {Array/tinymce.ui.Collection} items Array if controls to insert.
21714		 * @param {Number} index Index to insert controls at.
21715		 * @param {Boolean} [before=false] Inserts controls before the index.
21716		 */
21717		insert: function(items, index, before) {
21718			var self = this, curItems, beforeItems, afterItems;
21719
21720			items = self.create(items);
21721			curItems = self.items();
21722
21723			if (!before && index < curItems.length - 1) {
21724				index += 1;
21725			}
21726
21727			if (index >= 0 && index < curItems.length) {
21728				beforeItems = curItems.slice(0, index).toArray();
21729				afterItems = curItems.slice(index).toArray();
21730				curItems.set(beforeItems.concat(items, afterItems));
21731			}
21732
21733			return self.renderNew();
21734		},
21735
21736		/**
21737		 * Populates the form fields from the specified JSON data object.
21738		 *
21739		 * Control items in the form that matches the data will have it's value set.
21740		 *
21741		 * @method fromJSON
21742		 * @param {Object} data JSON data object to set control values by.
21743		 * @return {tinymce.ui.Container} Current form instance.
21744		 */
21745		fromJSON: function(data) {
21746			var self = this;
21747
21748			for (var name in data) {
21749				self.find('#' + name).value(data[name]);
21750			}
21751
21752			return self;
21753		},
21754
21755		/**
21756		 * Serializes the form into a JSON object by getting all items
21757		 * that has a name and a value.
21758		 *
21759		 * @method toJSON
21760		 * @return {Object} JSON object with form data.
21761		 */
21762		toJSON: function() {
21763			var self = this, data = {};
21764
21765			self.find('*').each(function(ctrl) {
21766				var name = ctrl.name(), value = ctrl.value();
21767
21768				if (name && typeof(value) != "undefined") {
21769					data[name] = value;
21770				}
21771			});
21772
21773			return data;
21774		},
21775
21776		preRender: function() {
21777		},
21778
21779		/**
21780		 * Renders the control as a HTML string.
21781		 *
21782		 * @method renderHtml
21783		 * @return {String} HTML representing the control.
21784		 */
21785		renderHtml: function() {
21786			var self = this, layout = self._layout;
21787
21788			self.preRender();
21789			layout.preRender(self);
21790
21791			return (
21792				'<div id="' + self._id + '" class="' + self.classes() + '" role="' + this.settings.role + '">' +
21793					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">'+
21794						(self.settings.html || '') + layout.renderHtml(self) +
21795					'</div>' +
21796				'</div>'
21797			);
21798		},
21799
21800		/**
21801		 * Post render method. Called after the control has been rendered to the target.
21802		 *
21803		 * @method postRender
21804		 * @return {tinymce.ui.Container} Current combobox instance.
21805		 */
21806		postRender: function() {
21807			var self = this, box;
21808
21809			self.items().exec('postRender');
21810			self._super();
21811
21812			self._layout.postRender(self);
21813			self._rendered = true;
21814
21815			if (self.settings.style) {
21816				DomUtils.css(self.getEl(), self.settings.style);
21817			}
21818
21819			if (self.settings.border) {
21820				box = self.borderBox();
21821				DomUtils.css(self.getEl(), {
21822					'border-top-width': box.top,
21823					'border-right-width': box.right,
21824					'border-bottom-width': box.bottom,
21825					'border-left-width': box.left
21826				});
21827			}
21828
21829			return self;
21830		},
21831
21832		/**
21833		 * Initializes the current controls layout rect.
21834		 * This will be executed by the layout managers to determine the
21835		 * default minWidth/minHeight etc.
21836		 *
21837		 * @method initLayoutRect
21838		 * @return {Object} Layout rect instance.
21839		 */
21840		initLayoutRect: function() {
21841			var self = this, layoutRect = self._super();
21842
21843			// Recalc container size by asking layout manager
21844			self._layout.recalc(self);
21845
21846			return layoutRect;
21847		},
21848
21849		/**
21850		 * Recalculates the positions of the controls in the current container.
21851		 * This is invoked by the reflow method and shouldn't be called directly.
21852		 *
21853		 * @method recalc
21854		 */
21855		recalc: function() {
21856			var self = this, rect = self._layoutRect, lastRect = self._lastRect;
21857
21858			if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
21859				self._layout.recalc(self);
21860				rect = self.layoutRect();
21861				self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h};
21862				return true;
21863			}
21864		},
21865
21866		/**
21867		 * Reflows the current container and it's children and possible parents.
21868		 * This should be used after you for example append children to the current control so
21869		 * that the layout managers know that they need to reposition everything.
21870		 *
21871		 * @example
21872		 * container.append({type: 'button', text: 'My button'}).reflow();
21873		 *
21874		 * @method reflow
21875		 * @return {tinymce.ui.Container} Current container instance.
21876		 */
21877		reflow: function() {
21878			var i, items;
21879
21880			if (this.visible()) {
21881				Control.repaintControls = [];
21882				Control.repaintControls.map = {};
21883
21884				items = this.recalc();
21885				i = Control.repaintControls.length;
21886
21887				while (i--) {
21888					Control.repaintControls[i].repaint();
21889				}
21890
21891				// TODO: Fix me!
21892				if (this.settings.layout !== "flow" && this.settings.layout !== "stack") {
21893					this.repaint();
21894				}
21895
21896				Control.repaintControls = [];
21897			}
21898
21899			return this;
21900		}
21901	});
21902});
21903
21904// Included from: js/tinymce/classes/ui/DragHelper.js
21905
21906/**
21907 * DragHelper.js
21908 *
21909 * Copyright, Moxiecode Systems AB
21910 * Released under LGPL License.
21911 *
21912 * License: http://www.tinymce.com/license
21913 * Contributing: http://www.tinymce.com/contributing
21914 */
21915
21916/**
21917 * Drag/drop helper class.
21918 *
21919 * @example
21920 * var dragHelper = new tinymce.ui.DragHelper('mydiv', {
21921 *     start: function(evt) {
21922 *     },
21923 *
21924 *     drag: function(evt) {
21925 *     },
21926 *
21927 *     end: function(evt) {
21928 *     }
21929 * });
21930 *
21931 * @class tinymce.ui.DragHelper
21932 */
21933define("tinymce/ui/DragHelper", [
21934	"tinymce/ui/DomUtils"
21935], function(DomUtils) {
21936	"use strict";
21937
21938	function getDocumentSize() {
21939		var doc = document, documentElement, body, scrollWidth, clientWidth;
21940		var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max;
21941
21942		documentElement = doc.documentElement;
21943		body = doc.body;
21944
21945		scrollWidth = max(documentElement.scrollWidth, body.scrollWidth);
21946		clientWidth = max(documentElement.clientWidth, body.clientWidth);
21947		offsetWidth = max(documentElement.offsetWidth, body.offsetWidth);
21948
21949		scrollHeight = max(documentElement.scrollHeight, body.scrollHeight);
21950		clientHeight = max(documentElement.clientHeight, body.clientHeight);
21951		offsetHeight = max(documentElement.offsetHeight, body.offsetHeight);
21952
21953		return {
21954			width: scrollWidth < offsetWidth ? clientWidth : scrollWidth,
21955			height: scrollHeight < offsetHeight ? clientHeight : scrollHeight
21956		};
21957	}
21958
21959	return function(id, settings) {
21960		var eventOverlayElm, doc = document, downButton, start, stop, drag, startX, startY;
21961
21962		settings = settings || {};
21963
21964		function getHandleElm() {
21965			return doc.getElementById(settings.handle || id);
21966		}
21967
21968		start = function(e) {
21969			var docSize = getDocumentSize(), handleElm, cursor;
21970
21971			e.preventDefault();
21972			downButton = e.button;
21973			handleElm = getHandleElm();
21974			startX = e.screenX;
21975			startY = e.screenY;
21976
21977			// Grab cursor from handle
21978			if (window.getComputedStyle) {
21979				cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor");
21980			} else {
21981				cursor = handleElm.runtimeStyle.cursor;
21982			}
21983
21984			// Create event overlay and add it to document
21985			eventOverlayElm = doc.createElement('div');
21986			DomUtils.css(eventOverlayElm, {
21987				position: "absolute",
21988				top: 0, left: 0,
21989				width: docSize.width,
21990				height: docSize.height,
21991				zIndex: 0x7FFFFFFF,
21992				opacity: 0.0001,
21993				background: 'red',
21994				cursor: cursor
21995			});
21996
21997			doc.body.appendChild(eventOverlayElm);
21998
21999			// Bind mouse events
22000			DomUtils.on(doc, 'mousemove', drag);
22001			DomUtils.on(doc, 'mouseup', stop);
22002
22003			// Begin drag
22004			settings.start(e);
22005		};
22006
22007		drag = function(e) {
22008			if (e.button !== downButton) {
22009				return stop(e);
22010			}
22011
22012			e.deltaX = e.screenX - startX;
22013			e.deltaY = e.screenY - startY;
22014
22015			e.preventDefault();
22016			settings.drag(e);
22017		};
22018
22019		stop = function(e) {
22020			DomUtils.off(doc, 'mousemove', drag);
22021			DomUtils.off(doc, 'mouseup', stop);
22022
22023			eventOverlayElm.parentNode.removeChild(eventOverlayElm);
22024
22025			if (settings.stop) {
22026				settings.stop(e);
22027			}
22028		};
22029
22030		/**
22031		 * Destroys the drag/drop helper instance.
22032		 *
22033		 * @method destroy
22034		 */
22035		this.destroy = function() {
22036			DomUtils.off(getHandleElm());
22037		};
22038
22039		DomUtils.on(getHandleElm(), 'mousedown', start);
22040	};
22041});
22042
22043// Included from: js/tinymce/classes/ui/Scrollable.js
22044
22045/**
22046 * Scrollable.js
22047 *
22048 * Copyright, Moxiecode Systems AB
22049 * Released under LGPL License.
22050 *
22051 * License: http://www.tinymce.com/license
22052 * Contributing: http://www.tinymce.com/contributing
22053 */
22054
22055/**
22056 * This mixin makes controls scrollable using custom scrollbars.
22057 *
22058 * @-x-less Scrollable.less
22059 * @mixin tinymce.ui.Scrollable
22060 */
22061define("tinymce/ui/Scrollable", [
22062	"tinymce/ui/DomUtils",
22063	"tinymce/ui/DragHelper"
22064], function(DomUtils, DragHelper) {
22065	"use strict";
22066
22067	return {
22068		init: function() {
22069			var self = this;
22070			self.on('repaint', self.renderScroll);
22071		},
22072
22073		renderScroll: function() {
22074			var self = this, margin = 2;
22075
22076			function repaintScroll() {
22077				var hasScrollH, hasScrollV, bodyElm;
22078
22079				function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
22080					var containerElm, scrollBarElm, scrollThumbElm;
22081					var containerSize, scrollSize, ratio, rect;
22082					var posNameLower, sizeNameLower;
22083
22084					scrollBarElm = self.getEl('scroll' + axisName);
22085					if (scrollBarElm) {
22086						posNameLower = posName.toLowerCase();
22087						sizeNameLower = sizeName.toLowerCase();
22088
22089						if (self.getEl('absend')) {
22090							DomUtils.css(self.getEl('absend'), posNameLower, self.layoutRect()[contentSizeName] - 1);
22091						}
22092
22093						if (!hasScroll) {
22094							DomUtils.css(scrollBarElm, 'display', 'none');
22095							return;
22096						}
22097
22098						DomUtils.css(scrollBarElm, 'display', 'block');
22099						containerElm = self.getEl('body');
22100						scrollThumbElm = self.getEl('scroll' + axisName + "t");
22101						containerSize = containerElm["client" + sizeName] - (margin * 2);
22102						containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
22103						scrollSize = containerElm["scroll" + sizeName];
22104						ratio = containerSize / scrollSize;
22105
22106						rect = {};
22107						rect[posNameLower] = containerElm["offset" + posName] + margin;
22108						rect[sizeNameLower] = containerSize;
22109						DomUtils.css(scrollBarElm, rect);
22110
22111						rect = {};
22112						rect[posNameLower] = containerElm["scroll" + posName] * ratio;
22113						rect[sizeNameLower] = containerSize * ratio;
22114						DomUtils.css(scrollThumbElm, rect);
22115					}
22116				}
22117
22118				bodyElm = self.getEl('body');
22119				hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
22120				hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
22121
22122				repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
22123				repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
22124			}
22125
22126			function addScroll() {
22127				function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
22128					var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
22129
22130					self.getEl().appendChild(DomUtils.createFragment(
22131						'<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
22132							'<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
22133						'</div>'
22134					));
22135
22136					self.draghelper = new DragHelper(axisId + 't', {
22137						start: function() {
22138							scrollStart = self.getEl('body')["scroll" + posName];
22139							DomUtils.addClass(DomUtils.get(axisId), prefix + 'active');
22140						},
22141
22142						drag: function(e) {
22143							var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
22144
22145							hasScrollH = layoutRect.contentW > layoutRect.innerW;
22146							hasScrollV = layoutRect.contentH > layoutRect.innerH;
22147							containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
22148							containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
22149
22150							ratio = containerSize / self.getEl('body')["scroll" + sizeName];
22151							self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
22152						},
22153
22154						stop: function() {
22155							DomUtils.removeClass(DomUtils.get(axisId), prefix + 'active');
22156						}
22157					});
22158/*
22159					self.on('click', function(e) {
22160						if (e.target.id == self._id + '-scrollv') {
22161
22162						}
22163					});*/
22164				}
22165
22166				self.addClass('scroll');
22167
22168				addScrollAxis("v", "Top", "Height", "Y", "Width");
22169				addScrollAxis("h", "Left", "Width", "X", "Height");
22170			}
22171
22172			if (self.settings.autoScroll) {
22173				if (!self._hasScroll) {
22174					self._hasScroll = true;
22175					addScroll();
22176
22177					self.on('wheel', function(e) {
22178						var bodyEl = self.getEl('body');
22179
22180						bodyEl.scrollLeft += (e.deltaX || 0) * 10;
22181						bodyEl.scrollTop += e.deltaY * 10;
22182
22183						repaintScroll();
22184					});
22185
22186					DomUtils.on(self.getEl('body'), "scroll", repaintScroll);
22187				}
22188
22189				repaintScroll();
22190			}
22191		}
22192	};
22193});
22194
22195// Included from: js/tinymce/classes/ui/Panel.js
22196
22197/**
22198 * Panel.js
22199 *
22200 * Copyright, Moxiecode Systems AB
22201 * Released under LGPL License.
22202 *
22203 * License: http://www.tinymce.com/license
22204 * Contributing: http://www.tinymce.com/contributing
22205 */
22206
22207/**
22208 * Creates a new panel.
22209 *
22210 * @-x-less Panel.less
22211 * @class tinymce.ui.Panel
22212 * @extends tinymce.ui.Container
22213 * @mixes tinymce.ui.Scrollable
22214 */
22215define("tinymce/ui/Panel", [
22216	"tinymce/ui/Container",
22217	"tinymce/ui/Scrollable"
22218], function(Container, Scrollable) {
22219	"use strict";
22220
22221	return Container.extend({
22222		Defaults: {
22223			layout: 'fit',
22224			containerCls: 'panel'
22225		},
22226
22227		Mixins: [Scrollable],
22228
22229		/**
22230		 * Renders the control as a HTML string.
22231		 *
22232		 * @method renderHtml
22233		 * @return {String} HTML representing the control.
22234		 */
22235		renderHtml: function() {
22236			var self = this, layout = self._layout, innerHtml = self.settings.html;
22237
22238			self.preRender();
22239			layout.preRender(self);
22240
22241			if (typeof(innerHtml) == "undefined") {
22242				innerHtml = (
22243					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
22244						layout.renderHtml(self) +
22245					'</div>'
22246				);
22247			} else {
22248				if (typeof(innerHtml) == 'function') {
22249					innerHtml = innerHtml.call(self);
22250				}
22251
22252				self._hasBody = false;
22253			}
22254
22255			return (
22256				'<div id="' + self._id + '" class="' + self.classes() + '" hideFocus="1" tabIndex="-1">' +
22257					(self._preBodyHtml || '') +
22258					innerHtml +
22259				'</div>'
22260			);
22261		}
22262	});
22263});
22264
22265// Included from: js/tinymce/classes/ui/Movable.js
22266
22267/**
22268 * Movable.js
22269 *
22270 * Copyright, Moxiecode Systems AB
22271 * Released under LGPL License.
22272 *
22273 * License: http://www.tinymce.com/license
22274 * Contributing: http://www.tinymce.com/contributing
22275 */
22276
22277/**
22278 * Movable mixin. Makes controls movable absolute and relative to other elements.
22279 *
22280 * @mixin tinymce.ui.Movable
22281 */
22282define("tinymce/ui/Movable", [
22283	"tinymce/ui/DomUtils"
22284], function(DomUtils) {
22285	"use strict";
22286
22287	function calculateRelativePosition(ctrl, targetElm, rel) {
22288		var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
22289
22290		viewport = DomUtils.getViewPort();
22291
22292		// Get pos of target
22293		pos = DomUtils.getPos(targetElm);
22294		x = pos.x;
22295		y = pos.y;
22296
22297		if (ctrl._fixed) {
22298			x -= viewport.x;
22299			y -= viewport.y;
22300		}
22301
22302		// Get size of self
22303		ctrlElm = ctrl.getEl();
22304		size = DomUtils.getSize(ctrlElm);
22305		selfW = size.width;
22306		selfH = size.height;
22307
22308		// Get size of target
22309		size = DomUtils.getSize(targetElm);
22310		targetW = size.width;
22311		targetH = size.height;
22312
22313		// Parse align string
22314		rel = (rel || '').split('');
22315
22316		// Target corners
22317		if (rel[0] === 'b') {
22318			y += targetH;
22319		}
22320
22321		if (rel[1] === 'r') {
22322			x += targetW;
22323		}
22324
22325		if (rel[0] === 'c') {
22326			y += Math.round(targetH / 2);
22327		}
22328
22329		if (rel[1] === 'c') {
22330			x += Math.round(targetW / 2);
22331		}
22332
22333		// Self corners
22334		if (rel[3] === 'b') {
22335			y -= selfH;
22336		}
22337
22338		if (rel[4] === 'r') {
22339			x -= selfW;
22340		}
22341
22342		if (rel[3] === 'c') {
22343			y -= Math.round(selfH / 2);
22344		}
22345
22346		if (rel[4] === 'c') {
22347			x -= Math.round(selfW / 2);
22348		}
22349
22350		return {
22351			x: x,
22352			y: y,
22353			w: selfW,
22354			h: selfH
22355		};
22356	}
22357
22358	return {
22359		/**
22360		 * Tests various positions to get the most suitable one.
22361		 *
22362		 * @method testMoveRel
22363		 * @param {DOMElement} elm Element to position against.
22364		 * @param {Array} rels Array with relative positions.
22365		 * @return {String} Best suitable relative position.
22366		 */
22367		testMoveRel: function(elm, rels) {
22368			var viewPortRect = DomUtils.getViewPort();
22369
22370			for (var i = 0; i < rels.length; i++) {
22371				var pos = calculateRelativePosition(this, elm, rels[i]);
22372
22373				if (this._fixed) {
22374					if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
22375						return rels[i];
22376					}
22377				} else {
22378					if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
22379						pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
22380						return rels[i];
22381					}
22382				}
22383			}
22384
22385			return rels[0];
22386		},
22387
22388		/**
22389		 * Move relative to the specified element.
22390		 *
22391		 * @method moveRel
22392		 * @param {Element} elm Element to move relative to.
22393		 * @param {String} rel Relative mode. For example: br-tl.
22394		 * @return {tinymce.ui.Control} Current control instance.
22395		 */
22396		moveRel: function(elm, rel) {
22397			if (typeof(rel) != 'string') {
22398				rel = this.testMoveRel(elm, rel);
22399			}
22400
22401			var pos = calculateRelativePosition(this, elm, rel);
22402			return this.moveTo(pos.x, pos.y);
22403		},
22404
22405		/**
22406		 * Move by a relative x, y values.
22407		 *
22408		 * @method moveBy
22409		 * @param {Number} dx Relative x position.
22410		 * @param {Number} dy Relative y position.
22411		 * @return {tinymce.ui.Control} Current control instance.
22412		 */
22413		moveBy: function(dx, dy) {
22414			var self = this, rect = self.layoutRect();
22415
22416			self.moveTo(rect.x + dx, rect.y + dy);
22417
22418			return self;
22419		},
22420
22421		/**
22422		 * Move to absolute position.
22423		 *
22424		 * @method moveTo
22425		 * @param {Number} x Absolute x position.
22426		 * @param {Number} y Absolute y position.
22427		 * @return {tinymce.ui.Control} Current control instance.
22428		 */
22429		moveTo: function(x, y) {
22430			var self = this;
22431
22432			// TODO: Move this to some global class
22433			function contrain(value, max, size) {
22434				if (value < 0) {
22435					return 0;
22436				}
22437
22438				if (value + size > max) {
22439					value = max - size;
22440					return value < 0 ? 0 : value;
22441				}
22442
22443				return value;
22444			}
22445
22446			if (self.settings.constrainToViewport) {
22447				var viewPortRect = DomUtils.getViewPort(window);
22448				var layoutRect = self.layoutRect();
22449
22450				x = contrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
22451				y = contrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
22452			}
22453
22454			if (self._rendered) {
22455				self.layoutRect({x: x, y: y}).repaint();
22456			} else {
22457				self.settings.x = x;
22458				self.settings.y = y;
22459			}
22460
22461			self.fire('move', {x: x, y: y});
22462
22463			return self;
22464		}
22465	};
22466});
22467
22468// Included from: js/tinymce/classes/ui/Resizable.js
22469
22470/**
22471 * Resizable.js
22472 *
22473 * Copyright, Moxiecode Systems AB
22474 * Released under LGPL License.
22475 *
22476 * License: http://www.tinymce.com/license
22477 * Contributing: http://www.tinymce.com/contributing
22478 */
22479
22480/**
22481 * Resizable mixin. Enables controls to be resized.
22482 *
22483 * @mixin tinymce.ui.Resizable
22484 */
22485define("tinymce/ui/Resizable", [
22486	"tinymce/ui/DomUtils"
22487], function(DomUtils) {
22488	"use strict";
22489
22490	return {
22491		/** 
22492		 * Resizes the control to contents.
22493		 *
22494		 * @method resizeToContent
22495		 */
22496		resizeToContent: function() {
22497			this._layoutRect.autoResize = true;
22498			this._lastRect = null;
22499			this.reflow();
22500		},
22501
22502		/** 
22503		 * Resizes the control to a specific width/height.
22504		 *
22505		 * @method resizeTo
22506		 * @param {Number} w Control width.
22507		 * @param {Number} h Control height.
22508		 * @return {tinymce.ui.Control} Current control instance.
22509		 */
22510		resizeTo: function(w, h) {
22511			// TODO: Fix hack
22512			if (w <= 1 || h <= 1) {
22513				var rect = DomUtils.getWindowSize();
22514
22515				w = w <= 1 ? w * rect.w : w;
22516				h = h <= 1 ? h * rect.h : h;
22517			}
22518
22519			this._layoutRect.autoResize = false;
22520			return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow();
22521		},
22522
22523		/** 
22524		 * Resizes the control to a specific relative width/height.
22525		 *
22526		 * @method resizeBy
22527		 * @param {Number} dw Relative control width.
22528		 * @param {Number} dh Relative control height.
22529		 * @return {tinymce.ui.Control} Current control instance.
22530		 */
22531		resizeBy: function(dw, dh) {
22532			var self = this, rect = self.layoutRect();
22533
22534			return self.resizeTo(rect.w + dw, rect.h + dh);
22535		}
22536	};
22537});
22538
22539// Included from: js/tinymce/classes/ui/FloatPanel.js
22540
22541/**
22542 * FloatPanel.js
22543 *
22544 * Copyright, Moxiecode Systems AB
22545 * Released under LGPL License.
22546 *
22547 * License: http://www.tinymce.com/license
22548 * Contributing: http://www.tinymce.com/contributing
22549 */
22550
22551/**
22552 * This class creates a floating panel.
22553 *
22554 * @-x-less FloatPanel.less
22555 * @class tinymce.ui.FloatPanel
22556 * @extends tinymce.ui.Panel
22557 * @mixes tinymce.ui.Movable
22558 * @mixes tinymce.ui.Resizable
22559 */
22560define("tinymce/ui/FloatPanel", [
22561	"tinymce/ui/Panel",
22562	"tinymce/ui/Movable",
22563	"tinymce/ui/Resizable",
22564	"tinymce/ui/DomUtils"
22565], function(Panel, Movable, Resizable, DomUtils) {
22566	"use strict";
22567
22568	var documentClickHandler, documentScrollHandler, visiblePanels = [];
22569	var zOrder = [], hasModal;
22570
22571	var FloatPanel = Panel.extend({
22572		Mixins: [Movable, Resizable],
22573
22574		/**
22575		 * Constructs a new control instance with the specified settings.
22576		 *
22577		 * @constructor
22578		 * @param {Object} settings Name/value object with settings.
22579		 * @setting {Boolean} autohide Automatically hide the panel.
22580		 */
22581		init: function(settings) {
22582			var self = this;
22583
22584			function reorder() {
22585				var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
22586
22587				if (zOrder.length) {
22588					for (i = 0; i < zOrder.length; i++) {
22589						if (zOrder[i].modal) {
22590							zIndex++;
22591							topModal = zOrder[i];
22592						}
22593
22594						zOrder[i].getEl().style.zIndex = zIndex;
22595						zOrder[i].zIndex = zIndex;
22596						zIndex++;
22597					}
22598				}
22599
22600				var modalBlockEl = document.getElementById(self.classPrefix + 'modal-block');
22601
22602				if (topModal) {
22603					DomUtils.css(modalBlockEl, 'z-index', topModal.zIndex - 1);
22604				} else if (modalBlockEl) {
22605					modalBlockEl.parentNode.removeChild(modalBlockEl);
22606					hasModal = false;
22607				}
22608
22609				FloatPanel.currentZIndex = zIndex;
22610			}
22611
22612			function isChildOf(ctrl, parent) {
22613				while (ctrl) {
22614					if (ctrl == parent) {
22615						return true;
22616					}
22617
22618					ctrl = ctrl.parent();
22619				}
22620			}
22621
22622			/**
22623			 * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
22624			 * also reposition all child panels of the current panel.
22625			 */
22626			function repositionPanel(panel) {
22627				var scrollY = DomUtils.getViewPort().y;
22628
22629				function toggleFixedChildPanels(fixed, deltaY) {
22630					var parent;
22631
22632					for (var i = 0; i < visiblePanels.length; i++) {
22633						if (visiblePanels[i] != panel) {
22634							parent = visiblePanels[i].parent();
22635
22636							while (parent && (parent = parent.parent())) {
22637								if (parent == panel) {
22638									visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
22639								}
22640							}
22641						}
22642					}
22643				}
22644
22645				if (panel.settings.autofix) {
22646					if (!panel._fixed) {
22647						panel._autoFixY = panel.layoutRect().y;
22648
22649						if (panel._autoFixY < scrollY) {
22650							panel.fixed(true).layoutRect({y: 0}).repaint();
22651							toggleFixedChildPanels(true, scrollY - panel._autoFixY);
22652						}
22653					} else {
22654						if (panel._autoFixY > scrollY) {
22655							panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
22656							toggleFixedChildPanels(false, panel._autoFixY - scrollY);
22657						}
22658					}
22659				}
22660			}
22661
22662			self._super(settings);
22663			self._eventsRoot = self;
22664
22665			self.addClass('floatpanel');
22666
22667			// Hide floatpanes on click out side the root button
22668			if (settings.autohide) {
22669				if (!documentClickHandler) {
22670					documentClickHandler = function(e) {
22671						var i, clickCtrl = self.getParentCtrl(e.target);
22672
22673						// Hide any float panel when a click is out side that float panel and the
22674						// float panels direct parent for example a click on a menu button
22675						i = visiblePanels.length;
22676						while (i--) {
22677							var panel = visiblePanels[i];
22678
22679							if (panel.settings.autohide) {
22680								if (clickCtrl) {
22681									if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
22682										continue;
22683									}
22684								}
22685
22686								e = panel.fire('autohide', {target: e.target});
22687								if (!e.isDefaultPrevented()) {
22688									panel.hide();
22689								}
22690							}
22691						}
22692					};
22693
22694					DomUtils.on(document, 'click', documentClickHandler);
22695				}
22696
22697				visiblePanels.push(self);
22698			}
22699
22700			if (settings.autofix) {
22701				if (!documentScrollHandler) {
22702					documentScrollHandler = function() {
22703						var i;
22704
22705						i = visiblePanels.length;
22706						while (i--) {
22707							repositionPanel(visiblePanels[i]);
22708						}
22709					};
22710
22711					DomUtils.on(window, 'scroll', documentScrollHandler);
22712				}
22713
22714				self.on('move', function() {
22715					repositionPanel(this);
22716				});
22717			}
22718
22719			self.on('postrender show', function(e) {
22720				if (e.control == self) {
22721					var modalBlockEl, prefix = self.classPrefix;
22722
22723					if (self.modal && !hasModal) {
22724						modalBlockEl = DomUtils.createFragment('<div id="' + prefix + 'modal-block" class="' +
22725							prefix + 'reset ' + prefix + 'fade"></div>');
22726						modalBlockEl = modalBlockEl.firstChild;
22727
22728						self.getContainerElm().appendChild(modalBlockEl);
22729
22730						setTimeout(function() {
22731							DomUtils.addClass(modalBlockEl, prefix + 'in');
22732							DomUtils.addClass(self.getEl(), prefix + 'in');
22733						}, 0);
22734
22735						hasModal = true;
22736					}
22737
22738					zOrder.push(self);
22739					reorder();
22740				}
22741			});
22742
22743			self.on('close hide', function(e) {
22744				if (e.control == self) {
22745					var i = zOrder.length;
22746
22747					while (i--) {
22748						if (zOrder[i] === self) {
22749							zOrder.splice(i, 1);
22750						}
22751					}
22752
22753					reorder();
22754				}
22755			});
22756
22757			self.on('show', function() {
22758				self.parents().each(function(ctrl) {
22759					if (ctrl._fixed) {
22760						self.fixed(true);
22761						return false;
22762					}
22763				});
22764			});
22765
22766			if (settings.popover) {
22767				self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
22768				self.addClass('popover').addClass('bottom').addClass(self.isRtl() ? 'end' : 'start');
22769			}
22770		},
22771
22772		fixed: function(state) {
22773			var self = this;
22774
22775			if (self._fixed != state) {
22776				if (self._rendered) {
22777					var viewport = DomUtils.getViewPort();
22778
22779					if (state) {
22780						self.layoutRect().y -= viewport.y;
22781					} else {
22782						self.layoutRect().y += viewport.y;
22783					}
22784				}
22785
22786				self.toggleClass('fixed', state);
22787				self._fixed = state;
22788			}
22789
22790			return self;
22791		},
22792
22793		/**
22794		 * Shows the current float panel.
22795		 *
22796		 * @method show
22797		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
22798		 */
22799		show: function() {
22800			var self = this, i, state = self._super();
22801
22802			i = visiblePanels.length;
22803			while (i--) {
22804				if (visiblePanels[i] === self) {
22805					break;
22806				}
22807			}
22808
22809			if (i === -1) {
22810				visiblePanels.push(self);
22811			}
22812
22813			return state;
22814		},
22815
22816		/**
22817		 * Hides the current float panel.
22818		 *
22819		 * @method hide
22820		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
22821		 */
22822		hide: function() {
22823			removeVisiblePanel(this);
22824			return this._super();
22825		},
22826
22827		/**
22828		 * Hides all visible the float panels.
22829		 *
22830		 * @method hideAll
22831		 */
22832		hideAll: function() {
22833			FloatPanel.hideAll();
22834		},
22835
22836		/**
22837		 * Closes the float panel. This will remove the float panel from page and fire the close event.
22838		 *
22839		 * @method close
22840		 */
22841		close: function() {
22842			var self = this;
22843
22844			self.fire('close');
22845
22846			return self.remove();
22847		},
22848
22849		/**
22850		 * Removes the float panel from page.
22851		 *
22852		 * @method remove
22853		 */
22854		remove: function() {
22855			removeVisiblePanel(this);
22856			this._super();
22857		}
22858	});
22859
22860	/**
22861	 * Hides all visible the float panels.
22862	 *
22863	 * @static
22864	 * @method hideAll
22865	 */
22866	FloatPanel.hideAll = function() {
22867		var i = visiblePanels.length;
22868
22869		while (i--) {
22870			var panel = visiblePanels[i];
22871
22872			if (panel.settings.autohide) {
22873				panel.fire('cancel', {}, false);
22874				panel.hide();
22875				visiblePanels.splice(i, 1);
22876			}
22877		}
22878	};
22879
22880	function removeVisiblePanel(panel) {
22881		var i;
22882
22883		i = visiblePanels.length;
22884		while (i--) {
22885			if (visiblePanels[i] === panel) {
22886				visiblePanels.splice(i, 1);
22887			}
22888		}
22889
22890		i = zOrder.length;
22891		while (i--) {
22892			if (zOrder[i] === panel) {
22893				zOrder.splice(i, 1);
22894			}
22895		}
22896	}
22897
22898	return FloatPanel;
22899});
22900
22901// Included from: js/tinymce/classes/ui/KeyboardNavigation.js
22902
22903/**
22904 * KeyboardNavigation.js
22905 *
22906 * Copyright, Moxiecode Systems AB
22907 * Released under LGPL License.
22908 *
22909 * License: http://www.tinymce.com/license
22910 * Contributing: http://www.tinymce.com/contributing
22911 */
22912
22913/**
22914 * This class handles keyboard navigation of controls and elements.
22915 *
22916 * @class tinymce.ui.KeyboardNavigation
22917 */
22918define("tinymce/ui/KeyboardNavigation", [
22919	"tinymce/ui/DomUtils"
22920], function(DomUtils) {
22921	"use strict";
22922
22923	/**
22924	 * Create a new KeyboardNavigation instance to handle the focus for a specific element.
22925	 *
22926	 * @constructor
22927	 * @param {Object} settings the settings object to define how keyboard navigation works.
22928	 *
22929	 * @setting {tinymce.ui.Control} root the root control navigation focus movement is scoped to this root.
22930	 * @setting {Array} items an array containing the items to move focus between. Every object in this array must have an
22931	 *                        id attribute which maps to the actual DOM element and it must be able to have focus i.e. tabIndex=-1.
22932	 * @setting {Function} onCancel the callback for when the user presses escape or otherwise indicates canceling.
22933	 * @setting {Function} onAction (optional) the action handler to call when the user activates an item.
22934	 * @setting {Boolean} enableLeftRight (optional, default) when true, the up/down arrows move through items.
22935	 * @setting {Boolean} enableUpDown (optional) when true, the up/down arrows move through items.
22936	 * Note for both up/down and left/right explicitly set both enableLeftRight and enableUpDown to true.
22937	 */
22938	return function(settings) {
22939		var root = settings.root, enableUpDown = settings.enableUpDown !== false;
22940		var enableLeftRight = settings.enableLeftRight !== false;
22941		var items = settings.items, focussedId;
22942
22943		/**
22944		 * Initializes the items array if needed. This will collect items/elements
22945		 * from the specified root control.
22946		 */
22947		function initItems() {
22948			if (!items) {
22949				items = [];
22950
22951				if (root.find) {
22952					// Root is a container then get child elements using the UI API
22953					root.find('*').each(function(ctrl) {
22954						if (ctrl.canFocus) {
22955							items.push(ctrl.getEl());
22956						}
22957					});
22958				} else {
22959					// Root is a control/widget then get the child elements of that control
22960					var elements = root.getEl().getElementsByTagName('*');
22961					for (var i = 0; i < elements.length; i++) {
22962						if (elements[i].id && elements[i]) {
22963							items.push(elements[i]);
22964						}
22965					}
22966				}
22967			}
22968		}
22969
22970		/**
22971		 * Returns the currently focused element.
22972		 *
22973		 * @private
22974		 * @return {Element} Currently focused element.
22975		 */
22976		function getFocusElement() {
22977			return document.getElementById(focussedId);
22978		}
22979
22980		/**
22981		 * Returns the currently focused elements wai aria role.
22982		 *
22983		 * @private
22984		 * @param {Element} elm Optional element to get role from.
22985		 * @return {String} Role of specified element.
22986		 */
22987		function getRole(elm) {
22988			elm = elm || getFocusElement();
22989
22990			return elm && elm.getAttribute('role');
22991		}
22992
22993		/**
22994		 * Returns the role of the parent element.
22995		 *
22996		 * @private
22997		 * @param {Element} elm Optional element to get parent role from.
22998		 * @return {String} Role of the first parent that has a role.
22999		 */
23000		function getParentRole(elm) {
23001			var role, parent = elm || getFocusElement();
23002
23003			while ((parent = parent.parentNode)) {
23004				if ((role = getRole(parent))) {
23005					return role;
23006				}
23007			}
23008		}
23009
23010		/**
23011		 * Returns an wai aria property by name.
23012		 *
23013		 * @private
23014		 * @param {String} name Name of the aria property to get for example "disabled".
23015		 * @return {String} Aria property value.
23016		 */
23017		function getAriaProp(name) {
23018			var elm = document.getElementById(focussedId);
23019
23020			if (elm) {
23021				return elm.getAttribute('aria-' + name);
23022			}
23023		}
23024
23025		/**
23026		 * Executes the onAction event callback. This is when the user presses enter/space.
23027		 *
23028		 * @private
23029		 */
23030		function action() {
23031			var focusElm = getFocusElement();
23032
23033			if (focusElm && (focusElm.nodeName == "TEXTAREA" || focusElm.type == "text")) {
23034				return;
23035			}
23036
23037			if (settings.onAction) {
23038				settings.onAction(focussedId);
23039			} else {
23040				DomUtils.fire(getFocusElement(), 'click', {keyboard: true});
23041			}
23042
23043			return true;
23044		}
23045
23046		/**
23047		 * Cancels the current navigation. The same as pressing the Esc key.
23048		 *
23049		 * @method cancel
23050		 */
23051		function cancel() {
23052			var focusElm;
23053
23054			if (settings.onCancel) {
23055				if ((focusElm = getFocusElement())) {
23056					focusElm.blur();
23057				}
23058
23059				settings.onCancel();
23060			} else {
23061				settings.root.fire('cancel');
23062			}
23063		}
23064
23065		/**
23066		 * Moves the focus to the next or previous item. It will wrap to start/end if it can't move.
23067		 *
23068		 * @method moveFocus
23069		 * @param {Number} dir Direction for move -1 or 1.
23070		 */
23071		function moveFocus(dir) {
23072			var idx = -1, focusElm, i;
23073			var visibleItems = [];
23074
23075			function isVisible(elm) {
23076				var rootElm = root ? root.getEl() : document.body;
23077
23078				while (elm && elm != rootElm) {
23079					if (elm.style.display == 'none') {
23080						return false;
23081					}
23082
23083					elm = elm.parentNode;
23084				}
23085
23086				return true;
23087			}
23088
23089			initItems();
23090
23091			// TODO: Optimize this, will be slow on lots of items
23092			i = visibleItems.length;
23093			for (i = 0; i < items.length; i++) {
23094				if (isVisible(items[i])) {
23095					visibleItems.push(items[i]);
23096				}
23097			}
23098
23099			i = visibleItems.length;
23100			while (i--) {
23101				if (visibleItems[i].id === focussedId) {
23102					idx = i;
23103					break;
23104				}
23105			}
23106
23107			idx += dir;
23108			if (idx < 0) {
23109				idx = visibleItems.length - 1;
23110			} else if (idx >= visibleItems.length) {
23111				idx = 0;
23112			}
23113
23114			focusElm = visibleItems[idx];
23115			focusElm.focus();
23116			focussedId = focusElm.id;
23117
23118			if (settings.actOnFocus) {
23119				action();
23120			}
23121		}
23122
23123		/**
23124		 * Moves focus to the first item or the last focused item if root is a toolbar.
23125		 *
23126		 * @method focusFirst
23127		 * @return {[type]} [description]
23128		 */
23129		function focusFirst() {
23130			var i, rootRole;
23131
23132			rootRole = getRole(settings.root.getEl());
23133			initItems();
23134
23135			i = items.length;
23136			while (i--) {
23137				if (rootRole == 'toolbar' && items[i].id === focussedId) {
23138					items[i].focus();
23139					return;
23140				}
23141			}
23142
23143			items[0].focus();
23144		}
23145
23146		// Handle accessible keys
23147		root.on('keydown', function(e) {
23148			var DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40;
23149			var DOM_VK_ESCAPE = 27, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32, DOM_VK_TAB = 9;
23150			var preventDefault;
23151
23152			switch (e.keyCode) {
23153				case DOM_VK_LEFT:
23154					if (enableLeftRight) {
23155						if (settings.leftAction) {
23156							settings.leftAction();
23157						} else {
23158							moveFocus(-1);
23159						}
23160
23161						preventDefault = true;
23162					}
23163					break;
23164
23165				case DOM_VK_RIGHT:
23166					if (enableLeftRight) {
23167						if (getRole() == 'menuitem' && getParentRole() == 'menu') {
23168							if (getAriaProp('haspopup')) {
23169								action();
23170							}
23171						} else {
23172							moveFocus(1);
23173						}
23174
23175						preventDefault = true;
23176					}
23177					break;
23178
23179				case DOM_VK_UP:
23180					if (enableUpDown) {
23181						moveFocus(-1);
23182						preventDefault = true;
23183					}
23184					break;
23185
23186				case DOM_VK_DOWN:
23187					if (enableUpDown) {
23188						if (getRole() == 'menuitem' && getParentRole() == 'menubar') {
23189							action();
23190						} else if (getRole() == 'button' && getAriaProp('haspopup')) {
23191							action();
23192						} else {
23193							moveFocus(1);
23194						}
23195
23196						preventDefault = true;
23197					}
23198					break;
23199
23200				case DOM_VK_TAB:
23201					preventDefault = true;
23202
23203					if (e.shiftKey) {
23204						moveFocus(-1);
23205					} else {
23206						moveFocus(1);
23207					}
23208					break;
23209
23210				case DOM_VK_ESCAPE:
23211					preventDefault = true;
23212					cancel();
23213					break;
23214
23215				case DOM_VK_ENTER:
23216				case DOM_VK_RETURN:
23217				case DOM_VK_SPACE:
23218					preventDefault = action();
23219					break;
23220			}
23221
23222			if (preventDefault) {
23223				e.stopPropagation();
23224				e.preventDefault();
23225			}
23226		});
23227
23228		// Init on focus in
23229		root.on('focusin', function(e) {
23230			initItems();
23231			focussedId = e.target.id;
23232		});
23233
23234		return {
23235			moveFocus: moveFocus,
23236			focusFirst: focusFirst,
23237			cancel: cancel
23238		};
23239	};
23240});
23241
23242// Included from: js/tinymce/classes/ui/Window.js
23243
23244/**
23245 * Window.js
23246 *
23247 * Copyright, Moxiecode Systems AB
23248 * Released under LGPL License.
23249 *
23250 * License: http://www.tinymce.com/license
23251 * Contributing: http://www.tinymce.com/contributing
23252 */
23253
23254/**
23255 * Creates a new window.
23256 *
23257 * @-x-less Window.less
23258 * @class tinymce.ui.Window
23259 * @extends tinymce.ui.FloatPanel
23260 */
23261define("tinymce/ui/Window", [
23262	"tinymce/ui/FloatPanel",
23263	"tinymce/ui/Panel",
23264	"tinymce/ui/DomUtils",
23265	"tinymce/ui/KeyboardNavigation",
23266	"tinymce/ui/DragHelper"
23267], function(FloatPanel, Panel, DomUtils, KeyboardNavigation, DragHelper) {
23268	"use strict";
23269
23270	var Window = FloatPanel.extend({
23271		modal: true,
23272
23273		Defaults: {
23274			border: 1,
23275			layout: 'flex',
23276			containerCls: 'panel',
23277			role: 'dialog',
23278			callbacks: {
23279				submit: function() {
23280					this.fire('submit', {data: this.toJSON()});
23281				},
23282
23283				close: function() {
23284					this.close();
23285				}
23286			}
23287		},
23288
23289		/**
23290		 * Constructs a instance with the specified settings.
23291		 *
23292		 * @constructor
23293		 * @param {Object} settings Name/value object with settings.
23294		 */
23295		init: function(settings) {
23296			var self = this;
23297
23298			self._super(settings);
23299
23300			if (self.isRtl()) {
23301				self.addClass('rtl');
23302			}
23303
23304			self.addClass('window');
23305			self._fixed = true;
23306
23307			// Create statusbar
23308			if (settings.buttons) {
23309				self.statusbar = new Panel({
23310					layout: 'flex',
23311					border: '1 0 0 0',
23312					spacing: 3,
23313					padding: 10,
23314					align: 'center',
23315					pack: self.isRtl() ? 'start' : 'end',
23316					defaults: {
23317						type: 'button'
23318					},
23319					items: settings.buttons
23320				});
23321
23322				self.statusbar.addClass('foot');
23323				self.statusbar.parent(self);
23324			}
23325
23326			self.on('click', function(e) {
23327				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
23328					self.close();
23329				}
23330			});
23331
23332			self.aria('label', settings.title);
23333			self._fullscreen = false;
23334		},
23335
23336		/**
23337		 * Recalculates the positions of the controls in the current container.
23338		 * This is invoked by the reflow method and shouldn't be called directly.
23339		 *
23340		 * @method recalc
23341		 */
23342		recalc: function() {
23343			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
23344
23345			if (self._fullscreen) {
23346				self.layoutRect(DomUtils.getWindowSize());
23347				self.layoutRect().contentH = self.layoutRect().innerH;
23348			}
23349
23350			self._super();
23351
23352			layoutRect = self.layoutRect();
23353
23354			// Resize window based on title width
23355			if (self.settings.title && !self._fullscreen) {
23356				width = layoutRect.headerW;
23357				if (width > layoutRect.w) {
23358					x = layoutRect.x - Math.max(0, width / 2);
23359					self.layoutRect({w: width, x: x});
23360					needsRecalc = true;
23361				}
23362			}
23363
23364			// Resize window based on statusbar width
23365			if (statusbar) {
23366				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
23367
23368				width = statusbar.layoutRect().minW + layoutRect.deltaW;
23369				if (width > layoutRect.w) {
23370					x = layoutRect.x - Math.max(0, width - layoutRect.w);
23371					self.layoutRect({w: width, x: x});
23372					needsRecalc = true;
23373				}
23374			}
23375
23376			// Recalc body and disable auto resize
23377			if (needsRecalc) {
23378				self.recalc();
23379			}
23380		},
23381
23382		/**
23383		 * Initializes the current controls layout rect.
23384		 * This will be executed by the layout managers to determine the
23385		 * default minWidth/minHeight etc.
23386		 *
23387		 * @method initLayoutRect
23388		 * @return {Object} Layout rect instance.
23389		 */
23390		initLayoutRect: function() {
23391			var self = this, layoutRect = self._super(), deltaH = 0, headEl;
23392
23393			// Reserve vertical space for title
23394			if (self.settings.title && !self._fullscreen) {
23395				headEl = self.getEl('head');
23396
23397				var size = DomUtils.getSize(headEl);
23398
23399				layoutRect.headerW = size.width;
23400				layoutRect.headerH = size.height;
23401
23402				deltaH += layoutRect.headerH;
23403			}
23404
23405			// Reserve vertical space for statusbar
23406			if (self.statusbar) {
23407				deltaH += self.statusbar.layoutRect().h;
23408			}
23409
23410			layoutRect.deltaH += deltaH;
23411			layoutRect.minH += deltaH;
23412			//layoutRect.innerH -= deltaH;
23413			layoutRect.h += deltaH;
23414
23415			var rect = DomUtils.getWindowSize();
23416
23417			layoutRect.x = Math.max(0, rect.w / 2 - layoutRect.w / 2);
23418			layoutRect.y = Math.max(0, rect.h / 2 - layoutRect.h / 2);
23419
23420			return layoutRect;
23421		},
23422
23423		/**
23424		 * Renders the control as a HTML string.
23425		 *
23426		 * @method renderHtml
23427		 * @return {String} HTML representing the control.
23428		 */
23429		renderHtml: function() {
23430			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
23431			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
23432
23433			self.preRender();
23434			layout.preRender(self);
23435
23436			if (settings.title) {
23437				headerHtml = (
23438					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
23439						'<div class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
23440						'<button type="button" class="' + prefix + 'close" aria-hidden="true">&times;</button>' +
23441						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
23442					'</div>'
23443				);
23444			}
23445
23446			if (settings.url) {
23447				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
23448			}
23449
23450			if (typeof(html) == "undefined") {
23451				html = layout.renderHtml(self);
23452			}
23453
23454			if (self.statusbar) {
23455				footerHtml = self.statusbar.renderHtml();
23456			}
23457
23458			return (
23459				'<div id="' + id + '" class="' + self.classes() + '" hideFocus="1" tabIndex="-1">' +
23460					headerHtml +
23461					'<div id="' + id + '-body" class="' + self.classes('body') + '">' +
23462						html +
23463					'</div>' +
23464					footerHtml +
23465				'</div>'
23466			);
23467		},
23468
23469		/**
23470		 * Switches the window fullscreen mode.
23471		 *
23472		 * @method fullscreen
23473		 * @param {Boolean} state True/false state.
23474		 * @return {tinymce.ui.Window} Current window instance.
23475		 */
23476		fullscreen: function(state) {
23477			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
23478
23479			if (state != self._fullscreen) {
23480				DomUtils.on(window, 'resize', function() {
23481					var time;
23482
23483					if (self._fullscreen) {
23484						// Time the layout time if it's to slow use a timeout to not hog the CPU
23485						if (!slowRendering) {
23486							time = new Date().getTime();
23487
23488							var rect = DomUtils.getWindowSize();
23489							self.moveTo(0, 0).resizeTo(rect.w, rect.h);
23490
23491							if ((new Date().getTime()) - time > 50) {
23492								slowRendering = true;
23493							}
23494						} else {
23495							if (!self._timer) {
23496								self._timer = setTimeout(function() {
23497									var rect = DomUtils.getWindowSize();
23498									self.moveTo(0, 0).resizeTo(rect.w, rect.h);
23499
23500									self._timer = 0;
23501								}, 50);
23502							}
23503						}
23504					}
23505				});
23506
23507				layoutRect = self.layoutRect();
23508				self._fullscreen = state;
23509
23510				if (!state) {
23511					self._borderBox = self.parseBox(self.settings.border);
23512					self.getEl('head').style.display = '';
23513					layoutRect.deltaH += layoutRect.headerH;
23514					DomUtils.removeClass(documentElement, prefix + 'fullscreen');
23515					DomUtils.removeClass(document.body, prefix + 'fullscreen');
23516					self.removeClass('fullscreen');
23517					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
23518				} else {
23519					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
23520
23521					self._borderBox = self.parseBox('0');
23522					self.getEl('head').style.display = 'none';
23523					layoutRect.deltaH -= layoutRect.headerH + 2;
23524					DomUtils.addClass(documentElement, prefix + 'fullscreen');
23525					DomUtils.addClass(document.body, prefix + 'fullscreen');
23526					self.addClass('fullscreen');
23527
23528					var rect = DomUtils.getWindowSize();
23529					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
23530				}
23531			}
23532
23533			return self.reflow();
23534		},
23535
23536		/**
23537		 * Called after the control has been rendered.
23538		 *
23539		 * @method postRender
23540		 */
23541		postRender: function() {
23542			var self = this, items = [], focusCtrl, autoFocusFound, startPos;
23543
23544			setTimeout(function() {
23545				self.addClass('in');
23546			}, 0);
23547
23548			self.keyboardNavigation = new KeyboardNavigation({
23549				root: self,
23550				enableLeftRight: false,
23551				enableUpDown: false,
23552				items: items,
23553				onCancel: function() {
23554					self.close();
23555				}
23556			});
23557
23558			self.find('*').each(function(ctrl) {
23559				if (ctrl.canFocus) {
23560					autoFocusFound = autoFocusFound || ctrl.settings.autofocus;
23561					focusCtrl = focusCtrl || ctrl;
23562
23563					// TODO: Figure out a better way
23564					if (ctrl.type == 'filepicker') {
23565						items.push(ctrl.getEl('inp'));
23566
23567						if (ctrl.getEl('open')) {
23568							items.push(ctrl.getEl('open'));
23569						}
23570					} else {
23571						items.push(ctrl.getEl());
23572					}
23573				}
23574			});
23575
23576			if (self.statusbar) {
23577				self.statusbar.find('*').each(function(ctrl) {
23578					if (ctrl.canFocus) {
23579						autoFocusFound = autoFocusFound || ctrl.settings.autofocus;
23580						focusCtrl = focusCtrl || ctrl;
23581						items.push(ctrl.getEl());
23582					}
23583				});
23584			}
23585
23586			self._super();
23587
23588			if (self.statusbar) {
23589				self.statusbar.postRender();
23590			}
23591
23592			if (!autoFocusFound && focusCtrl) {
23593				focusCtrl.focus();
23594			}
23595
23596			this.dragHelper = new DragHelper(self._id + '-dragh', {
23597				start: function() {
23598					startPos = {
23599						x: self.layoutRect().x,
23600						y: self.layoutRect().y
23601					};
23602				},
23603
23604				drag: function(e) {
23605					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
23606				}
23607			});
23608
23609			self.on('submit', function(e) {
23610				if (!e.isDefaultPrevented()) {
23611					self.close();
23612				}
23613			});
23614		},
23615
23616		/**
23617		 * Fires a submit event with the serialized form.
23618		 *
23619		 * @method submit
23620		 * @return {Object} Event arguments object.
23621		 */
23622		submit: function() {
23623			return this.fire('submit', {data: this.toJSON()});
23624		},
23625
23626		/**
23627		 * Removes the current control from DOM and from UI collections.
23628		 *
23629		 * @method remove
23630		 * @return {tinymce.ui.Control} Current control instance.
23631		 */
23632		remove: function() {
23633			var self = this, prefix = self.classPrefix;
23634
23635			self.dragHelper.destroy();
23636			self._super();
23637
23638			if (self.statusbar) {
23639				this.statusbar.remove();
23640			}
23641
23642			if (self._fullscreen) {
23643				DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
23644				DomUtils.removeClass(document.body, prefix + 'fullscreen');
23645			}
23646		}
23647	});
23648
23649	return Window;
23650});
23651
23652// Included from: js/tinymce/classes/ui/MessageBox.js
23653
23654/**
23655 * MessageBox.js
23656 *
23657 * Copyright, Moxiecode Systems AB
23658 * Released under LGPL License.
23659 *
23660 * License: http://www.tinymce.com/license
23661 * Contributing: http://www.tinymce.com/contributing
23662 */
23663
23664/**
23665 * This class is used to create MessageBoxes like alerts/confirms etc.
23666 *
23667 * @class tinymce.ui.Window
23668 * @extends tinymce.ui.FloatPanel
23669 */
23670define("tinymce/ui/MessageBox", [
23671	"tinymce/ui/Window"
23672], function(Window) {
23673	"use strict";
23674
23675	var MessageBox = Window.extend({
23676		/**
23677		 * Constructs a instance with the specified settings.
23678		 *
23679		 * @constructor
23680		 * @param {Object} settings Name/value object with settings.
23681		 */
23682		init: function(settings) {
23683			settings = {
23684				border: 1,
23685				padding: 20,
23686				layout: 'flex',
23687				pack: "center",
23688				align: "center",
23689				containerCls: 'panel',
23690				autoScroll: true,
23691				buttons: {type: "button", text: "Ok", action: "ok"},
23692				items: {
23693					type: "label",
23694					multiline: true,
23695					maxWidth: 500,
23696					maxHeight: 200
23697				}
23698			};
23699
23700			this._super(settings);
23701		},
23702
23703		Statics: {
23704			/**
23705			 * Ok buttons constant.
23706			 *
23707			 * @static
23708			 * @final
23709			 * @field {Number} OK
23710			 */
23711			OK: 1,
23712
23713			/**
23714			 * Ok/cancel buttons constant.
23715			 *
23716			 * @static
23717			 * @final
23718			 * @field {Number} OK_CANCEL
23719			 */
23720			OK_CANCEL: 2,
23721
23722			/**
23723			 * yes/no buttons constant.
23724			 *
23725			 * @static
23726			 * @final
23727			 * @field {Number} YES_NO
23728			 */
23729			YES_NO: 3,
23730
23731			/**
23732			 * yes/no/cancel buttons constant.
23733			 *
23734			 * @static
23735			 * @final
23736			 * @field {Number} YES_NO_CANCEL
23737			 */
23738			YES_NO_CANCEL: 4,
23739
23740			/**
23741			 * Constructs a new message box and renders it to the body element.
23742			 *
23743			 * @static
23744			 * @method msgBox
23745			 * @param {Object} settings Name/value object with settings.
23746			 */
23747			msgBox: function(settings) {
23748				var buttons, callback = settings.callback || function() {};
23749
23750				switch (settings.buttons) {
23751					case MessageBox.OK_CANCEL:
23752						buttons = [
23753							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
23754								e.control.parents()[1].close();
23755								callback(true);
23756							}},
23757
23758							{type: "button", text: "Cancel", onClick: function(e) {
23759								e.control.parents()[1].close();
23760								callback(false);
23761							}}
23762						];
23763						break;
23764
23765					case MessageBox.YES_NO:
23766						buttons = [
23767							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
23768								e.control.parents()[1].close();
23769								callback(true);
23770							}}
23771						];
23772						break;
23773
23774					case MessageBox.YES_NO_CANCEL:
23775						buttons = [
23776							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
23777								e.control.parents()[1].close();
23778							}}
23779						];
23780						break;
23781
23782					default:
23783						buttons = [
23784							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
23785								e.control.parents()[1].close();
23786								callback(true);
23787							}}
23788						];
23789						break;
23790				}
23791
23792				return new Window({
23793					padding: 20,
23794					x: settings.x,
23795					y: settings.y,
23796					minWidth: 300,
23797					minHeight: 100,
23798					layout: "flex",
23799					pack: "center",
23800					align: "center",
23801					buttons: buttons,
23802					title: settings.title,
23803					items: {
23804						type: "label",
23805						multiline: true,
23806						maxWidth: 500,
23807						maxHeight: 200,
23808						text: settings.text
23809					},
23810					onClose: settings.onClose
23811				}).renderTo(document.body).reflow();
23812			},
23813
23814			/**
23815			 * Creates a new alert dialog.
23816			 *
23817			 * @method alert
23818			 * @param {Object} settings Settings for the alert dialog.
23819			 * @param {function} [callback] Callback to execute when the user makes a choice.
23820			 */
23821			alert: function(settings, callback) {
23822				if (typeof(settings) == "string") {
23823					settings = {text: settings};
23824				}
23825
23826				settings.callback = callback;
23827				return MessageBox.msgBox(settings);
23828			},
23829
23830			/**
23831			 * Creates a new confirm dialog.
23832			 *
23833			 * @method confirm
23834			 * @param {Object} settings Settings for the confirm dialog.
23835			 * @param {function} [callback] Callback to execute when the user makes a choice.
23836			 */
23837			confirm: function(settings, callback) {
23838				if (typeof(settings) == "string") {
23839					settings = {text: settings};
23840				}
23841
23842				settings.callback = callback;
23843				settings.buttons = MessageBox.OK_CANCEL;
23844
23845				return MessageBox.msgBox(settings);
23846			}
23847		}
23848	});
23849
23850	return MessageBox;
23851});
23852
23853// Included from: js/tinymce/classes/WindowManager.js
23854
23855/**
23856 * WindowManager.js
23857 *
23858 * Copyright, Moxiecode Systems AB
23859 * Released under LGPL License.
23860 *
23861 * License: http://www.tinymce.com/license
23862 * Contributing: http://www.tinymce.com/contributing
23863 */
23864
23865/**
23866 * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs.
23867 *
23868 * @class tinymce.WindowManager
23869 * @example
23870 * // Opens a new dialog with the file.htm file and the size 320x240
23871 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
23872 * tinymce.activeEditor.windowManager.open({
23873 *    url: 'file.htm',
23874 *    width: 320,
23875 *    height: 240
23876 * }, {
23877 *    custom_param: 1
23878 * });
23879 *
23880 * // Displays an alert box using the active editors window manager instance
23881 * tinymce.activeEditor.windowManager.alert('Hello world!');
23882 *
23883 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
23884 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
23885 *    if (s)
23886 *       tinymce.activeEditor.windowManager.alert("Ok");
23887 *    else
23888 *       tinymce.activeEditor.windowManager.alert("Cancel");
23889 * });
23890 */
23891define("tinymce/WindowManager", [
23892	"tinymce/ui/Window",
23893	"tinymce/ui/MessageBox"
23894], function(Window, MessageBox) {
23895	return function(editor) {
23896		var self = this, windows = [];
23897
23898		function getTopMostWindow() {
23899			if (windows.length) {
23900				return windows[windows.length - 1];
23901			}
23902		}
23903
23904		self.windows = windows;
23905
23906		/**
23907		 * Opens a new window.
23908		 *
23909		 * @method open
23910		 * @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
23911		 * @option {String} title Window title.
23912		 * @option {String} file URL of the file to open in the window.
23913		 * @option {Number} width Width in pixels.
23914		 * @option {Number} height Height in pixels.
23915		 * @option {Boolean} resizable Specifies whether the popup window is resizable or not.
23916		 * @option {Boolean} maximizable Specifies whether the popup window has a "maximize" button and can get maximized or not.
23917		 * @option {String/bool} scrollbars Specifies whether the popup window can have scrollbars if required (i.e. content
23918		 * larger than the popup size specified).
23919		 */
23920		self.open = function(args, params) {
23921			var win;
23922
23923			editor.editorManager.activeEditor = editor;
23924
23925			args.title = args.title || ' ';
23926
23927			// Handle URL
23928			args.url = args.url || args.file; // Legacy
23929			if (args.url) {
23930				args.width = parseInt(args.width || 320, 10);
23931				args.height = parseInt(args.height || 240, 10);
23932			}
23933
23934			// Handle body
23935			if (args.body) {
23936				args.items = {
23937					defaults: args.defaults,
23938					type: args.bodyType || 'form',
23939					items: args.body
23940				};
23941			}
23942
23943			if (!args.url && !args.buttons) {
23944				args.buttons = [
23945					{text: 'Ok', subtype: 'primary', onclick: function() {
23946						win.find('form')[0].submit();
23947						win.close();
23948					}},
23949
23950					{text: 'Cancel', onclick: function() {
23951						win.close();
23952					}}
23953				];
23954			}
23955
23956			win = new Window(args);
23957			windows.push(win);
23958
23959			win.on('close', function() {
23960				var i = windows.length;
23961
23962				while (i--) {
23963					if (windows[i] === win) {
23964						windows.splice(i, 1);
23965					}
23966				}
23967
23968				editor.focus();
23969			});
23970
23971			// Handle data
23972			if (args.data) {
23973				win.on('postRender', function() {
23974					this.find('*').each(function(ctrl) {
23975						var name = ctrl.name();
23976
23977						if (name in args.data) {
23978							ctrl.value(args.data[name]);
23979						}
23980					});
23981				});
23982			}
23983
23984			// store args and parameters
23985			win.features = args || {};
23986			win.params = params || {};
23987
23988			// Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
23989			editor.nodeChanged();
23990
23991			return win.renderTo(document.body).reflow();
23992		};
23993
23994		/**
23995		 * Creates a alert dialog. Please don't use the blocking behavior of this
23996		 * native version use the callback method instead then it can be extended.
23997		 *
23998		 * @method alert
23999		 * @param {String} message Text to display in the new alert dialog.
24000		 * @param {function} callback Callback function to be executed after the user has selected ok.
24001		 * @param {Object} scope Optional scope to execute the callback in.
24002		 * @example
24003		 * // Displays an alert box using the active editors window manager instance
24004		 * tinymce.activeEditor.windowManager.alert('Hello world!');
24005		 */
24006		self.alert = function(message, callback, scope) {
24007			MessageBox.alert(message, function() {
24008				if (callback) {
24009					callback.call(scope || this);
24010				}
24011			});
24012		};
24013
24014		/**
24015		 * Creates a confirm dialog. Please don't use the blocking behavior of this
24016		 * native version use the callback method instead then it can be extended.
24017		 *
24018		 * @method confirm
24019		 * @param {String} messageText to display in the new confirm dialog.
24020		 * @param {function} callback Callback function to be executed after the user has selected ok or cancel.
24021		 * @param {Object} scope Optional scope to execute the callback in.
24022		 * @example
24023		 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
24024		 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
24025		 *    if (s)
24026		 *       tinymce.activeEditor.windowManager.alert("Ok");
24027		 *    else
24028		 *       tinymce.activeEditor.windowManager.alert("Cancel");
24029		 * });
24030		 */
24031		self.confirm = function(message, callback, scope) {
24032			MessageBox.confirm(message, function(state) {
24033				callback.call(scope || this, state);
24034			});
24035		};
24036
24037		/**
24038		 * Closes the top most window.
24039		 *
24040		 * @method close
24041		 */
24042		self.close = function() {
24043			if (getTopMostWindow()) {
24044				getTopMostWindow().close();
24045			}
24046		};
24047
24048		/**
24049		 * Returns the params of the last window open call. This can be used in iframe based
24050		 * dialog to get params passed from the tinymce plugin.
24051		 *
24052		 * @example
24053		 * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams();
24054		 *
24055		 * @method getParams
24056		 * @return {Object} Name/value object with parameters passed from windowManager.open call.
24057		 */
24058		self.getParams = function() {
24059			return getTopMostWindow() ? getTopMostWindow().params : null;
24060		};
24061
24062		/**
24063		 * Sets the params of the last opened window.
24064		 *
24065		 * @method setParams
24066		 * @param {Object} params Params object to set for the last opened window.
24067		 */
24068		self.setParams = function(params) {
24069			if (getTopMostWindow()) {
24070				getTopMostWindow().params = params;
24071			}
24072		};
24073	};
24074});
24075
24076// Included from: js/tinymce/classes/util/Quirks.js
24077
24078/**
24079 * Quirks.js
24080 *
24081 * Copyright, Moxiecode Systems AB
24082 * Released under LGPL License.
24083 *
24084 * License: http://www.tinymce.com/license
24085 * Contributing: http://www.tinymce.com/contributing
24086 *
24087 * @ignore-file
24088 */
24089
24090/**
24091 * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
24092 *
24093 * @class tinymce.util.Quirks
24094 */
24095define("tinymce/util/Quirks", [
24096	"tinymce/util/VK",
24097	"tinymce/dom/RangeUtils",
24098	"tinymce/html/Node",
24099	"tinymce/html/Entities",
24100	"tinymce/Env",
24101	"tinymce/util/Tools"
24102], function(VK, RangeUtils, Node, Entities, Env, Tools) {
24103	return function(editor) {
24104		var each = Tools.each;
24105		var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
24106			settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
24107		var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
24108
24109		/**
24110		 * Executes a command with a specific state this can be to enable/disable browser editing features.
24111		 */
24112		function setEditorCommandState(cmd, state) {
24113			try {
24114				editor.getDoc().execCommand(cmd, false, state);
24115			} catch (ex) {
24116				// Ignore
24117			}
24118		}
24119
24120		/**
24121		 * Returns current IE document mode.
24122		 */
24123		function getDocumentMode() {
24124			var documentMode = editor.getDoc().documentMode;
24125
24126			return documentMode ? documentMode : 6;
24127		}
24128
24129		/**
24130		 * Returns true/false if the event is prevented or not.
24131		 *
24132		 * @private
24133		 * @param {Event} e Event object.
24134		 * @return {Boolean} true/false if the event is prevented or not.
24135		 */
24136		function isDefaultPrevented(e) {
24137			return e.isDefaultPrevented();
24138		}
24139
24140		/**
24141		 * Fixes a WebKit bug when deleting contents using backspace or delete key.
24142		 * WebKit will produce a span element if you delete across two block elements.
24143		 *
24144		 * Example:
24145		 * <h1>a</h1><p>|b</p>
24146		 *
24147		 * Will produce this on backspace:
24148		 * <h1>a<span style="<all runtime styles>">b</span></p>
24149		 *
24150		 * This fixes the backspace to produce:
24151		 * <h1>a|b</p>
24152		 *
24153		 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
24154		 *
24155		 * This fixes the following delete scenarios:
24156		 *  1. Delete by pressing backspace key.
24157		 *  2. Delete by pressing delete key.
24158		 *  3. Delete by pressing backspace key with ctrl/cmd (Word delete).
24159		 *  4. Delete by pressing delete key with ctrl/cmd (Word delete).
24160		 *  5. Delete by drag/dropping contents inside the editor.
24161		 *  6. Delete by using Cut Ctrl+X/Cmd+X.
24162		 *  7. Delete by selecting contents and writing a character.'
24163		 *
24164		 * This code is a ugly hack since writing full custom delete logic for just this bug
24165		 * fix seemed like a huge task. I hope we can remove this before the year 2030. 
24166		 */
24167		function cleanupStylesWhenDeleting() {
24168			var doc = editor.getDoc();
24169
24170			if (!window.MutationObserver) {
24171				return;
24172			}
24173
24174			function customDelete(isForward) {
24175				var mutationObserver = new MutationObserver(function() {});
24176
24177				Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
24178					// Mark existing spans
24179					if (elm.tagName == 'SPAN') {
24180						elm.setAttribute('mce-data-marked', 1);
24181					}
24182
24183					// Make sure all elements has a data-mce-style attribute
24184					if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
24185						editor.dom.setAttrib(elm, 'style', elm.getAttribute('style'));
24186					}
24187				});
24188
24189				// Observe added nodes and style attribute changes
24190				mutationObserver.observe(editor.getDoc(), {
24191					childList: true,
24192					attributes: true,
24193					subtree: true,
24194					attributeFilter: ['style']
24195				});
24196
24197				editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
24198
24199				var rng = editor.selection.getRng();
24200				var caretElement = rng.startContainer.parentNode;
24201
24202				Tools.each(mutationObserver.takeRecords(), function(record) {
24203					// Restore style attribute to previous value
24204					if (record.attributeName == "style") {
24205						var oldValue = record.target.getAttribute('data-mce-style');
24206
24207						if (oldValue) {
24208							record.target.setAttribute("style", oldValue);
24209						} else {
24210							record.target.removeAttribute("style");
24211						}
24212					}
24213
24214					// Remove all spans that isn't maked and retain selection
24215					Tools.each(record.addedNodes, function(node) {
24216						if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
24217							var offset, container;
24218
24219							if (node == caretElement) {
24220								offset = rng.startOffset;
24221								container = node.firstChild;
24222							}
24223
24224							dom.remove(node, true);
24225
24226							if (container) {
24227								rng.setStart(container, offset);
24228								rng.setEnd(container, offset);
24229								editor.selection.setRng(rng);
24230							}
24231						}
24232					});
24233				});
24234
24235				mutationObserver.disconnect();
24236
24237				// Remove any left over marks
24238				Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
24239					span.removeAttribute('mce-data-marked');
24240				});
24241			}
24242
24243			editor.on('keydown', function(e) {
24244				var isForward = e.keyCode == DELETE, isMeta = VK.metaKeyPressed(e);
24245
24246				if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
24247					var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
24248
24249					// Ignore non meta delete in the where there is text before/after the caret
24250					if (!isMeta && rng.collapsed && container.nodeType == 3) {
24251						if (isForward ? offset < container.data.length : offset > 0) {
24252							return;
24253						}
24254					}
24255
24256					e.preventDefault();
24257
24258					if (isMeta) {
24259						editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", "word");
24260					}
24261
24262					customDelete(isForward);
24263				}
24264			});
24265
24266			editor.on('keypress', function(e) {
24267				if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode) {
24268					e.preventDefault();
24269					customDelete(true);
24270					editor.selection.setContent(String.fromCharCode(e.charCode));
24271				}
24272			});
24273
24274			editor.addCommand('Delete', function() {
24275				customDelete();
24276			});
24277
24278			editor.addCommand('ForwardDelete', function() {
24279				customDelete(true);
24280			});
24281
24282			editor.on('dragstart', function(e) {
24283				e.dataTransfer.setData('mce-internal', editor.selection.getContent());
24284			});
24285
24286			editor.on('drop', function(e) {
24287				if (!isDefaultPrevented(e)) {
24288					var internalContent = e.dataTransfer.getData('mce-internal');
24289
24290					if (internalContent && doc.caretRangeFromPoint) {
24291						e.preventDefault();
24292						customDelete();
24293						editor.selection.setRng(doc.caretRangeFromPoint(e.x, e.y));
24294						editor.insertContent(internalContent);
24295					}
24296				}
24297			});
24298
24299			editor.on('cut', function(e) {
24300				if (!isDefaultPrevented(e) && e.clipboardData) {
24301					e.preventDefault();
24302					e.clipboardData.clearData();
24303					e.clipboardData.setData('text/html', editor.selection.getContent());
24304					e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
24305					customDelete(true);
24306				}
24307			});
24308		}
24309
24310		/**
24311		 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
24312		 *
24313		 * For example:
24314		 * <p><b>|</b></p>
24315		 *
24316		 * Or:
24317		 * <h1>|</h1>
24318		 *
24319		 * Or:
24320		 * [<h1></h1>]
24321		 */
24322		function emptyEditorWhenDeleting() {
24323			function serializeRng(rng) {
24324				var body = dom.create("body");
24325				var contents = rng.cloneContents();
24326				body.appendChild(contents);
24327				return selection.serializer.serialize(body, {format: 'html'});
24328			}
24329
24330			function allContentsSelected(rng) {
24331				if (!rng.setStart) {
24332					if (rng.item) {
24333						return false;
24334					}
24335
24336					var bodyRng = rng.duplicate();
24337					bodyRng.moveToElementText(editor.getBody());
24338					return RangeUtils.compareRanges(rng, bodyRng);
24339				}
24340
24341				var selection = serializeRng(rng);
24342
24343				var allRng = dom.createRng();
24344				allRng.selectNode(editor.getBody());
24345
24346				var allSelection = serializeRng(allRng);
24347				return selection === allSelection;
24348			}
24349
24350			editor.on('keydown', function(e) {
24351				var keyCode = e.keyCode, isCollapsed, body;
24352
24353				// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
24354				if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
24355					isCollapsed = editor.selection.isCollapsed();
24356					body = editor.getBody();
24357
24358					// Selection is collapsed but the editor isn't empty
24359					if (isCollapsed && !dom.isEmpty(body)) {
24360						return;
24361					}
24362
24363					// Selection isn't collapsed but not all the contents is selected
24364					if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
24365						return;
24366					}
24367
24368					// Manually empty the editor
24369					e.preventDefault();
24370					editor.setContent('');
24371
24372					if (body.firstChild && dom.isBlock(body.firstChild)) {
24373						editor.selection.setCursorLocation(body.firstChild, 0);
24374					} else {
24375						editor.selection.setCursorLocation(body, 0);
24376					}
24377
24378					editor.nodeChanged();
24379				}
24380			});
24381		}
24382
24383		/**
24384		 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
24385		 * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
24386		 * This selects the whole body so that backspace/delete logic will delete everything
24387		 */
24388		function selectAll() {
24389			editor.on('keydown', function(e) {
24390				if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
24391					e.preventDefault();
24392					editor.execCommand('SelectAll');
24393				}
24394			});
24395		}
24396
24397		/**
24398		 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
24399		 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
24400		 *
24401		 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
24402		 * you enter a character into the editor.
24403		 *
24404		 * It also happens when the first focus in made to the body.
24405		 *
24406		 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
24407		 */
24408		function inputMethodFocus() {
24409			if (!editor.settings.content_editable) {
24410				// Case 1 IME doesn't initialize if you focus the document
24411				dom.bind(editor.getDoc(), 'focusin', function() {
24412					selection.setRng(selection.getRng());
24413				});
24414
24415				// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
24416				dom.bind(editor.getDoc(), 'mousedown', function(e) {
24417					if (e.target == editor.getDoc().documentElement) {
24418						editor.getWin().focus();
24419						selection.setRng(selection.getRng());
24420					}
24421				});
24422			}
24423		}
24424
24425		/**
24426		 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
24427		 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
24428		 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
24429		 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
24430		 * browsers.
24431		 *
24432		 * It also fixes a bug on Firefox where it's impossible to delete HR elements.
24433		 */
24434		function removeHrOnBackspace() {
24435			editor.on('keydown', function(e) {
24436				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
24437					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
24438						var node = selection.getNode();
24439						var previousSibling = node.previousSibling;
24440
24441						if (node.nodeName == 'HR') {
24442							dom.remove(node);
24443							e.preventDefault();
24444							return;
24445						}
24446
24447						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
24448							dom.remove(previousSibling);
24449							e.preventDefault();
24450						}
24451					}
24452				}
24453			});
24454		}
24455
24456		/**
24457		 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
24458		 * side it's rectangle.
24459		 */
24460		function focusBody() {
24461			// Fix for a focus bug in FF 3.x where the body element
24462			// wouldn't get proper focus if the user clicked on the HTML element
24463			if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
24464				editor.on('mousedown', function(e) {
24465					if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
24466						var body = editor.getBody();
24467
24468						// Blur the body it's focused but not correctly focused
24469						body.blur();
24470
24471						// Refocus the body after a little while
24472						setTimeout(function() {
24473							body.focus();
24474						}, 0);
24475					}
24476				});
24477			}
24478		}
24479
24480		/**
24481		 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
24482		 * by clicking on them so we need to fake that.
24483		 */
24484		function selectControlElements() {
24485			editor.on('click', function(e) {
24486				e = e.target;
24487
24488				// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
24489				// WebKit can't even do simple things like selecting an image
24490				// Needs tobe the setBaseAndExtend or it will fail to select floated images
24491				if (/^(IMG|HR)$/.test(e.nodeName)) {
24492					selection.getSel().setBaseAndExtent(e, 0, e, 1);
24493				}
24494
24495				if (e.nodeName == 'A' && dom.hasClass(e, 'mce-item-anchor')) {
24496					selection.select(e);
24497				}
24498
24499				editor.nodeChanged();
24500			});
24501		}
24502
24503		/**
24504		 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
24505		 *
24506		 * Fixes do backspace/delete on this:
24507		 * <p>bla[ck</p><p style="color:red">r]ed</p>
24508		 *
24509		 * Would become:
24510		 * <p>bla|ed</p>
24511		 *
24512		 * Instead of:
24513		 * <p style="color:red">bla|ed</p>
24514		 */
24515		function removeStylesWhenDeletingAcrossBlockElements() {
24516			function getAttributeApplyFunction() {
24517				var template = dom.getAttribs(selection.getStart().cloneNode(false));
24518
24519				return function() {
24520					var target = selection.getStart();
24521
24522					if (target !== editor.getBody()) {
24523						dom.setAttrib(target, "style", null);
24524
24525						each(template, function(attr) {
24526							target.setAttributeNode(attr.cloneNode(true));
24527						});
24528					}
24529				};
24530			}
24531
24532			function isSelectionAcrossElements() {
24533				return !selection.isCollapsed() &&
24534					dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
24535			}
24536
24537			editor.on('keypress', function(e) {
24538				var applyAttributes;
24539
24540				if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
24541					applyAttributes = getAttributeApplyFunction();
24542					editor.getDoc().execCommand('delete', false, null);
24543					applyAttributes();
24544					e.preventDefault();
24545					return false;
24546				}
24547			});
24548
24549			dom.bind(editor.getDoc(), 'cut', function(e) {
24550				var applyAttributes;
24551
24552				if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
24553					applyAttributes = getAttributeApplyFunction();
24554
24555					setTimeout(function() {
24556						applyAttributes();
24557					}, 0);
24558				}
24559			});
24560		}
24561
24562		/**
24563		 * Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
24564		 * event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
24565		 */
24566		function selectionChangeNodeChanged() {
24567			var lastRng, selectionTimer;
24568
24569			editor.on('selectionchange', function() {
24570				if (selectionTimer) {
24571					clearTimeout(selectionTimer);
24572					selectionTimer = 0;
24573				}
24574
24575				selectionTimer = window.setTimeout(function() {
24576					var rng = selection.getRng();
24577
24578					// Compare the ranges to see if it was a real change or not
24579					if (!lastRng || !RangeUtils.compareRanges(rng, lastRng)) {
24580						editor.nodeChanged();
24581						lastRng = rng;
24582					}
24583				}, 50);
24584			});
24585		}
24586
24587		/**
24588		 * Screen readers on IE needs to have the role application set on the body.
24589		 */
24590		function ensureBodyHasRoleApplication() {
24591			document.body.setAttribute("role", "application");
24592		}
24593
24594		/**
24595		 * Backspacing into a table behaves differently depending upon browser type.
24596		 * Therefore, disable Backspace when cursor immediately follows a table.
24597		 */
24598		function disableBackspaceIntoATable() {
24599			editor.on('keydown', function(e) {
24600				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
24601					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
24602						var previousSibling = selection.getNode().previousSibling;
24603						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
24604							e.preventDefault();
24605							return false;
24606						}
24607					}
24608				}
24609			});
24610		}
24611
24612		/**
24613		 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
24614		 * logic adds a \n before the BR so that it will get rendered.
24615		 */
24616		function addNewLinesBeforeBrInPre() {
24617			// IE8+ rendering mode does the right thing with BR in PRE
24618			if (getDocumentMode() > 7) {
24619				return;
24620			}
24621
24622			 // Enable display: none in area and add a specific class that hides all BR elements in PRE to
24623			 // avoid the caret from getting stuck at the BR elements while pressing the right arrow key
24624			setEditorCommandState('RespectVisibilityInDesign', true);
24625			editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
24626			dom.addClass(editor.getBody(), 'mceHideBrInPre');
24627
24628			// Adds a \n before all BR elements in PRE to get them visual
24629			parser.addNodeFilter('pre', function(nodes) {
24630				var i = nodes.length, brNodes, j, brElm, sibling;
24631
24632				while (i--) {
24633					brNodes = nodes[i].getAll('br');
24634					j = brNodes.length;
24635					while (j--) {
24636						brElm = brNodes[j];
24637
24638						// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
24639						sibling = brElm.prev;
24640						if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
24641							sibling.value += '\n';
24642						} else {
24643							brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
24644						}
24645					}
24646				}
24647			});
24648
24649			// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
24650			serializer.addNodeFilter('pre', function(nodes) {
24651				var i = nodes.length, brNodes, j, brElm, sibling;
24652
24653				while (i--) {
24654					brNodes = nodes[i].getAll('br');
24655					j = brNodes.length;
24656					while (j--) {
24657						brElm = brNodes[j];
24658						sibling = brElm.prev;
24659						if (sibling && sibling.type == 3) {
24660							sibling.value = sibling.value.replace(/\r?\n$/, '');
24661						}
24662					}
24663				}
24664			});
24665		}
24666
24667		/**
24668		 * Moves style width/height to attribute width/height when the user resizes an image on IE.
24669		 */
24670		function removePreSerializedStylesWhenSelectingControls() {
24671			dom.bind(editor.getBody(), 'mouseup', function() {
24672				var value, node = selection.getNode();
24673
24674				// Moved styles to attributes on IMG eements
24675				if (node.nodeName == 'IMG') {
24676					// Convert style width to width attribute
24677					if ((value = dom.getStyle(node, 'width'))) {
24678						dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
24679						dom.setStyle(node, 'width', '');
24680					}
24681
24682					// Convert style height to height attribute
24683					if ((value = dom.getStyle(node, 'height'))) {
24684						dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
24685						dom.setStyle(node, 'height', '');
24686					}
24687				}
24688			});
24689		}
24690
24691		/**
24692		 * Removes a blockquote when backspace is pressed at the beginning of it.
24693		 *
24694		 * For example:
24695		 * <blockquote><p>|x</p></blockquote>
24696		 *
24697		 * Becomes:
24698		 * <p>|x</p>
24699		 */
24700		function removeBlockQuoteOnBackSpace() {
24701			// Add block quote deletion handler
24702			editor.on('keydown', function(e) {
24703				var rng, container, offset, root, parent;
24704
24705				if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
24706					return;
24707				}
24708
24709				rng = selection.getRng();
24710				container = rng.startContainer;
24711				offset = rng.startOffset;
24712				root = dom.getRoot();
24713				parent = container;
24714
24715				if (!rng.collapsed || offset !== 0) {
24716					return;
24717				}
24718
24719				while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
24720					parent = parent.parentNode;
24721				}
24722
24723				// Is the cursor at the beginning of a blockquote?
24724				if (parent.tagName === 'BLOCKQUOTE') {
24725					// Remove the blockquote
24726					editor.formatter.toggle('blockquote', null, parent);
24727
24728					// Move the caret to the beginning of container
24729					rng = dom.createRng();
24730					rng.setStart(container, 0);
24731					rng.setEnd(container, 0);
24732					selection.setRng(rng);
24733				}
24734			});
24735		}
24736
24737		/**
24738		 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
24739		 */
24740		function setGeckoEditingOptions() {
24741			function setOpts() {
24742				editor._refreshContentEditable();
24743
24744				setEditorCommandState("StyleWithCSS", false);
24745				setEditorCommandState("enableInlineTableEditing", false);
24746
24747				if (!settings.object_resizing) {
24748					setEditorCommandState("enableObjectResizing", false);
24749				}
24750			}
24751
24752			if (!settings.readonly) {
24753				editor.on('BeforeExecCommand MouseDown', setOpts);
24754			}
24755		}
24756
24757		/**
24758		 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
24759		 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
24760		 *
24761		 * For example this:
24762		 * <p><b><a href="#">x</a></b></p>
24763		 *
24764		 * Becomes this:
24765		 * <p><b><a href="#">x</a></b><br></p>
24766		 */
24767		function addBrAfterLastLinks() {
24768			function fixLinks() {
24769				each(dom.select('a'), function(node) {
24770					var parentNode = node.parentNode, root = dom.getRoot();
24771
24772					if (parentNode.lastChild === node) {
24773						while (parentNode && !dom.isBlock(parentNode)) {
24774							if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
24775								return;
24776							}
24777
24778							parentNode = parentNode.parentNode;
24779						}
24780
24781						dom.add(parentNode, 'br', {'data-mce-bogus': 1});
24782					}
24783				});
24784			}
24785
24786			editor.on('SetContent ExecCommand', function(e) {
24787				if (e.type == "setcontent" || e.command === 'mceInsertLink') {
24788					fixLinks();
24789				}
24790			});
24791		}
24792
24793		/**
24794		 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
24795		 * default we want to change that behavior.
24796		 */
24797		function setDefaultBlockType() {
24798			if (settings.forced_root_block) {
24799				editor.on('init', function() {
24800					setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
24801				});
24802			}
24803		}
24804
24805		/**
24806		 * Removes ghost selections from images/tables on Gecko.
24807		 */
24808		function removeGhostSelection() {
24809			editor.on('Undo Redo SetContent', function(e) {
24810				if (!e.initial) {
24811					editor.execCommand('mceRepaint');
24812				}
24813			});
24814		}
24815
24816		/**
24817		 * Deletes the selected image on IE instead of navigating to previous page.
24818		 */
24819		function deleteControlItemOnBackSpace() {
24820			editor.on('keydown', function(e) {
24821				var rng;
24822
24823				if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
24824					rng = editor.getDoc().selection.createRange();
24825					if (rng && rng.item) {
24826						e.preventDefault();
24827						editor.undoManager.beforeChange();
24828						dom.remove(rng.item(0));
24829						editor.undoManager.add();
24830					}
24831				}
24832			});
24833		}
24834
24835		/**
24836		 * IE10 doesn't properly render block elements with the right height until you add contents to them.
24837		 * This fixes that by adding a padding-right to all empty text block elements.
24838		 * See: https://connect.microsoft.com/IE/feedback/details/743881
24839		 */
24840		function renderEmptyBlocksFix() {
24841			var emptyBlocksCSS;
24842
24843			// IE10+
24844			if (getDocumentMode() >= 10) {
24845				emptyBlocksCSS = '';
24846				each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
24847					emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
24848				});
24849
24850				editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
24851			}
24852		}
24853
24854		/**
24855		 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
24856		 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
24857		 */
24858		function keepNoScriptContents() {
24859			if (getDocumentMode() < 9) {
24860				parser.addNodeFilter('noscript', function(nodes) {
24861					var i = nodes.length, node, textNode;
24862
24863					while (i--) {
24864						node = nodes[i];
24865						textNode = node.firstChild;
24866
24867						if (textNode) {
24868							node.attr('data-mce-innertext', textNode.value);
24869						}
24870					}
24871				});
24872
24873				serializer.addNodeFilter('noscript', function(nodes) {
24874					var i = nodes.length, node, textNode, value;
24875
24876					while (i--) {
24877						node = nodes[i];
24878						textNode = nodes[i].firstChild;
24879
24880						if (textNode) {
24881							textNode.value = Entities.decode(textNode.value);
24882						} else {
24883							// Old IE can't retain noscript value so an attribute is used to store it
24884							value = node.attributes.map['data-mce-innertext'];
24885							if (value) {
24886								node.attr('data-mce-innertext', null);
24887								textNode = new Node('#text', 3);
24888								textNode.value = value;
24889								textNode.raw = true;
24890								node.append(textNode);
24891							}
24892						}
24893					}
24894				});
24895			}
24896		}
24897
24898		/**
24899		 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
24900		 */
24901		function fixCaretSelectionOfDocumentElementOnIe() {
24902			var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
24903
24904			// Return range from point or null if it failed
24905			function rngFromPoint(x, y) {
24906				var rng = body.createTextRange();
24907
24908				try {
24909					rng.moveToPoint(x, y);
24910				} catch (ex) {
24911					// IE sometimes throws and exception, so lets just ignore it
24912					rng = null;
24913				}
24914
24915				return rng;
24916			}
24917
24918			// Fires while the selection is changing
24919			function selectionChange(e) {
24920				var pointRng;
24921
24922				// Check if the button is down or not
24923				if (e.button) {
24924					// Create range from mouse position
24925					pointRng = rngFromPoint(e.x, e.y);
24926
24927					if (pointRng) {
24928						// Check if pointRange is before/after selection then change the endPoint
24929						if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
24930							pointRng.setEndPoint('StartToStart', startRng);
24931						} else {
24932							pointRng.setEndPoint('EndToEnd', startRng);
24933						}
24934
24935						pointRng.select();
24936					}
24937				} else {
24938					endSelection();
24939				}
24940			}
24941
24942			// Removes listeners
24943			function endSelection() {
24944				var rng = doc.selection.createRange();
24945
24946				// If the range is collapsed then use the last start range
24947				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
24948					startRng.select();
24949				}
24950
24951				dom.unbind(doc, 'mouseup', endSelection);
24952				dom.unbind(doc, 'mousemove', selectionChange);
24953				startRng = started = 0;
24954			}
24955
24956			// Make HTML element unselectable since we are going to handle selection by hand
24957			doc.documentElement.unselectable = true;
24958
24959			// Detect when user selects outside BODY
24960			dom.bind(doc, 'mousedown contextmenu', function(e) {
24961				if (e.target.nodeName === 'HTML') {
24962					if (started) {
24963						endSelection();
24964					}
24965
24966					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
24967					htmlElm = doc.documentElement;
24968					if (htmlElm.scrollHeight > htmlElm.clientHeight) {
24969						return;
24970					}
24971
24972					started = 1;
24973					// Setup start position
24974					startRng = rngFromPoint(e.x, e.y);
24975					if (startRng) {
24976						// Listen for selection change events
24977						dom.bind(doc, 'mouseup', endSelection);
24978						dom.bind(doc, 'mousemove', selectionChange);
24979
24980						dom.win.focus();
24981						startRng.select();
24982					}
24983				}
24984			});
24985		}
24986
24987		/**
24988		 * Fixes selection issues on Gecko where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
24989		 * this fix will lean the caret right into the closest inline element.
24990		 */
24991		function normalizeSelection() {
24992			// 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
24993			editor.on('keyup focusin', function(e) {
24994				if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
24995					selection.normalize();
24996				}
24997			});
24998		}
24999
25000		/**
25001		 * Forces Gecko to render a broken image icon if it fails to load an image.
25002		 */
25003		function showBrokenImageIcon() {
25004			editor.contentStyles.push(
25005				'img:-moz-broken {' +
25006					'-moz-force-broken-image-icon:1;' +
25007					'min-width:24px;' +
25008					'min-height:24px' +
25009				'}'
25010			);
25011		}
25012
25013		/**
25014		 * iOS has a bug where it's impossible to type if the document has a touchstart event
25015		 * bound and the user touches the document while having the on screen keyboard visible.
25016		 *
25017		 * The touch event moves the focus to the parent document while having the caret inside the iframe
25018		 * this fix moves the focus back into the iframe document.
25019		 */
25020		function restoreFocusOnKeyDown() {
25021			if (!editor.inline) {
25022				editor.on('keydown', function() {
25023					if (document.activeElement == document.body) {
25024						editor.getWin().focus();
25025					}
25026				});
25027			}
25028		}
25029
25030		/**
25031		 * IE 11 has an annoying issue where you can't move focus into the editor
25032		 * by clicking on the white area HTML element. We used to be able to to fix this with
25033		 * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
25034		 * object it's not possible anymore. So we need to hack in a ungly CSS to force the
25035		 * body to be at least 150px. If the user clicks the HTML element out side this 150px region
25036		 * we simply move the focus into the first paragraph. Not ideal since you loose the
25037		 * positioning of the caret but goot enough for most cases.
25038		 */
25039		function bodyHeight() {
25040			if (!editor.inline) {
25041				editor.contentStyles.push('body {min-height: 150px}');
25042				editor.on('click', function(e) {
25043					if (e.target.nodeName == 'HTML') {
25044						editor.execCommand('SelectAll');
25045						editor.selection.collapse(true);
25046						editor.nodeChanged();
25047					}
25048				});
25049			}
25050		}
25051
25052		/**
25053		 * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
25054		 * You might then loose all your work so we need to block that behavior and replace it with our own.
25055		 */
25056		function blockCmdArrowNavigation() {
25057			if (Env.mac) {
25058				editor.on('keydown', function(e) {
25059					if (VK.metaKeyPressed(e) && (e.keyCode == 37 || e.keyCode == 39)) {
25060						e.preventDefault();
25061						editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'word');
25062					}
25063				});
25064			}
25065		}
25066
25067		// All browsers
25068		disableBackspaceIntoATable();
25069		removeBlockQuoteOnBackSpace();
25070		emptyEditorWhenDeleting();
25071		normalizeSelection();
25072
25073		// WebKit
25074		if (isWebKit) {
25075			cleanupStylesWhenDeleting();
25076			inputMethodFocus();
25077			selectControlElements();
25078			setDefaultBlockType();
25079
25080			// iOS
25081			if (Env.iOS) {
25082				selectionChangeNodeChanged();
25083				restoreFocusOnKeyDown();
25084				bodyHeight();
25085			} else {
25086				selectAll();
25087			}
25088		}
25089
25090		// IE
25091		if (isIE && Env.ie < 11) {
25092			removeHrOnBackspace();
25093			ensureBodyHasRoleApplication();
25094			addNewLinesBeforeBrInPre();
25095			removePreSerializedStylesWhenSelectingControls();
25096			deleteControlItemOnBackSpace();
25097			renderEmptyBlocksFix();
25098			keepNoScriptContents();
25099			fixCaretSelectionOfDocumentElementOnIe();
25100		}
25101
25102		if (Env.ie >= 11) {
25103			bodyHeight();
25104		}
25105
25106		if (Env.ie) {
25107			selectAll();
25108		}
25109
25110		// Gecko
25111		if (isGecko) {
25112			removeHrOnBackspace();
25113			focusBody();
25114			removeStylesWhenDeletingAcrossBlockElements();
25115			setGeckoEditingOptions();
25116			addBrAfterLastLinks();
25117			removeGhostSelection();
25118			showBrokenImageIcon();
25119			blockCmdArrowNavigation();
25120		}
25121	};
25122});
25123
25124// Included from: js/tinymce/classes/util/Observable.js
25125
25126/**
25127 * Observable.js
25128 *
25129 * Copyright, Moxiecode Systems AB
25130 * Released under LGPL License.
25131 *
25132 * License: http://www.tinymce.com/license
25133 * Contributing: http://www.tinymce.com/contributing
25134 */
25135
25136/**
25137 * This mixin will add event binding logic to classes.
25138 *
25139 * @mixin tinymce.util.Observable
25140 */
25141define("tinymce/util/Observable", [
25142	"tinymce/util/Tools"
25143], function(Tools) {
25144	var bindingsName = "__bindings";
25145	var nativeEvents = Tools.makeMap(
25146		"focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange" +
25147		" mouseout mouseenter mouseleave keydown keypress keyup contextmenu dragstart dragend dragover draggesture dragdrop drop drag", ' '
25148	);
25149
25150	function returnFalse() {
25151		return false;
25152	}
25153
25154	function returnTrue() {
25155		return true;
25156	}
25157
25158	return {
25159		/**
25160		 * Fires the specified event by name.
25161		 *
25162		 * @method fire
25163		 * @param {String} name Name of the event to fire.
25164		 * @param {tinymce.Event/Object?} args Event arguments.
25165		 * @param {Boolean?} bubble True/false if the event is to be bubbled.
25166		 * @return {tinymce.Event} Event instance passed in converted into tinymce.Event instance.
25167		 * @example
25168		 * instance.fire('event', {...});
25169		 */
25170		fire: function(name, args, bubble) {
25171			var self = this, handlers, i, l, callback, parent;
25172
25173			name = name.toLowerCase();
25174			args = args || {};
25175			args.type = name;
25176
25177			// Setup target is there isn't one
25178			if (!args.target) {
25179				args.target = self;
25180			}
25181
25182			// Add event delegation methods if they are missing
25183			if (!args.preventDefault) {
25184				// Add preventDefault method
25185				args.preventDefault = function() {
25186					args.isDefaultPrevented = returnTrue;
25187				};
25188
25189				// Add stopPropagation
25190				args.stopPropagation = function() {
25191					args.isPropagationStopped = returnTrue;
25192				};
25193
25194				// Add stopImmediatePropagation
25195				args.stopImmediatePropagation = function() {
25196					args.isImmediatePropagationStopped = returnTrue;
25197				};
25198
25199				// Add event delegation states
25200				args.isDefaultPrevented = returnFalse;
25201				args.isPropagationStopped = returnFalse;
25202				args.isImmediatePropagationStopped = returnFalse;
25203			}
25204
25205			//console.log(name, args);
25206
25207			if (self[bindingsName]) {
25208				handlers = self[bindingsName][name];
25209
25210				if (handlers) {
25211					for (i = 0, l = handlers.length; i < l; i++) {
25212						handlers[i] = callback = handlers[i];
25213
25214						// Stop immediate propagation if needed
25215						if (args.isImmediatePropagationStopped()) {
25216							break;
25217						}
25218
25219						// If callback returns false then prevent default and stop all propagation
25220						if (callback.call(self, args) === false) {
25221							args.preventDefault();
25222							return args;
25223						}
25224					}
25225				}
25226			}
25227
25228			// Bubble event up to parents
25229			if (bubble !== false && self.parent) {
25230				parent = self.parent();
25231				while (parent && !args.isPropagationStopped()) {
25232					parent.fire(name, args, false);
25233					parent = parent.parent();
25234				}
25235			}
25236
25237			return args;
25238		},
25239
25240		/**
25241		 * Binds an event listener to a specific event by name.
25242		 *
25243		 * @method on
25244		 * @param {String} name Event name or space separated list of events to bind.
25245		 * @param {callback} callback Callback to be executed when the event occurs.
25246		 * @return {Object} Current class instance.
25247		 * @example
25248		 * instance.on('event', function(e) {
25249		 *     // Callback logic
25250		 * });
25251		 */
25252		on: function(name, callback) {
25253			var self = this, bindings, handlers, names, i;
25254
25255			if (callback === false) {
25256				callback = function() {
25257					return false;
25258				};
25259			}
25260
25261			if (callback) {
25262				names = name.toLowerCase().split(' ');
25263				i = names.length;
25264				while (i--) {
25265					name = names[i];
25266
25267					bindings = self[bindingsName];
25268					if (!bindings) {
25269						bindings = self[bindingsName] = {};
25270					}
25271
25272					handlers = bindings[name];
25273					if (!handlers) {
25274						handlers = bindings[name] = [];
25275						if (self.bindNative && nativeEvents[name]) {
25276							self.bindNative(name);
25277						}
25278					}
25279
25280					handlers.push(callback);
25281				}
25282			}
25283
25284			return self;
25285		},
25286
25287		/**
25288		 * Unbinds an event listener to a specific event by name.
25289		 *
25290		 * @method off
25291		 * @param {String?} name Name of the event to unbind.
25292		 * @param {callback?} callback Callback to unbind.
25293		 * @return {Object} Current class instance.
25294		 * @example
25295		 * // Unbind specific callback
25296		 * instance.off('event', handler);
25297		 *
25298		 * // Unbind all listeners by name
25299		 * instance.off('event');
25300		 *
25301		 * // Unbind all events
25302		 * instance.off();
25303		 */
25304		off: function(name, callback) {
25305			var self = this, i, bindings = self[bindingsName], handlers, bindingName, names, hi;
25306
25307			if (bindings) {
25308				if (name) {
25309					names = name.toLowerCase().split(' ');
25310					i = names.length;
25311					while (i--) {
25312						name = names[i];
25313						handlers = bindings[name];
25314
25315						// Unbind all handlers
25316						if (!name) {
25317							for (bindingName in bindings) {
25318								bindings[name].length = 0;
25319							}
25320
25321							return self;
25322						}
25323
25324						if (handlers) {
25325							// Unbind all by name
25326							if (!callback) {
25327								handlers.length = 0;
25328							} else {
25329								// Unbind specific ones
25330								hi = handlers.length;
25331								while (hi--) {
25332									if (handlers[hi] === callback) {
25333										handlers.splice(hi, 1);
25334									}
25335								}
25336							}
25337
25338							if (!handlers.length && self.unbindNative && nativeEvents[name]) {
25339								self.unbindNative(name);
25340								delete bindings[name];
25341							}
25342						}
25343					}
25344				} else {
25345					if (self.unbindNative) {
25346						for (name in bindings) {
25347							self.unbindNative(name);
25348						}
25349					}
25350
25351					self[bindingsName] = [];
25352				}
25353			}
25354
25355			return self;
25356		},
25357
25358		hasEventListeners: function(name) {
25359			var bindings = this[bindingsName];
25360
25361			name = name.toLowerCase();
25362
25363			return !(!bindings || !bindings[name] || bindings[name].length === 0);
25364		}
25365	};
25366});
25367
25368// Included from: js/tinymce/classes/Shortcuts.js
25369
25370/**
25371 * Shortcuts.js
25372 *
25373 * Copyright, Moxiecode Systems AB
25374 * Released under LGPL License.
25375 *
25376 * License: http://www.tinymce.com/license
25377 * Contributing: http://www.tinymce.com/contributing
25378 */
25379
25380/**
25381 * Contains all logic for handling of keyboard shortcuts.
25382 */
25383define("tinymce/Shortcuts", [
25384	"tinymce/util/Tools",
25385	"tinymce/Env"
25386], function(Tools, Env) {
25387	var each = Tools.each, explode = Tools.explode;
25388
25389	var keyCodeLookup = {
25390		"f9": 120,
25391		"f10": 121,
25392		"f11": 122
25393	};
25394
25395	return function(editor) {
25396		var self = this, shortcuts = {};
25397
25398		editor.on('keyup keypress keydown', function(e) {
25399			if (e.altKey || e.ctrlKey || e.metaKey) {
25400				each(shortcuts, function(shortcut) {
25401					var ctrlKey = Env.mac ? e.metaKey : e.ctrlKey;
25402
25403					if (shortcut.ctrl != ctrlKey || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
25404						return;
25405					}
25406
25407					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
25408						e.preventDefault();
25409
25410						if (e.type == "keydown") {
25411							shortcut.func.call(shortcut.scope);
25412						}
25413
25414						return true;
25415					}
25416				});
25417			}
25418		});
25419
25420		/**
25421		 * Adds a keyboard shortcut for some command or function.
25422		 *
25423		 * @method addShortcut
25424		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
25425		 * @param {String} desc Text description for the command.
25426		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
25427		 * @param {Object} sc Optional scope to execute the function in.
25428		 * @return {Boolean} true/false state if the shortcut was added or not.
25429		 */
25430		self.add = function(pattern, desc, cmdFunc, scope) {
25431			var cmd;
25432
25433			cmd = cmdFunc;
25434
25435			if (typeof(cmdFunc) === 'string') {
25436				cmdFunc = function() {
25437					editor.execCommand(cmd, false, null);
25438				};
25439			} else if (Tools.isArray(cmd)) {
25440				cmdFunc = function() {
25441					editor.execCommand(cmd[0], cmd[1], cmd[2]);
25442				};
25443			}
25444
25445			each(explode(pattern.toLowerCase()), function(pattern) {
25446				var shortcut = {
25447					func: cmdFunc,
25448					scope: scope || editor,
25449					desc: editor.translate(desc),
25450					alt: false,
25451					ctrl: false,
25452					shift: false
25453				};
25454
25455				each(explode(pattern, '+'), function(value) {
25456					switch (value) {
25457						case 'alt':
25458						case 'ctrl':
25459						case 'shift':
25460							shortcut[value] = true;
25461							break;
25462
25463						default:
25464							shortcut.charCode = value.charCodeAt(0);
25465							shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
25466					}
25467				});
25468
25469				shortcuts[
25470					(shortcut.ctrl ? 'ctrl' : '') + ',' +
25471					(shortcut.alt ? 'alt' : '') + ',' +
25472					(shortcut.shift ? 'shift' : '') + ',' +
25473					shortcut.keyCode
25474				] = shortcut;
25475			});
25476
25477			return true;
25478		};
25479	};
25480});
25481
25482// Included from: js/tinymce/classes/Editor.js
25483
25484/**
25485 * Editor.js
25486 *
25487 * Copyright, Moxiecode Systems AB
25488 * Released under LGPL License.
25489 *
25490 * License: http://www.tinymce.com/license
25491 * Contributing: http://www.tinymce.com/contributing
25492 */
25493
25494/*jshint scripturl:true */
25495
25496/**
25497 * Include the base event class documentation.
25498 *
25499 * @include ../../../tools/docs/tinymce.Event.js
25500 */
25501
25502/**
25503 * This class contains the core logic for a TinyMCE editor.
25504 *
25505 * @class tinymce.Editor
25506 * @mixes tinymce.util.Observable
25507 * @example
25508 * // Add a class to all paragraphs in the editor.
25509 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
25510 *
25511 * // Gets the current editors selection as text
25512 * tinymce.activeEditor.selection.getContent({format: 'text'});
25513 *
25514 * // Creates a new editor instance
25515 * var ed = new tinymce.Editor('textareaid', {
25516 *     some_setting: 1
25517 * }, tinymce.EditorManager);
25518 *
25519 * // Select each item the user clicks on
25520 * ed.on('click', function(e) {
25521 *     ed.selection.select(e.target);
25522 * });
25523 *
25524 * ed.render();
25525 */
25526define("tinymce/Editor", [
25527	"tinymce/dom/DOMUtils",
25528	"tinymce/AddOnManager",
25529	"tinymce/html/Node",
25530	"tinymce/dom/Serializer",
25531	"tinymce/html/Serializer",
25532	"tinymce/dom/Selection",
25533	"tinymce/Formatter",
25534	"tinymce/UndoManager",
25535	"tinymce/EnterKey",
25536	"tinymce/ForceBlocks",
25537	"tinymce/EditorCommands",
25538	"tinymce/util/URI",
25539	"tinymce/dom/ScriptLoader",
25540	"tinymce/dom/EventUtils",
25541	"tinymce/WindowManager",
25542	"tinymce/html/Schema",
25543	"tinymce/html/DomParser",
25544	"tinymce/util/Quirks",
25545	"tinymce/Env",
25546	"tinymce/util/Tools",
25547	"tinymce/util/Observable",
25548	"tinymce/Shortcuts"
25549], function(
25550	DOMUtils, AddOnManager, Node, DomSerializer, Serializer,
25551	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
25552	URI, ScriptLoader, EventUtils, WindowManager,
25553	Schema, DomParser, Quirks, Env, Tools, Observable, Shortcuts
25554) {
25555	// Shorten these names
25556	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
25557	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
25558	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
25559	var Event = EventUtils.Event;
25560	var isGecko = Env.gecko, ie = Env.ie;
25561
25562	function getEventTarget(editor, eventName) {
25563		if (eventName == 'selectionchange' || eventName == 'drop') {
25564			return editor.getDoc();
25565		}
25566
25567		// Need to bind mousedown/mouseup etc to document not body in iframe mode
25568		// Since the user might click on the HTML element not the BODY
25569		if (!editor.inline && /^mouse|click|contextmenu/.test(eventName)) {
25570			return editor.getDoc();
25571		}
25572
25573		return editor.getBody();
25574	}
25575
25576	/**
25577	 * Include documentation for all the events.
25578	 *
25579	 * @include ../../../tools/docs/tinymce.Editor.js
25580	 */
25581
25582	/**
25583	 * Constructs a editor instance by id.
25584	 *
25585	 * @constructor
25586	 * @method Editor
25587	 * @param {String} id Unique id for the editor.
25588	 * @param {Object} settings Settings for the editor.
25589	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
25590	 * @author Moxiecode
25591	 */
25592	function Editor(id, settings, editorManager) {
25593		var self = this, documentBaseUrl, baseUri;
25594
25595		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
25596		baseUri = editorManager.baseURI;
25597
25598		/**
25599		 * Name/value collection with editor settings.
25600		 *
25601		 * @property settings
25602		 * @type Object
25603		 * @example
25604		 * // Get the value of the theme setting
25605		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
25606		 */
25607		self.settings = settings = extend({
25608			id: id,
25609			theme: 'modern',
25610			delta_width: 0,
25611			delta_height: 0,
25612			popup_css: '',
25613			plugins: '',
25614			document_base_url: documentBaseUrl,
25615			add_form_submit_trigger: true,
25616			submit_patch: true,
25617			add_unload_trigger: true,
25618			convert_urls: true,
25619			relative_urls: true,
25620			remove_script_host: true,
25621			object_resizing: true,
25622			doctype: '<!DOCTYPE html>',
25623			visual: true,
25624			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
25625
25626			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
25627			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
25628			forced_root_block: 'p',
25629			hidden_input: true,
25630			padd_empty_editor: true,
25631			render_ui: true,
25632			indentation: '30px',
25633			inline_styles: true,
25634			convert_fonts_to_spans: true,
25635			indent: 'simple',
25636			indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,' +
25637				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
25638			indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,' +
25639				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
25640			validate: true,
25641			entity_encoding: 'named',
25642			url_converter: self.convertURL,
25643			url_converter_scope: self,
25644			ie7_compat: true
25645		}, settings);
25646
25647		AddOnManager.language = settings.language || 'en';
25648		AddOnManager.languageLoad = settings.language_load;
25649
25650		AddOnManager.baseURL = editorManager.baseURL;
25651
25652		/**
25653		 * Editor instance id, normally the same as the div/textarea that was replaced.
25654		 *
25655		 * @property id
25656		 * @type String
25657		 */
25658		self.id = settings.id = id;
25659
25660		/**
25661		 * State to force the editor to return false on a isDirty call.
25662		 *
25663		 * @property isNotDirty
25664		 * @type Boolean
25665		 * @example
25666		 * function ajaxSave() {
25667		 *     var ed = tinymce.get('elm1');
25668		 *
25669		 *     // Save contents using some XHR call
25670		 *     alert(ed.getContent());
25671		 *
25672		 *     ed.isNotDirty = true; // Force not dirty state
25673		 * }
25674		 */
25675		self.isNotDirty = true;
25676
25677		/**
25678		 * Name/Value object containting plugin instances.
25679		 *
25680		 * @property plugins
25681		 * @type Object
25682		 * @example
25683		 * // Execute a method inside a plugin directly
25684		 * tinymce.activeEditor.plugins.someplugin.someMethod();
25685		 */
25686		self.plugins = {};
25687
25688		/**
25689		 * URI object to document configured for the TinyMCE instance.
25690		 *
25691		 * @property documentBaseURI
25692		 * @type tinymce.util.URI
25693		 * @example
25694		 * // Get relative URL from the location of document_base_url
25695		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
25696		 *
25697		 * // Get absolute URL from the location of document_base_url
25698		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
25699		 */
25700		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
25701			base_uri: baseUri
25702		});
25703
25704		/**
25705		 * URI object to current document that holds the TinyMCE editor instance.
25706		 *
25707		 * @property baseURI
25708		 * @type tinymce.util.URI
25709		 * @example
25710		 * // Get relative URL from the location of the API
25711		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
25712		 *
25713		 * // Get absolute URL from the location of the API
25714		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
25715		 */
25716		self.baseURI = baseUri;
25717
25718		/**
25719		 * Array with CSS files to load into the iframe.
25720		 *
25721		 * @property contentCSS
25722		 * @type Array
25723		 */
25724		self.contentCSS = [];
25725
25726		/**
25727		 * Array of CSS styles to add to head of document when the editor loads.
25728		 *
25729		 * @property contentStyles
25730		 * @type Array
25731		 */
25732		self.contentStyles = [];
25733
25734		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
25735		self.shortcuts = new Shortcuts(self);
25736
25737		// Internal command handler objects
25738		self.execCommands = {};
25739		self.queryStateCommands = {};
25740		self.queryValueCommands = {};
25741		self.loadedCSS = {};
25742
25743		self.suffix = editorManager.suffix;
25744		self.editorManager = editorManager;
25745		self.inline = settings.inline;
25746
25747		// Call setup
25748		self.execCallback('setup', self);
25749		editorManager.fire('SetupEditor', self);
25750	}
25751
25752	Editor.prototype = {
25753		/**
25754		 * Renderes the editor/adds it to the page.
25755		 *
25756		 * @method render
25757		 */
25758		render: function() {
25759			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
25760
25761			function readyHandler() {
25762				DOM.unbind(window, 'ready', readyHandler);
25763				self.render();
25764			}
25765
25766			// Page is not loaded yet, wait for it
25767			if (!Event.domLoaded) {
25768				DOM.bind(window, 'ready', readyHandler);
25769				return;
25770			}
25771
25772			// Element not found, then skip initialization
25773			if (!self.getElement()) {
25774				return;
25775			}
25776
25777			// No editable support old iOS versions etc
25778			if (!Env.contentEditable) {
25779				return;
25780			}
25781
25782			// Hide target element early to prevent content flashing
25783			if (!settings.inline) {
25784				self.orgVisibility = self.getElement().style.visibility;
25785				self.getElement().style.visibility = 'hidden';
25786			} else {
25787				self.inline = true;
25788			}
25789
25790			var form = self.getElement().form || DOM.getParent(id, 'form');
25791			if (form) {
25792				self.formElement = form;
25793
25794				// Add hidden input for non input elements inside form elements
25795				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
25796					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
25797					self.hasHiddenInput = true;
25798				}
25799
25800				// Pass submit/reset from form to editor instance
25801				self.formEventDelegate = function(e) {
25802					self.fire(e.type, e);
25803				};
25804
25805				DOM.bind(form, 'submit reset', self.formEventDelegate);
25806
25807				// Reset contents in editor when the form is reset
25808				self.on('reset', function() {
25809					self.setContent(self.startContent, {format: 'raw'});
25810				});
25811
25812				// Check page uses id="submit" or name="submit" for it's submit button
25813				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
25814					form._mceOldSubmit = form.submit;
25815					form.submit = function() {
25816						self.editorManager.triggerSave();
25817						self.isNotDirty = true;
25818
25819						return form._mceOldSubmit(form);
25820					};
25821				}
25822			}
25823
25824			/**
25825			 * Window manager reference, use this to open new windows and dialogs.
25826			 *
25827			 * @property windowManager
25828			 * @type tinymce.WindowManager
25829			 * @example
25830			 * // Shows an alert message
25831			 * tinymce.activeEditor.windowManager.alert('Hello world!');
25832			 *
25833			 * // Opens a new dialog with the file.htm file and the size 320x240
25834			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
25835			 * tinymce.activeEditor.windowManager.open({
25836			 *    url: 'file.htm',
25837			 *    width: 320,
25838			 *    height: 240
25839			 * }, {
25840			 *    custom_param: 1
25841			 * });
25842			 */
25843			self.windowManager = new WindowManager(self);
25844
25845			if (settings.encoding == 'xml') {
25846				self.on('GetContent', function(e) {
25847					if (e.save) {
25848						e.content = DOM.encode(e.content);
25849					}
25850				});
25851			}
25852
25853			if (settings.add_form_submit_trigger) {
25854				self.on('submit', function() {
25855					if (self.initialized) {
25856						self.save();
25857					}
25858				});
25859			}
25860
25861			if (settings.add_unload_trigger) {
25862				self._beforeUnload = function() {
25863					if (self.initialized && !self.destroyed && !self.isHidden()) {
25864						self.save({format: 'raw', no_events: true, set_dirty: false});
25865					}
25866				};
25867
25868				self.editorManager.on('BeforeUnload', self._beforeUnload);
25869			}
25870
25871			// Load scripts
25872			function loadScripts() {
25873				var scriptLoader = ScriptLoader.ScriptLoader;
25874
25875				if (settings.language && settings.language != 'en') {
25876					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
25877				}
25878
25879				if (settings.language_url) {
25880					scriptLoader.add(settings.language_url);
25881				}
25882
25883				if (settings.theme && typeof settings.theme != "function" &&
25884					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
25885					var themeUrl = settings.theme_url;
25886
25887					if (themeUrl) {
25888						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
25889					} else {
25890						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
25891					}
25892
25893					ThemeManager.load(settings.theme, themeUrl);
25894				}
25895
25896				if (Tools.isArray(settings.plugins)) {
25897					settings.plugins = settings.plugins.join(' ');
25898				}
25899
25900				each(settings.external_plugins, function(url, name) {
25901					PluginManager.load(name, url);
25902					settings.plugins += ' ' + name;
25903				});
25904
25905				each(settings.plugins.split(/[ ,]/), function(plugin) {
25906					plugin = trim(plugin);
25907
25908					if (plugin && !PluginManager.urls[plugin]) {
25909						if (plugin.charAt(0) == '-') {
25910							plugin = plugin.substr(1, plugin.length);
25911
25912							var dependencies = PluginManager.dependencies(plugin);
25913
25914							each(dependencies, function(dep) {
25915								var defaultSettings = {
25916									prefix:'plugins/',
25917									resource: dep,
25918									suffix:'/plugin' + suffix + '.js?v=4.1.6.20161202' // DAJ
25919								};
25920
25921								dep = PluginManager.createUrl(defaultSettings, dep);
25922								PluginManager.load(dep.resource, dep);
25923							});
25924						} else {
25925							PluginManager.load(plugin, {
25926								prefix: 'plugins/',
25927								resource: plugin,
25928								suffix: '/plugin' + suffix + '.js?v=4.1.6.20161202' // DAJ
25929							});
25930						}
25931					}
25932				});
25933
25934				scriptLoader.loadQueue(function() {
25935					if (!self.removed) {
25936						self.init();
25937					}
25938				});
25939			}
25940
25941			loadScripts();
25942		},
25943
25944		/**
25945		 * Initializes the editor this will be called automatically when
25946		 * all plugins/themes and language packs are loaded by the rendered method.
25947		 * This method will setup the iframe and create the theme and plugin instances.
25948		 *
25949		 * @method init
25950		 */
25951		init: function() {
25952			var self = this, settings = self.settings, elm = self.getElement();
25953			var w, h, minHeight, n, o, url, bodyId, bodyClass, re, i, initializedPlugins = [];
25954
25955			self.rtl = this.editorManager.i18n.rtl;
25956			self.editorManager.add(self);
25957
25958			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
25959
25960			/**
25961			 * Reference to the theme instance that was used to generate the UI.
25962			 *
25963			 * @property theme
25964			 * @type tinymce.Theme
25965			 * @example
25966			 * // Executes a method on the theme directly
25967			 * tinymce.activeEditor.theme.someMethod();
25968			 */
25969			if (settings.theme) {
25970				if (typeof settings.theme != "function") {
25971					settings.theme = settings.theme.replace(/-/, '');
25972					o = ThemeManager.get(settings.theme);
25973					self.theme = new o(self, ThemeManager.urls[settings.theme]);
25974
25975					if (self.theme.init) {
25976						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''));
25977					}
25978				} else {
25979					self.theme = settings.theme;
25980				}
25981			}
25982
25983			function initPlugin(plugin) {
25984				var constr = PluginManager.get(plugin), url, pluginInstance;
25985
25986				url = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
25987				plugin = trim(plugin);
25988				if (constr && inArray(initializedPlugins, plugin) === -1) {
25989					each(PluginManager.dependencies(plugin), function(dep){
25990						initPlugin(dep);
25991					});
25992
25993					pluginInstance = new constr(self, url);
25994
25995					self.plugins[plugin] = pluginInstance;
25996
25997					if (pluginInstance.init) {
25998						pluginInstance.init(self, url);
25999						initializedPlugins.push(plugin);
26000					}
26001				}
26002			}
26003
26004			// Create all plugins
26005			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
26006
26007			// Measure box
26008			if (settings.render_ui && self.theme) {
26009				self.orgDisplay = elm.style.display;
26010
26011				if (typeof settings.theme != "function") {
26012					w = settings.width || elm.style.width || elm.offsetWidth;
26013					h = settings.height || elm.style.height || elm.offsetHeight;
26014					minHeight = settings.min_height || 100;
26015					re = /^[0-9\.]+(|px)$/i;
26016
26017					if (re.test('' + w)) {
26018						w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
26019					}
26020
26021					if (re.test('' + h)) {
26022						h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), minHeight);
26023					}
26024
26025					// Render UI
26026					o = self.theme.renderUI({
26027						targetNode: elm,
26028						width: w,
26029						height: h,
26030						deltaWidth: settings.delta_width,
26031						deltaHeight: settings.delta_height
26032					});
26033
26034					// Resize editor
26035					if (!settings.content_editable) {
26036						DOM.setStyles(o.sizeContainer || o.editorContainer, {
26037							wi2dth: w,
26038							// TODO: Fix this
26039							h2eight: h
26040						});
26041
26042						h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
26043						if (h < minHeight) {
26044							h = minHeight;
26045						}
26046					}
26047				} else {
26048					o = settings.theme(self, elm);
26049
26050					// Convert element type to id:s
26051					if (o.editorContainer.nodeType) {
26052						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
26053					}
26054
26055					// Convert element type to id:s
26056					if (o.iframeContainer.nodeType) {
26057						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
26058					}
26059
26060					// Use specified iframe height or the targets offsetHeight
26061					h = o.iframeHeight || elm.offsetHeight;
26062				}
26063
26064				self.editorContainer = o.editorContainer;
26065			}
26066
26067			// Load specified content CSS last
26068			if (settings.content_css) {
26069				each(explode(settings.content_css), function(u) {
26070					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
26071				});
26072			}
26073
26074			// Load specified content CSS last
26075			if (settings.content_style) {
26076				self.contentStyles.push(settings.content_style);
26077			}
26078
26079			// Content editable mode ends here
26080			if (settings.content_editable) {
26081				elm = n = o = null; // Fix IE leak
26082				return self.initContentBody();
26083			}
26084
26085			self.iframeHTML = settings.doctype + '<html><head>';
26086
26087			// We only need to override paths if we have to
26088			// IE has a bug where it remove site absolute urls to relative ones if this is specified
26089			if (settings.document_base_url != self.documentBaseUrl) {
26090				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
26091			}
26092
26093			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
26094			if (!Env.caretAfter && settings.ie7_compat) {
26095				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
26096			}
26097
26098			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
26099
26100			// Load the CSS by injecting them into the HTML this will reduce "flicker"
26101			for (i = 0; i < self.contentCSS.length; i++) {
26102				var cssUrl = self.contentCSS[i];
26103				self.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + cssUrl + '" />';
26104				self.loadedCSS[cssUrl] = true;
26105			}
26106
26107			bodyId = settings.body_id || 'tinymce';
26108			if (bodyId.indexOf('=') != -1) {
26109				bodyId = self.getParam('body_id', '', 'hash');
26110				bodyId = bodyId[self.id] || bodyId;
26111			}
26112
26113			bodyClass = settings.body_class || '';
26114			if (bodyClass.indexOf('=') != -1) {
26115				bodyClass = self.getParam('body_class', '', 'hash');
26116				bodyClass = bodyClass[self.id] || '';
26117			}
26118
26119			self.iframeHTML += '</head><body id="' + bodyId + '" class="mce-content-body ' + bodyClass + '" ' +
26120				'onload="window.parent.tinymce.get(\'' + self.id + '\').fire(\'load\');"><br></body></html>';
26121
26122			var domainRelaxUrl = 'javascript:(function(){'+
26123				'document.open();document.domain="' + document.domain + '";' +
26124				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
26125				'document.close();ed.initContentBody(true);})()';
26126
26127			// Domain relaxing is required since the user has messed around with document.domain
26128			if (document.domain != location.hostname) {
26129				url = domainRelaxUrl;
26130			}
26131
26132			// Create iframe
26133			// TODO: ACC add the appropriate description on this.
26134			n = DOM.add(o.iframeContainer, 'iframe', {
26135				id: self.id + "_ifr",
26136				src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
26137				frameBorder: '0',
26138				allowTransparency: "true",
26139				title: '',
26140                                        // MODIF TN :: Pas de Alt/Title sur le tinyMCE
26141                                        //self.editorManager.translate(
26142					//"Rich Text Area. Press ALT-F9 for menu. " +
26143					//"Press ALT-F10 for toolbar. Press ALT-0 for help"
26144                                        //),
26145				style: {
26146					width: '100%',
26147					height: h,
26148					display: 'block' // Important for Gecko to render the iframe correctly
26149				}
26150			});
26151
26152			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
26153			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
26154			if (ie) {
26155				try {
26156					self.getDoc();
26157				} catch (e) {
26158					n.src = url = domainRelaxUrl;
26159				}
26160			}
26161
26162			self.contentAreaContainer = o.iframeContainer;
26163
26164			if (o.editorContainer) {
26165				DOM.get(o.editorContainer).style.display = self.orgDisplay;
26166			}
26167
26168			DOM.get(self.id).style.display = 'none';
26169			DOM.setAttrib(self.id, 'aria-hidden', true);
26170
26171			if (!url) {
26172				self.initContentBody();
26173			}
26174
26175			elm = n = o = null; // Cleanup
26176		},
26177
26178		/**
26179		 * This method get called by the init method ones the iframe is loaded.
26180		 * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
26181		 *
26182		 * @method initContentBody
26183		 * @private
26184		 */
26185		initContentBody: function(skipWrite) {
26186			var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), body, contentCssText;
26187
26188			// Restore visibility on target element
26189			if (!settings.inline) {
26190				self.getElement().style.visibility = self.orgVisibility;
26191			}
26192
26193			// Setup iframe body
26194			if (!skipWrite && !settings.content_editable) {
26195				doc.open();
26196				doc.write(self.iframeHTML);
26197				doc.close();
26198			}
26199
26200			if (settings.content_editable) {
26201				self.on('remove', function() {
26202					var body = this.getBody();
26203
26204					DOM.removeClass(body, 'mce-content-body');
26205					DOM.removeClass(body, 'mce-edit-focus');
26206					DOM.setAttrib(body, 'tabIndex', null);
26207					DOM.setAttrib(body, 'contentEditable', null);
26208				});
26209
26210				DOM.addClass(targetElm, 'mce-content-body');
26211				targetElm.tabIndex = -1;
26212				self.contentDocument = doc = settings.content_document || document;
26213				self.contentWindow = settings.content_window || window;
26214				self.bodyElement = targetElm;
26215
26216				// Prevent leak in IE
26217				settings.content_document = settings.content_window = null;
26218
26219				// TODO: Fix this
26220				settings.root_name = targetElm.nodeName.toLowerCase();
26221			}
26222
26223			// It will not steal focus while setting contentEditable
26224			body = self.getBody();
26225			body.disabled = true;
26226
26227			if (!settings.readonly) {
26228				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
26229					body.style.position = 'relative';
26230				}
26231
26232				body.contentEditable = self.getParam('content_editable_state', true);
26233			}
26234
26235			body.disabled = false;
26236
26237			/**
26238			 * Schema instance, enables you to validate elements and it's children.
26239			 *
26240			 * @property schema
26241			 * @type tinymce.html.Schema
26242			 */
26243			self.schema = new Schema(settings);
26244
26245			/**
26246			 * DOM instance for the editor.
26247			 *
26248			 * @property dom
26249			 * @type tinymce.dom.DOMUtils
26250			 * @example
26251			 * // Adds a class to all paragraphs within the editor
26252			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
26253			 */
26254			self.dom = new DOMUtils(doc, {
26255				keep_values: true,
26256				url_converter: self.convertURL,
26257				url_converter_scope: self,
26258				hex_colors: settings.force_hex_style_colors,
26259				class_filter: settings.class_filter,
26260				update_styles: true,
26261				root_element: settings.content_editable ? self.id : null,
26262				collect: settings.content_editable,
26263				schema: self.schema,
26264				onSetAttrib: function(e) {
26265					self.fire('SetAttrib', e);
26266				}
26267			});
26268
26269			/**
26270			 * HTML parser will be used when contents is inserted into the editor.
26271			 *
26272			 * @property parser
26273			 * @type tinymce.html.DomParser
26274			 */
26275			self.parser = new DomParser(settings, self.schema);
26276
26277			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
26278			self.parser.addAttributeFilter('src,href,style', function(nodes, name) {
26279				var i = nodes.length, node, dom = self.dom, value, internalName;
26280
26281				while (i--) {
26282					node = nodes[i];
26283					value = node.attr(name);
26284					internalName = 'data-mce-' + name;
26285
26286					// Add internal attribute if we need to we don't on a refresh of the document
26287					if (!node.attributes.map[internalName]) {
26288						if (name === "style") {
26289							node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
26290						} else {
26291							node.attr(internalName, self.convertURL(value, name, node.name));
26292						}
26293					}
26294				}
26295			});
26296
26297			// Keep scripts from executing
26298			self.parser.addNodeFilter('script', function(nodes) {
26299				var i = nodes.length, node;
26300
26301				while (i--) {
26302					node = nodes[i];
26303					node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
26304				}
26305			});
26306
26307			self.parser.addNodeFilter('#cdata', function(nodes) {
26308				var i = nodes.length, node;
26309
26310				while (i--) {
26311					node = nodes[i];
26312					node.type = 8;
26313					node.name = '#comment';
26314					node.value = '[CDATA[' + node.value + ']]';
26315				}
26316			});
26317
26318			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
26319				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
26320
26321				while (i--) {
26322					node = nodes[i];
26323
26324					if (node.isEmpty(nonEmptyElements)) {
26325						node.empty().append(new Node('br', 1)).shortEnded = true;
26326					}
26327				}
26328			});
26329
26330			/**
26331			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
26332			 *
26333			 * @property serializer
26334			 * @type tinymce.dom.Serializer
26335			 * @example
26336			 * // Serializes the first paragraph in the editor into a string
26337			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
26338			 */
26339			self.serializer = new DomSerializer(settings, self);
26340
26341			/**
26342			 * Selection instance for the editor.
26343			 *
26344			 * @property selection
26345			 * @type tinymce.dom.Selection
26346			 * @example
26347			 * // Sets some contents to the current selection in the editor
26348			 * tinymce.activeEditor.selection.setContent('Some contents');
26349			 *
26350			 * // Gets the current selection
26351			 * alert(tinymce.activeEditor.selection.getContent());
26352			 *
26353			 * // Selects the first paragraph found
26354			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
26355			 */
26356			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
26357
26358			/**
26359			 * Formatter instance.
26360			 *
26361			 * @property formatter
26362			 * @type tinymce.Formatter
26363			 */
26364			self.formatter = new Formatter(self);
26365
26366			/**
26367			 * Undo manager instance, responsible for handling undo levels.
26368			 *
26369			 * @property undoManager
26370			 * @type tinymce.UndoManager
26371			 * @example
26372			 * // Undoes the last modification to the editor
26373			 * tinymce.activeEditor.undoManager.undo();
26374			 */
26375			self.undoManager = new UndoManager(self);
26376
26377			self.forceBlocks = new ForceBlocks(self);
26378			self.enterKey = new EnterKey(self);
26379			self.editorCommands = new EditorCommands(self);
26380
26381			self.fire('PreInit');
26382
26383			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
26384				doc.body.spellcheck = false; // Gecko
26385				DOM.setAttrib(body, "spellcheck", "false");
26386			}
26387
26388			self.fire('PostRender');
26389
26390			self.quirks = Quirks(self);
26391
26392			if (settings.directionality) {
26393				body.dir = settings.directionality;
26394			}
26395
26396			if (settings.nowrap) {
26397				body.style.whiteSpace = "nowrap";
26398			}
26399
26400			if (settings.protect) {
26401				self.on('BeforeSetContent', function(e) {
26402					each(settings.protect, function(pattern) {
26403						e.content = e.content.replace(pattern, function(str) {
26404							return '<!--mce:protected ' + escape(str) + '-->';
26405						});
26406					});
26407				});
26408			}
26409
26410			self.on('SetContent', function() {
26411				self.addVisual(self.getBody());
26412			});
26413
26414			// Remove empty contents
26415			if (settings.padd_empty_editor) {
26416				self.on('PostProcess', function(e) {
26417					e.content = e.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
26418				});
26419			}
26420
26421			self.load({initial: true, format: 'html'});
26422			self.startContent = self.getContent({format: 'raw'});
26423
26424			/**
26425			 * Is set to true after the editor instance has been initialized
26426			 *
26427			 * @property initialized
26428			 * @type Boolean
26429			 * @example
26430			 * function isEditorInitialized(editor) {
26431			 *     return editor && editor.initialized;
26432			 * }
26433			 */
26434			self.initialized = true;
26435
26436			each(self._pendingNativeEvents, function(name) {
26437				self.dom.bind(getEventTarget(self, name), name, function(e) {
26438					self.fire(e.type, e);
26439				});
26440			});
26441
26442			self.fire('init');
26443			self.focus(true);
26444			self.nodeChanged({initial: true});
26445			self.execCallback('init_instance_callback', self);
26446
26447			// Add editor specific CSS styles
26448			if (self.contentStyles.length > 0) {
26449				contentCssText = '';
26450
26451				each(self.contentStyles, function(style) {
26452					contentCssText += style + "\r\n";
26453				});
26454
26455				self.dom.addStyle(contentCssText);
26456			}
26457
26458			// Load specified content CSS last
26459			each(self.contentCSS, function(cssUrl) {
26460				if (!self.loadedCSS[cssUrl]) {
26461					self.dom.loadCSS(cssUrl);
26462					self.loadedCSS[cssUrl] = true;
26463				}
26464			});
26465
26466			// Handle auto focus
26467			if (settings.auto_focus) {
26468				setTimeout(function () {
26469					var ed = self.editorManager.get(settings.auto_focus);
26470
26471					ed.selection.select(ed.getBody(), 1);
26472					ed.selection.collapse(1);
26473					ed.getBody().focus();
26474					ed.getWin().focus();
26475				}, 100);
26476			}
26477
26478			// Clean up references for IE
26479			targetElm = doc = body = null;
26480		},
26481
26482		/**
26483		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
26484		 * it will also place DOM focus inside the editor.
26485		 *
26486		 * @method focus
26487		 * @param {Boolean} skip_focus Skip DOM focus. Just set is as the active editor.
26488		 */
26489		focus: function(skip_focus) {
26490			var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
26491			var controlElm, doc = self.getDoc(), body;
26492
26493			if (!skip_focus) {
26494				// Get selected control element
26495				rng = selection.getRng();
26496				if (rng.item) {
26497					controlElm = rng.item(0);
26498				}
26499
26500				self._refreshContentEditable();
26501
26502				// Focus the window iframe
26503				if (!contentEditable) {
26504					// WebKit needs this call to fire focusin event properly see #5948
26505					// But Opera pre Blink engine will produce an empty selection so skip Opera
26506					if (!Env.opera) {
26507						self.getBody().focus();
26508					}
26509
26510					self.getWin().focus();
26511				}
26512
26513				// Focus the body as well since it's contentEditable
26514				if (isGecko || contentEditable) {
26515					body = self.getBody();
26516
26517					// Check for setActive since it doesn't scroll to the element
26518					if (body.setActive && Env.ie < 11) {
26519						body.setActive();
26520					} else {
26521						body.focus();
26522					}
26523
26524					if (contentEditable) {
26525						selection.normalize();
26526					}
26527				}
26528
26529				// Restore selected control element
26530				// This is needed when for example an image is selected within a
26531				// layer a call to focus will then remove the control selection
26532				if (controlElm && controlElm.ownerDocument == doc) {
26533					rng = doc.body.createControlRange();
26534					rng.addElement(controlElm);
26535					rng.select();
26536				}
26537			}
26538
26539			if (self.editorManager.activeEditor != self) {
26540				if ((oed = self.editorManager.activeEditor)) {
26541					oed.fire('deactivate', {relatedTarget: self});
26542				}
26543
26544				self.fire('activate', {relatedTarget: oed});
26545			}
26546
26547			self.editorManager.activeEditor = self;
26548		},
26549
26550		/**
26551		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
26552		 * There new event model is a better way to add callback so this method might be removed in the future.
26553		 *
26554		 * @method execCallback
26555		 * @param {String} name Name of the callback to execute.
26556		 * @return {Object} Return value passed from callback function.
26557		 */
26558		execCallback: function(name) {
26559			var self = this, callback = self.settings[name], scope;
26560
26561			if (!callback) {
26562				return;
26563			}
26564
26565			// Look through lookup
26566			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
26567				callback = scope.func;
26568				scope = scope.scope;
26569			}
26570
26571			if (typeof(callback) === 'string') {
26572				scope = callback.replace(/\.\w+$/, '');
26573				scope = scope ? resolve(scope) : 0;
26574				callback = resolve(callback);
26575				self.callbackLookup = self.callbackLookup || {};
26576				self.callbackLookup[name] = {func: callback, scope: scope};
26577			}
26578
26579			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
26580		},
26581
26582		/**
26583		 * Translates the specified string by replacing variables with language pack items it will also check if there is
26584		 * a key mathcin the input.
26585		 *
26586		 * @method translate
26587		 * @param {String} text String to translate by the language pack data.
26588		 * @return {String} Translated string.
26589		 */
26590		translate: function(text) {
26591			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
26592
26593			if (!text) {
26594				return '';
26595			}
26596
26597			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
26598				return i18n.data[lang + '.' + b] || '{#' + b + '}';
26599			});
26600		},
26601
26602		/**
26603		 * Returns a language pack item by name/key.
26604		 *
26605		 * @method getLang
26606		 * @param {String} name Name/key to get from the language pack.
26607		 * @param {String} defaultVal Optional default value to retrive.
26608		 */
26609		getLang: function(name, defaultVal) {
26610			return (
26611				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
26612				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
26613			);
26614		},
26615
26616		/**
26617		 * Returns a configuration parameter by name.
26618		 *
26619		 * @method getParam
26620		 * @param {String} name Configruation parameter to retrive.
26621		 * @param {String} defaultVal Optional default value to return.
26622		 * @param {String} type Optional type parameter.
26623		 * @return {String} Configuration parameter value or default value.
26624		 * @example
26625		 * // Returns a specific config value from the currently active editor
26626		 * var someval = tinymce.activeEditor.getParam('myvalue');
26627		 *
26628		 * // Returns a specific config value from a specific editor instance by id
26629		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
26630		 */
26631		getParam: function(name, defaultVal, type) {
26632			var value = name in this.settings ? this.settings[name] : defaultVal, output;
26633
26634			if (type === 'hash') {
26635				output = {};
26636
26637				if (typeof(value) === 'string') {
26638					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
26639						value = value.split('=');
26640
26641						if (value.length > 1) {
26642							output[trim(value[0])] = trim(value[1]);
26643						} else {
26644							output[trim(value[0])] = trim(value);
26645						}
26646					});
26647				} else {
26648					output = value;
26649				}
26650
26651				return output;
26652			}
26653
26654			return value;
26655		},
26656
26657		/**
26658		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
26659		 * need to update the UI states or element path etc.
26660		 *
26661		 * @method nodeChanged
26662		 */
26663		nodeChanged: function() {
26664			var self = this, selection = self.selection, node, parents, root;
26665
26666			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
26667			if (self.initialized && !self.settings.disable_nodechange) {
26668				// Get start node
26669				root = self.getBody();
26670				node = selection.getStart() || root;
26671				node = ie && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
26672
26673				// Edge case for <p>|<img></p>
26674				if (node.nodeName == 'IMG' && selection.isCollapsed()) {
26675					node = node.parentNode;
26676				}
26677
26678				// Get parents and add them to object
26679				parents = [];
26680				self.dom.getParent(node, function(node) {
26681					if (node === root) {
26682						return true;
26683					}
26684
26685					parents.push(node);
26686				});
26687
26688				self.fire('NodeChange', {element: node, parents: parents});
26689			}
26690		},
26691
26692		/**
26693		 * Adds a button that later gets created by the ControlManager. This is a shorter and easier method
26694		 * of adding buttons without the need to deal with the ControlManager directly. But it's also less
26695		 * powerfull if you need more control use the ControlManagers factory methods instead.
26696		 *
26697		 * @method addButton
26698		 * @param {String} name Button name to add.
26699		 * @param {Object} settings Settings object with title, cmd etc.
26700		 * @example
26701		 * // Adds a custom button to the editor that inserts contents when clicked
26702		 * tinymce.init({
26703		 *    ...
26704		 *
26705		 *    toolbar: 'example'
26706		 *
26707		 *    setup: function(ed) {
26708		 *       ed.addButton('example', {
26709		 *          title: 'My title',
26710		 *          image: '../js/tinymce/plugins/example/img/example.gif',
26711		 *          onclick: function() {
26712		 *             ed.insertContent('Hello world!!');
26713		 *          }
26714		 *       });
26715		 *    }
26716		 * });
26717		 */
26718		addButton: function(name, settings) {
26719			var self = this;
26720
26721			if (settings.cmd) {
26722				settings.onclick = function() {
26723					self.execCommand(settings.cmd);
26724				};
26725			}
26726
26727			if (!settings.text && !settings.icon) {
26728				settings.icon = name;
26729			}
26730
26731			self.buttons = self.buttons || {};
26732			settings.tooltip = settings.tooltip || settings.title;
26733			self.buttons[name] = settings;
26734		},
26735
26736		/**
26737		 * Adds a menu item to be used in the menus of the modern theme.
26738		 *
26739		 * @method addMenuItem
26740		 * @param {String} name Menu item name to add.
26741		 * @param {Object} settings Settings object with title, cmd etc.
26742		 * @example
26743		 * // Adds a custom menu item to the editor that inserts contents when clicked
26744		 * // The context option allows you to add the menu item to an existing default menu
26745		 * tinymce.init({
26746		 *    ...
26747		 *
26748		 *    setup: function(ed) {
26749		 *       ed.addMenuItem('example', {
26750		 *          title: 'My menu item',
26751		 *          context: 'tools',
26752		 *          onclick: function() {
26753		 *             ed.insertContent('Hello world!!');
26754		 *          }
26755		 *       });
26756		 *    }
26757		 * });
26758		 */
26759		addMenuItem: function(name, settings) {
26760			var self = this;
26761
26762			if (settings.cmd) {
26763				settings.onclick = function() {
26764					self.execCommand(settings.cmd);
26765				};
26766			}
26767
26768			self.menuItems = self.menuItems || {};
26769			self.menuItems[name] = settings;
26770		},
26771
26772		/**
26773		 * Adds a custom command to the editor, you can also override existing commands with this method.
26774		 * The command that you add can be executed with execCommand.
26775		 *
26776		 * @method addCommand
26777		 * @param {String} name Command name to add/override.
26778		 * @param {addCommandCallback} callback Function to execute when the command occurs.
26779		 * @param {Object} scope Optional scope to execute the function in.
26780		 * @example
26781		 * // Adds a custom command that later can be executed using execCommand
26782		 * tinymce.init({
26783		 *    ...
26784		 *
26785		 *    setup: function(ed) {
26786		 *       // Register example command
26787		 *       ed.addCommand('mycommand', function(ui, v) {
26788		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
26789		 *       });
26790		 *    }
26791		 * });
26792		 */
26793		addCommand: function(name, callback, scope) {
26794			/**
26795			 * Callback function that gets called when a command is executed.
26796			 *
26797			 * @callback addCommandCallback
26798			 * @param {Boolean} ui Display UI state true/false.
26799			 * @param {Object} value Optional value for command.
26800			 * @return {Boolean} True/false state if the command was handled or not.
26801			 */
26802			this.execCommands[name] = {func: callback, scope: scope || this};
26803		},
26804
26805		/**
26806		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
26807		 * The command that you add can be executed with queryCommandState function.
26808		 *
26809		 * @method addQueryStateHandler
26810		 * @param {String} name Command name to add/override.
26811		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
26812		 * @param {Object} scope Optional scope to execute the function in.
26813		 */
26814		addQueryStateHandler: function(name, callback, scope) {
26815			/**
26816			 * Callback function that gets called when a queryCommandState is executed.
26817			 *
26818			 * @callback addQueryStateHandlerCallback
26819			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
26820			 */
26821			this.queryStateCommands[name] = {func: callback, scope: scope || this};
26822		},
26823
26824		/**
26825		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
26826		 * The command that you add can be executed with queryCommandValue function.
26827		 *
26828		 * @method addQueryValueHandler
26829		 * @param {String} name Command name to add/override.
26830		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
26831		 * @param {Object} scope Optional scope to execute the function in.
26832		 */
26833		addQueryValueHandler: function(name, callback, scope) {
26834			/**
26835			 * Callback function that gets called when a queryCommandValue is executed.
26836			 *
26837			 * @callback addQueryValueHandlerCallback
26838			 * @return {Object} Value of the command or undefined.
26839			 */
26840			this.queryValueCommands[name] = {func: callback, scope: scope || this};
26841		},
26842
26843		/**
26844		 * Adds a keyboard shortcut for some command or function.
26845		 *
26846		 * @method addShortcut
26847		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
26848		 * @param {String} desc Text description for the command.
26849		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
26850		 * @param {Object} sc Optional scope to execute the function in.
26851		 * @return {Boolean} true/false state if the shortcut was added or not.
26852		 */
26853		addShortcut: function(pattern, desc, cmdFunc, scope) {
26854			this.shortcuts.add(pattern, desc, cmdFunc, scope);
26855		},
26856
26857		/**
26858		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
26859		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
26860		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
26861		 * return true it will handle the command as a internal browser command.
26862		 *
26863		 * @method execCommand
26864		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
26865		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
26866		 * @param {mixed} value Optional command value, this can be anything.
26867		 * @param {Object} a Optional arguments object.
26868		 */
26869		execCommand: function(cmd, ui, value, args) {
26870			var self = this, state = 0, cmdItem;
26871
26872			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(cmd) && (!args || !args.skip_focus)) {
26873				self.focus();
26874			}
26875
26876			args = extend({}, args);
26877			args = self.fire('BeforeExecCommand', {command: cmd, ui: ui, value: value});
26878			if (args.isDefaultPrevented()) {
26879				return false;
26880			}
26881
26882			// Registred commands
26883			if ((cmdItem = self.execCommands[cmd])) {
26884				// Fall through on true
26885				if (cmdItem.func.call(cmdItem.scope, ui, value) !== true) {
26886					self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
26887					return true;
26888				}
26889			}
26890
26891			// Plugin commands
26892			each(self.plugins, function(p) {
26893				if (p.execCommand && p.execCommand(cmd, ui, value)) {
26894					self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
26895					state = true;
26896					return false;
26897				}
26898			});
26899
26900			if (state) {
26901				return state;
26902			}
26903
26904			// Theme commands
26905			if (self.theme && self.theme.execCommand && self.theme.execCommand(cmd, ui, value)) {
26906				self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
26907				return true;
26908			}
26909
26910			// Editor commands
26911			if (self.editorCommands.execCommand(cmd, ui, value)) {
26912				self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
26913				return true;
26914			}
26915
26916			// Browser commands
26917			self.getDoc().execCommand(cmd, ui, value);
26918			self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
26919		},
26920
26921		/**
26922		 * Returns a command specific state, for example if bold is enabled or not.
26923		 *
26924		 * @method queryCommandState
26925		 * @param {string} cmd Command to query state from.
26926		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
26927		 */
26928		queryCommandState: function(cmd) {
26929			var self = this, queryItem, returnVal;
26930
26931			// Is hidden then return undefined
26932			if (self._isHidden()) {
26933				return;
26934			}
26935
26936			// Registred commands
26937			if ((queryItem = self.queryStateCommands[cmd])) {
26938				returnVal = queryItem.func.call(queryItem.scope);
26939
26940				// Fall though on true
26941				if (returnVal !== true) {
26942					return returnVal;
26943				}
26944			}
26945
26946			// Editor commands
26947			returnVal = self.editorCommands.queryCommandState(cmd);
26948			if (returnVal !== -1) {
26949				return returnVal;
26950			}
26951
26952			// Browser commands
26953			try {
26954				return self.getDoc().queryCommandState(cmd);
26955			} catch (ex) {
26956				// Fails sometimes see bug: 1896577
26957			}
26958		},
26959
26960		/**
26961		 * Returns a command specific value, for example the current font size.
26962		 *
26963		 * @method queryCommandValue
26964		 * @param {string} cmd Command to query value from.
26965		 * @return {Object} Command specific value, for example the current font size.
26966		 */
26967		queryCommandValue: function(cmd) {
26968			var self = this, queryItem, returnVal;
26969
26970			// Is hidden then return undefined
26971			if (self._isHidden()) {
26972				return;
26973			}
26974
26975			// Registred commands
26976			if ((queryItem = self.queryValueCommands[cmd])) {
26977				returnVal = queryItem.func.call(queryItem.scope);
26978
26979				// Fall though on true
26980				if (returnVal !== true) {
26981					return returnVal;
26982				}
26983			}
26984
26985			// Editor commands
26986			returnVal = self.editorCommands.queryCommandValue(cmd);
26987			if (returnVal !== undefined) {
26988				return returnVal;
26989			}
26990
26991			// Browser commands
26992			try {
26993				return self.getDoc().queryCommandValue(cmd);
26994			} catch (ex) {
26995				// Fails sometimes see bug: 1896577
26996			}
26997		},
26998
26999		/**
27000		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
27001		 *
27002		 * @method show
27003		 */
27004		show: function() {
27005			var self = this;
27006
27007			DOM.show(self.getContainer());
27008			DOM.hide(self.id);
27009			self.load();
27010			self.fire('show');
27011		},
27012
27013		/**
27014		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
27015		 *
27016		 * @method hide
27017		 */
27018		hide: function() {
27019			var self = this, doc = self.getDoc();
27020
27021			// Fixed bug where IE has a blinking cursor left from the editor
27022			if (ie && doc && !self.inline) {
27023				doc.execCommand('SelectAll');
27024			}
27025
27026			// We must save before we hide so Safari doesn't crash
27027			self.save();
27028
27029			// defer the call to hide to prevent an IE9 crash #4921
27030			DOM.hide(self.getContainer());
27031			DOM.setStyle(self.id, 'display', self.orgDisplay);
27032			self.fire('hide');
27033		},
27034
27035		/**
27036		 * Returns true/false if the editor is hidden or not.
27037		 *
27038		 * @method isHidden
27039		 * @return {Boolean} True/false if the editor is hidden or not.
27040		 */
27041		isHidden: function() {
27042			return !DOM.isHidden(this.id);
27043		},
27044
27045		/**
27046		 * Sets the progress state, this will display a throbber/progess for the editor.
27047		 * This is ideal for asycronous operations like an AJAX save call.
27048		 *
27049		 * @method setProgressState
27050		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
27051		 * @param {Number} time Optional time to wait before the progress gets shown.
27052		 * @return {Boolean} Same as the input state.
27053		 * @example
27054		 * // Show progress for the active editor
27055		 * tinymce.activeEditor.setProgressState(true);
27056		 * 
27057		 * // Hide progress for the active editor
27058		 * tinymce.activeEditor.setProgressState(false);
27059		 * 
27060		 * // Show progress after 3 seconds
27061		 * tinymce.activeEditor.setProgressState(true, 3000);
27062		 */
27063		setProgressState: function(state, time) {
27064			this.fire('ProgressState', {state: state, time: time});
27065		},
27066
27067		/**
27068		 * Loads contents from the textarea or div element that got converted into an editor instance.
27069		 * This method will move the contents from that textarea or div into the editor by using setContent
27070		 * so all events etc that method has will get dispatched as well.
27071		 *
27072		 * @method load
27073		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
27074		 * @return {String} HTML string that got set into the editor.
27075		 */
27076		load: function(args) {
27077			var self = this, elm = self.getElement(), html;
27078
27079			if (elm) {
27080				args = args || {};
27081				args.load = true;
27082
27083				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
27084				args.element = elm;
27085
27086				if (!args.no_events) {
27087					self.fire('LoadContent', args);
27088				}
27089
27090				args.element = elm = null;
27091
27092				return html;
27093			}
27094		},
27095
27096		/**
27097		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
27098		 * This method will move the HTML contents from the editor into that textarea or div by getContent
27099		 * so all events etc that method has will get dispatched as well.
27100		 *
27101		 * @method save
27102		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
27103		 * @return {String} HTML string that got set into the textarea/div.
27104		 */
27105		save: function(args) {
27106			var self = this, elm = self.getElement(), html, form;
27107
27108			if (!elm || !self.initialized) {
27109				return;
27110			}
27111
27112			args = args || {};
27113			args.save = true;
27114
27115			args.element = elm;
27116			html = args.content = self.getContent(args);
27117
27118			if (!args.no_events) {
27119				self.fire('SaveContent', args);
27120			}
27121
27122			html = args.content;
27123
27124			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
27125				elm.innerHTML = html;
27126
27127				// Update hidden form element
27128				if ((form = DOM.getParent(self.id, 'form'))) {
27129					each(form.elements, function(elm) {
27130						if (elm.name == self.id) {
27131							elm.value = html;
27132							return false;
27133						}
27134					});
27135				}
27136			} else {
27137				elm.value = html;
27138			}
27139
27140			args.element = elm = null;
27141
27142			if (args.set_dirty !== false) {
27143				self.isNotDirty = true;
27144			}
27145
27146			return html;
27147		},
27148
27149		/**
27150		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
27151		 * the different cleanup rules options.
27152		 *
27153		 * @method setContent
27154		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
27155		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
27156		 * @return {String} HTML string that got set into the editor.
27157		 * @example
27158		 * // Sets the HTML contents of the activeEditor editor
27159		 * tinymce.activeEditor.setContent('<span>some</span> html');
27160		 *
27161		 * // Sets the raw contents of the activeEditor editor
27162		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
27163		 *
27164		 * // Sets the content of a specific editor (my_editor in this example)
27165		 * tinymce.get('my_editor').setContent(data);
27166		 *
27167		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
27168		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
27169		 */
27170		setContent: function(content, args) {
27171			var self = this, body = self.getBody(), forcedRootBlockName;
27172
27173			// Setup args object
27174			args = args || {};
27175			args.format = args.format || 'html';
27176			args.set = true;
27177			args.content = content;
27178
27179			// Do preprocessing
27180			if (!args.no_events) {
27181				self.fire('BeforeSetContent', args);
27182			}
27183
27184			content = args.content;
27185
27186			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
27187			// It will also be impossible to place the caret in the editor unless there is a BR element present
27188			if (content.length === 0 || /^\s+$/.test(content)) {
27189				forcedRootBlockName = self.settings.forced_root_block;
27190
27191				// Check if forcedRootBlock is configured and that the block is a valid child of the body
27192				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
27193					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
27194					content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
27195					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
27196				} else if (!ie || ie < 11) {
27197					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
27198					content = '<br data-mce-bogus="1">';
27199				}
27200
27201				body.innerHTML = content;
27202
27203				self.fire('SetContent', args);
27204			} else {
27205				// Parse and serialize the html
27206				if (args.format !== 'raw') {
27207					content = new Serializer({}, self.schema).serialize(
27208						self.parser.parse(content, {isRootContent: true})
27209					);
27210				}
27211
27212				// Set the new cleaned contents to the editor
27213				args.content = trim(content);
27214				self.dom.setHTML(body, args.content);
27215
27216				// Do post processing
27217				if (!args.no_events) {
27218					self.fire('SetContent', args);
27219				}
27220
27221				// Don't normalize selection if the focused element isn't the body in
27222				// content editable mode since it will steal focus otherwise
27223				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
27224					self.selection.normalize();
27225				}*/
27226			}
27227
27228			return args.content;
27229		},
27230
27231		/**
27232		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
27233		 * the different cleanup rules options.
27234		 *
27235		 * @method getContent
27236		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
27237		 * @return {String} Cleaned content string, normally HTML contents.
27238		 * @example
27239		 * // Get the HTML contents of the currently active editor
27240		 * console.debug(tinymce.activeEditor.getContent());
27241		 *
27242		 * // Get the raw contents of the currently active editor
27243		 * tinymce.activeEditor.getContent({format: 'raw'});
27244		 *
27245		 * // Get content of a specific editor:
27246		 * tinymce.get('content id').getContent()
27247		 */
27248		getContent: function(args) {
27249			var self = this, content, body = self.getBody();
27250
27251			// Setup args object
27252			args = args || {};
27253			args.format = args.format || 'html';
27254			args.get = true;
27255			args.getInner = true;
27256
27257			// Do preprocessing
27258			if (!args.no_events) {
27259				self.fire('BeforeGetContent', args);
27260			}
27261
27262			// Get raw contents or by default the cleaned contents
27263			if (args.format == 'raw') {
27264				content = body.innerHTML;
27265			} else if (args.format == 'text') {
27266				content = body.innerText || body.textContent;
27267			} else {
27268				content = self.serializer.serialize(body, args);
27269			}
27270
27271			// Trim whitespace in beginning/end of HTML
27272			if (args.format != 'text') {
27273				args.content = trim(content);
27274			} else {
27275				args.content = content;
27276			}
27277
27278			// Do post processing
27279			if (!args.no_events) {
27280				self.fire('GetContent', args);
27281			}
27282
27283			return args.content;
27284		},
27285
27286		/**
27287		 * Inserts content at caret position.
27288		 *
27289		 * @method insertContent
27290		 * @param {String} content Content to insert.
27291		 */
27292		insertContent: function(content) {
27293			this.execCommand('mceInsertContent', false, content);
27294		},
27295
27296		/**
27297		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
27298		 *
27299		 * @method isDirty
27300		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
27301		 * @example
27302		 * if (tinymce.activeEditor.isDirty())
27303		 *     alert("You must save your contents.");
27304		 */
27305		isDirty: function() {
27306			return !this.isNotDirty;
27307		},
27308
27309		/**
27310		 * Returns the editors container element. The container element wrappes in
27311		 * all the elements added to the page for the editor. Such as UI, iframe etc.
27312		 *
27313		 * @method getContainer
27314		 * @return {Element} HTML DOM element for the editor container.
27315		 */
27316		getContainer: function() {
27317			var self = this;
27318
27319			if (!self.container) {
27320				self.container = DOM.get(self.editorContainer || self.id + '_parent');
27321			}
27322
27323			return self.container;
27324		},
27325
27326		/**
27327		 * Returns the editors content area container element. The this element is the one who
27328		 * holds the iframe or the editable element.
27329		 *
27330		 * @method getContentAreaContainer
27331		 * @return {Element} HTML DOM element for the editor area container.
27332		 */
27333		getContentAreaContainer: function() {
27334			return this.contentAreaContainer;
27335		},
27336
27337		/**
27338		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
27339		 *
27340		 * @method getElement
27341		 * @return {Element} HTML DOM element for the replaced element.
27342		 */
27343		getElement: function() {
27344			return DOM.get(this.settings.content_element || this.id);
27345		},
27346
27347		/**
27348		 * Returns the iframes window object.
27349		 *
27350		 * @method getWin
27351		 * @return {Window} Iframe DOM window object.
27352		 */
27353		getWin: function() {
27354			var self = this, elm;
27355
27356			if (!self.contentWindow) {
27357				elm = DOM.get(self.id + "_ifr");
27358
27359				if (elm) {
27360					self.contentWindow = elm.contentWindow;
27361				}
27362			}
27363
27364			return self.contentWindow;
27365		},
27366
27367		/**
27368		 * Returns the iframes document object.
27369		 *
27370		 * @method getDoc
27371		 * @return {Document} Iframe DOM document object.
27372		 */
27373		getDoc: function() {
27374			var self = this, win;
27375
27376			if (!self.contentDocument) {
27377				win = self.getWin();
27378
27379				if (win) {
27380					self.contentDocument = win.document;
27381				}
27382			}
27383
27384			return self.contentDocument;
27385		},
27386
27387		/**
27388		 * Returns the iframes body element.
27389		 *
27390		 * @method getBody
27391		 * @return {Element} Iframe body element.
27392		 */
27393		getBody: function() {
27394			return this.bodyElement || this.getDoc().body;
27395		},
27396
27397		/**
27398		 * URL converter function this gets executed each time a user adds an img, a or
27399		 * any other element that has a URL in it. This will be called both by the DOM and HTML
27400		 * manipulation functions.
27401		 *
27402		 * @method convertURL
27403		 * @param {string} url URL to convert.
27404		 * @param {string} name Attribute name src, href etc.
27405		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
27406		 * @return {string} Converted URL string.
27407		 */
27408		convertURL: function(url, name, elm) {
27409			var self = this, settings = self.settings;
27410
27411			// Use callback instead
27412			if (settings.urlconverter_callback) {
27413				return self.execCallback('urlconverter_callback', url, elm, true, name);
27414			}
27415
27416			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
27417			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
27418				return url;
27419			}
27420
27421			// Convert to relative
27422			if (settings.relative_urls) {
27423				return self.documentBaseURI.toRelative(url);
27424			}
27425
27426			// Convert to absolute
27427			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
27428
27429			return url;
27430		},
27431
27432		/**
27433		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
27434		 *
27435		 * @method addVisual
27436		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
27437		 */
27438		addVisual: function(elm) {
27439			var self = this, settings = self.settings, dom = self.dom, cls;
27440
27441			elm = elm || self.getBody();
27442
27443			if (self.hasVisual === undefined) {
27444				self.hasVisual = settings.visual;
27445			}
27446
27447			each(dom.select('table,a', elm), function(elm) {
27448				var value;
27449
27450				switch (elm.nodeName) {
27451					case 'TABLE':
27452						cls = settings.visual_table_class || 'mce-item-table';
27453						value = dom.getAttrib(elm, 'border');
27454
27455						if (!value || value == '0') {
27456							if (self.hasVisual) {
27457								dom.addClass(elm, cls);
27458							} else {
27459								dom.removeClass(elm, cls);
27460							}
27461						}
27462
27463						return;
27464
27465					case 'A':
27466						if (!dom.getAttrib(elm, 'href', false)) {
27467							value = dom.getAttrib(elm, 'name') || elm.id;
27468							cls = 'mce-item-anchor';
27469
27470							if (value) {
27471								if (self.hasVisual) {
27472									dom.addClass(elm, cls);
27473								} else {
27474									dom.removeClass(elm, cls);
27475								}
27476							}
27477						}
27478
27479						return;
27480				}
27481			});
27482
27483			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
27484		},
27485
27486		/**
27487		 * Removes the editor from the dom and tinymce collection.
27488		 *
27489		 * @method remove
27490		 */
27491		remove: function() {
27492			var self = this;
27493
27494			if (!self.removed) {
27495				self.removed = 1; // Cancels post remove event execution
27496
27497				// Remove any hidden input
27498				if (self.hasHiddenInput) {
27499					DOM.remove(self.getElement().nextSibling);
27500				}
27501
27502				// Fixed bug where IE has a blinking cursor left from the editor
27503				var doc = self.getDoc();
27504				if (ie && doc && !self.inline) {
27505					doc.execCommand('SelectAll');
27506				}
27507
27508				// We must save before we hide so Safari doesn't crash
27509				self.save();
27510
27511				DOM.setStyle(self.id, 'display', self.orgDisplay);
27512
27513				// Don't clear the window or document if content editable
27514				// is enabled since other instances might still be present
27515				if (!self.settings.content_editable) {
27516					Event.unbind(self.getWin());
27517					Event.unbind(self.getDoc());
27518				}
27519
27520				var elm = self.getContainer();
27521				Event.unbind(self.getBody());
27522				Event.unbind(elm);
27523
27524				self.fire('remove');
27525
27526				self.editorManager.remove(self);
27527				DOM.remove(elm);
27528				self.destroy();
27529			}
27530		},
27531
27532		bindNative: function(name) {
27533			var self = this;
27534
27535			if (self.settings.readonly) {
27536				return;
27537			}
27538
27539			if (self.initialized) {
27540				self.dom.bind(getEventTarget(self, name), name, function(e) {
27541					self.fire(name, e);
27542				});
27543			} else {
27544				if (!self._pendingNativeEvents) {
27545					self._pendingNativeEvents = [name];
27546				} else {
27547					self._pendingNativeEvents.push(name);
27548				}
27549			}
27550		},
27551
27552		unbindNative: function(name) {
27553			var self = this;
27554
27555			if (self.initialized) {
27556				self.dom.unbind(name);
27557			}
27558		},
27559
27560		/**
27561		 * Destroys the editor instance by removing all events, element references or other resources
27562		 * that could leak memory. This method will be called automatically when the page is unloaded
27563		 * but you can also call it directly if you know what you are doing.
27564		 *
27565		 * @method destroy
27566		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
27567		 */
27568		destroy: function(automatic) {
27569			var self = this, form;
27570
27571			// One time is enough
27572			if (self.destroyed) {
27573				return;
27574			}
27575
27576			// If user manually calls destroy and not remove
27577			// Users seems to have logic that calls destroy instead of remove
27578			if (!automatic && !self.removed) {
27579				self.remove();
27580				return;
27581			}
27582
27583			// We must unbind on Gecko since it would otherwise produce the pesky "attempt
27584			// to run compile-and-go script on a cleared scope" message
27585			if (automatic && isGecko) {
27586				Event.unbind(self.getDoc());
27587				Event.unbind(self.getWin());
27588				Event.unbind(self.getBody());
27589			}
27590
27591			if (!automatic) {
27592				self.editorManager.off('beforeunload', self._beforeUnload);
27593
27594				// Manual destroy
27595				if (self.theme && self.theme.destroy) {
27596					self.theme.destroy();
27597				}
27598
27599				// Destroy controls, selection and dom
27600				self.selection.destroy();
27601				self.dom.destroy();
27602			}
27603
27604			form = self.formElement;
27605			if (form) {
27606				if (form._mceOldSubmit) {
27607					form.submit = form._mceOldSubmit;
27608					form._mceOldSubmit = null;
27609				}
27610
27611				DOM.unbind(form, 'submit reset', self.formEventDelegate);
27612			}
27613
27614			self.contentAreaContainer = self.formElement = self.container = null;
27615			self.settings.content_element = self.bodyElement = self.contentDocument = self.contentWindow = null;
27616
27617			if (self.selection) {
27618				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
27619			}
27620
27621			self.destroyed = 1;
27622		},
27623
27624		// Internal functions
27625
27626		_refreshContentEditable: function() {
27627			var self = this, body, parent;
27628
27629			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
27630			if (self._isHidden()) {
27631				body = self.getBody();
27632				parent = body.parentNode;
27633
27634				parent.removeChild(body);
27635				parent.appendChild(body);
27636
27637				body.focus();
27638			}
27639		},
27640
27641		_isHidden: function() {
27642			var sel;
27643
27644			if (!isGecko) {
27645				return 0;
27646			}
27647
27648			// Weird, wheres that cursor selection?
27649			sel = this.selection.getSel();
27650			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
27651		}
27652	};
27653
27654	extend(Editor.prototype, Observable);
27655
27656	return Editor;
27657});
27658
27659// Included from: js/tinymce/classes/util/I18n.js
27660
27661/**
27662 * I18n.js
27663 *
27664 * Copyright, Moxiecode Systems AB
27665 * Released under LGPL License.
27666 *
27667 * License: http://www.tinymce.com/license
27668 * Contributing: http://www.tinymce.com/contributing
27669 */
27670
27671/**
27672 * I18n class that handles translation of TinyMCE UI.
27673 * Uses po style with csharp style parameters.
27674 *
27675 * @class tinymce.util.I18n
27676 */
27677define("tinymce/util/I18n", [], function() {
27678	"use strict";
27679
27680	var data = {};
27681
27682	return {
27683		/**
27684		 * Property gets set to true if a RTL language pack was loaded.
27685		 *
27686		 * @property rtl
27687		 * @type {Boolean}
27688		 */
27689		rtl: false,
27690
27691		/**
27692		 * Adds translations for a specific language code.
27693		 *
27694		 * @method add
27695		 * @param {String} code Language code like sv_SE.
27696		 * @param {Array} items Name/value array with English en_US to sv_SE.
27697		 */
27698		add: function(code, items) {
27699			for (var name in items) {
27700				data[name] = items[name];
27701			}
27702
27703			this.rtl = this.rtl || data._dir === 'rtl';
27704		},
27705
27706		/**
27707		 * Translates the specified text.
27708		 *
27709		 * It has a few formats:
27710		 * I18n.translate("Text");
27711		 * I18n.translate(["Text {0}/{1}", 0, 1]);
27712		 * I18n.translate({raw: "Raw string"});
27713		 *
27714		 * @method translate
27715		 * @param {String/Object/Array} text Text to translate.
27716		 * @return {String} String that got translated.
27717		 */
27718		translate: function(text) {
27719			if (typeof(text) == "undefined") {
27720				return text;
27721			}
27722
27723			if (typeof(text) != "string" && text.raw) {
27724				return text.raw;
27725			}
27726
27727			if (text.push) {
27728				var values = text.slice(1);
27729
27730				text = (data[text[0]] || text[0]).replace(/\{([^\}]+)\}/g, function(match1, match2) {
27731					return values[match2];
27732				});
27733			}
27734
27735			return data[text] || text;
27736		},
27737
27738		data: data
27739	};
27740});
27741
27742// Included from: js/tinymce/classes/FocusManager.js
27743
27744/**
27745 * FocusManager.js
27746 *
27747 * Copyright, Moxiecode Systems AB
27748 * Released under LGPL License.
27749 *
27750 * License: http://www.tinymce.com/license
27751 * Contributing: http://www.tinymce.com/contributing
27752 */
27753
27754/**
27755 * This class manages the focus/blur state of the editor. This class is needed since some
27756 * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
27757 *
27758 * This class will fire two events focus and blur on the editor instances that got affected.
27759 * It will also handle the restore of selection when the focus is lost and returned.
27760 *
27761 * @class tinymce.FocusManager
27762 */
27763define("tinymce/FocusManager", [
27764	"tinymce/dom/DOMUtils",
27765	"tinymce/Env"
27766], function(DOMUtils, Env) {
27767	/**
27768	 * Constructs a new focus manager instance.
27769	 *
27770	 * @constructor FocusManager
27771	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
27772	 */
27773	function FocusManager(editorManager) {
27774		function getActiveElement() {
27775			try {
27776				return document.activeElement;
27777			} catch (ex) {
27778				// IE sometimes fails to get the activeElement when resizing table
27779				// TODO: Investigate this
27780				return document.body;
27781			}
27782		}
27783
27784		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
27785		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
27786		function createBookmark(rng) {
27787			if (rng && rng.startContainer) {
27788				return {
27789					startContainer: rng.startContainer,
27790					startOffset: rng.startOffset,
27791					endContainer: rng.endContainer,
27792					endOffset: rng.endOffset
27793				};
27794			}
27795
27796			return rng;
27797		}
27798
27799		function bookmarkToRng(editor, bookmark) {
27800			var rng;
27801
27802			if (bookmark.startContainer) {
27803				rng = editor.getDoc().createRange();
27804				rng.setStart(bookmark.startContainer, bookmark.startOffset);
27805				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
27806			} else {
27807				rng = bookmark;
27808			}
27809
27810			return rng;
27811		}
27812
27813		function isUIElement(elm) {
27814			return !!DOMUtils.DOM.getParent(elm, FocusManager.isEditorUIElement);
27815		}
27816
27817		function isNodeInBodyOfEditor(node, editor) {
27818			var body = editor.getBody();
27819
27820			while (node) {
27821				if (node == body) {
27822					return true;
27823				}
27824
27825				node = node.parentNode;
27826			}
27827		}
27828
27829		function registerEvents(e) {
27830			var editor = e.editor, selectionChangeHandler;
27831
27832			editor.on('init', function() {
27833				// On IE take selection snapshot onbeforedeactivate
27834				if ("onbeforedeactivate" in document && Env.ie < 11) {
27835					editor.dom.bind(editor.getBody(), 'beforedeactivate', function() {
27836						try {
27837							editor.lastRng = editor.selection.getRng();
27838						} catch (ex) {
27839							// IE throws "Unexcpected call to method or property access" some times so lets ignore it
27840						}
27841
27842						editor.selection.lastFocusBookmark = createBookmark(editor.lastRng);
27843					});
27844				} else if (editor.inline || Env.ie > 10) {
27845					// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
27846					editor.on('nodechange keyup', function() {
27847						var node = document.activeElement;
27848
27849						// IE 11 reports active element as iframe not body of iframe
27850						if (node && node.id == editor.id + '_ifr') {
27851							node = editor.getBody();
27852						}
27853
27854						if (isNodeInBodyOfEditor(node, editor)) {
27855							editor.lastRng = editor.selection.getRng();
27856						}
27857					});
27858
27859					// Handles the issue with WebKit not retaining selection within inline document
27860					// If the user releases the mouse out side the body while selecting a nodeChange won't
27861					// fire and there for the selection snapshot won't be stored
27862					// TODO: Optimize this since we only need to bind these on the active editor
27863					if (Env.webkit) {
27864						selectionChangeHandler = function() {
27865							var rng = editor.selection.getRng();
27866
27867							// Store when it's non collapsed
27868							if (!rng.collapsed) {
27869								editor.lastRng = rng;
27870							}
27871						};
27872
27873						// Bind selection handler
27874						DOMUtils.DOM.bind(document, 'selectionchange', selectionChangeHandler);
27875
27876						editor.on('remove', function() {
27877							DOMUtils.DOM.unbind(document, 'selectionchange', selectionChangeHandler);
27878						});
27879					}
27880				}
27881			});
27882
27883			editor.on('setcontent', function() {
27884				editor.lastRng = null;
27885			});
27886
27887			// Remove last selection bookmark on mousedown see #6305
27888			editor.on('mousedown', function() {
27889				editor.selection.lastFocusBookmark = null;
27890			});
27891
27892			editor.on('focusin', function() {
27893				var focusedEditor = editorManager.focusedEditor;
27894
27895				if (editor.selection.lastFocusBookmark) {
27896					editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
27897					editor.selection.lastFocusBookmark = null;
27898				}
27899
27900				if (focusedEditor != editor) {
27901					if (focusedEditor) {
27902						focusedEditor.fire('blur', {focusedEditor: editor});
27903					}
27904
27905					editorManager.activeEditor = editor;
27906					editorManager.focusedEditor = editor;
27907					editor.fire('focus', {blurredEditor: focusedEditor});
27908					editor.focus(false);
27909				}
27910
27911				editor.lastRng = null;
27912			});
27913
27914			editor.on('focusout', function() {
27915				window.setTimeout(function() {
27916					var focusedEditor = editorManager.focusedEditor;
27917
27918					// Still the same editor the the blur was outside any editor UI
27919					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
27920						editor.fire('blur', {focusedEditor: null});
27921						editorManager.focusedEditor = null;
27922						editor.selection.lastFocusBookmark = null;
27923					}
27924				}, 0);
27925			});
27926		}
27927
27928		// Check if focus is moved to an element outside the active editor by checking if the target node
27929		// isn't within the body of the activeEditor nor a UI element such as a dialog child control
27930		DOMUtils.DOM.bind(document, 'focusin', function(e) {
27931			var activeEditor = editorManager.activeEditor;
27932
27933			if (activeEditor && e.target.ownerDocument == document) {
27934				activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.lastRng);
27935
27936				// Fire a blur event if the element isn't a UI element
27937				if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
27938					activeEditor.fire('blur', {focusedEditor: null});
27939					editorManager.focusedEditor = null;
27940				}
27941			}
27942		});
27943
27944		editorManager.on('AddEditor', registerEvents);
27945	}
27946
27947	/**
27948	 * Returns true if the specified element is part of the UI for example an button or text input.
27949	 *
27950	 * @method isEditorUIElement
27951	 * @param  {Element} elm Element to check if it's part of the UI or not.
27952	 * @return {Boolean} True/false state if the element is part of the UI or not.
27953	 */
27954	FocusManager.isEditorUIElement = function(elm) {
27955            // MODIF TN :: Test que ClassName est une chaine avant le indexOf pour eviter le form.className sur la propagation qui renvoi le input id="className"
27956            return ((typeof elm.className == "string")&&(elm.className.indexOf('mce-') !== -1));
27957            //return elm.className.indexOf('mce-') !== -1;
27958	};
27959
27960	return FocusManager;
27961});
27962
27963// Included from: js/tinymce/classes/EditorManager.js
27964
27965/**
27966 * EditorManager.js
27967 *
27968 * Copyright, Moxiecode Systems AB
27969 * Released under LGPL License.
27970 *
27971 * License: http://www.tinymce.com/license
27972 * Contributing: http://www.tinymce.com/contributing
27973 */
27974
27975/**
27976 * This class used as a factory for manager for tinymce.Editor instances.
27977 *
27978 * @example
27979 * tinymce.EditorManager.init({});
27980 *
27981 * @class tinymce.EditorManager
27982 * @mixes tinymce.util.Observable
27983 * @static
27984 */
27985define("tinymce/EditorManager", [
27986	"tinymce/Editor",
27987	"tinymce/dom/DOMUtils",
27988	"tinymce/util/URI",
27989	"tinymce/Env",
27990	"tinymce/util/Tools",
27991	"tinymce/util/Observable",
27992	"tinymce/util/I18n",
27993	"tinymce/FocusManager"
27994], function(Editor, DOMUtils, URI, Env, Tools, Observable, I18n, FocusManager) {
27995	var DOM = DOMUtils.DOM;
27996	var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
27997	var instanceCounter = 0, beforeUnloadDelegate;
27998
27999	var EditorManager = {
28000		/**
28001		 * Major version of TinyMCE build.
28002		 *
28003		 * @property majorVersion
28004		 * @type String
28005		 */
28006		majorVersion : '4',
28007
28008		/**
28009		 * Minor version of TinyMCE build.
28010		 *
28011		 * @property minorVersion
28012		 * @type String
28013		 */
28014		minorVersion : '0.12',
28015
28016		/**
28017		 * Release date of TinyMCE build.
28018		 *
28019		 * @property releaseDate
28020		 * @type String
28021		 */
28022		releaseDate: '2013-12-18',
28023
28024		/**
28025		 * Collection of editor instances.
28026		 *
28027		 * @property editors
28028		 * @type Object
28029		 * @example
28030		 * for (edId in tinymce.editors)
28031		 *     tinymce.editors[edId].save();
28032		 */
28033		editors: [],
28034
28035		/**
28036		 * Collection of language pack data.
28037		 *
28038		 * @property i18n
28039		 * @type Object
28040		 */
28041		i18n: I18n,
28042
28043		/**
28044		 * Currently active editor instance.
28045		 *
28046		 * @property activeEditor
28047		 * @type tinymce.Editor
28048		 * @example
28049		 * tinyMCE.activeEditor.selection.getContent();
28050		 * tinymce.EditorManager.activeEditor.selection.getContent();
28051		 */
28052		activeEditor: null,
28053
28054		setup: function() {
28055			var self = this, baseURL, documentBaseURL, suffix = "", preInit;
28056
28057			// Get base URL for the current document
28058			documentBaseURL = document.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
28059			if (!/[\/\\]$/.test(documentBaseURL)) {
28060				documentBaseURL += '/';
28061			}
28062
28063			// If tinymce is defined and has a base use that or use the old tinyMCEPreInit
28064			preInit = window.tinymce || window.tinyMCEPreInit;
28065			if (preInit) {
28066				baseURL = preInit.base || preInit.baseURL;
28067				suffix = preInit.suffix;
28068			} else {
28069				// Get base where the tinymce script is located
28070				var scripts = document.getElementsByTagName('script');
28071				for (var i = 0; i < scripts.length; i++) {
28072					var src = scripts[i].src;
28073
28074					// Script types supported:
28075					// tinymce.js tinymce.min.js tinymce.dev.js
28076					// tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
28077					// tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
28078					if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
28079						if (src.indexOf('.min') != -1) {
28080							suffix = '.min';
28081						}
28082
28083						baseURL = src.substring(0, src.lastIndexOf('/'));
28084						break;
28085					}
28086				}
28087			}
28088
28089			/**
28090			 * Base URL where the root directory if TinyMCE is located.
28091			 *
28092			 * @property baseURL
28093			 * @type String
28094			 */
28095			self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
28096
28097			/**
28098			 * Document base URL where the current document is located.
28099			 *
28100			 * @property documentBaseURL
28101			 * @type String
28102			 */
28103			self.documentBaseURL = documentBaseURL;
28104
28105			/**
28106			 * Absolute baseURI for the installation path of TinyMCE.
28107			 *
28108			 * @property baseURI
28109			 * @type tinymce.util.URI
28110			 */
28111			self.baseURI = new URI(self.baseURL);
28112
28113			/**
28114			 * Current suffix to add to each plugin/theme that gets loaded for example ".min".
28115			 *
28116			 * @property suffix
28117			 * @type String
28118			 */
28119			self.suffix = suffix;
28120
28121			self.focusManager = new FocusManager(self);
28122		},
28123
28124		/**
28125		 * Initializes a set of editors. This method will create editors based on various settings.
28126		 *
28127		 * @method init
28128		 * @param {Object} settings Settings object to be passed to each editor instance.
28129		 * @example
28130		 * // Initializes a editor using the longer method
28131		 * tinymce.EditorManager.init({
28132		 *    some_settings : 'some value'
28133		 * });
28134		 *
28135		 * // Initializes a editor instance using the shorter version
28136		 * tinyMCE.init({
28137		 *    some_settings : 'some value'
28138		 * });
28139		 */
28140		init: function(settings) {
28141			var self = this, editors = [], editor;
28142
28143			function createId(elm) {
28144				var id = elm.id;
28145
28146				// Use element id, or unique name or generate a unique id
28147				if (!id) {
28148					id = elm.name;
28149
28150					if (id && !DOM.get(id)) {
28151						id = elm.name;
28152					} else {
28153						// Generate unique name
28154						id = DOM.uniqueId();
28155					}
28156
28157					elm.setAttribute('id', id);
28158				}
28159
28160				return id;
28161			}
28162
28163			function execCallback(se, n, s) {
28164				var f = se[n];
28165
28166				if (!f) {
28167					return;
28168				}
28169
28170				return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
28171			}
28172
28173			function hasClass(n, c) {
28174				return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
28175			}
28176
28177			function readyHandler() {
28178				var l, co;
28179
28180				DOM.unbind(window, 'ready', readyHandler);
28181
28182				execCallback(settings, 'onpageload');
28183
28184				if (settings.types) {
28185					// Process type specific selector
28186					each(settings.types, function(type) {
28187						each(DOM.select(type.selector), function(elm) {
28188							var editor = new Editor(createId(elm), extend({}, settings, type), self);
28189							editors.push(editor);
28190							editor.render(1);
28191						});
28192					});
28193
28194					return;
28195				} else if (settings.selector) {
28196					// Process global selector
28197					each(DOM.select(settings.selector), function(elm) {
28198						var editor = new Editor(createId(elm), settings, self);
28199						editors.push(editor);
28200						editor.render(1);
28201					});
28202
28203					return;
28204				}
28205
28206				// Fallback to old setting
28207				switch (settings.mode) {
28208					case "exact":
28209						l = settings.elements || '';
28210
28211						if(l.length > 0) {
28212							each(explode(l), function(v) {
28213								if (DOM.get(v)) {
28214									editor = new Editor(v, settings, self);
28215									editors.push(editor);
28216									editor.render(true);
28217								} else {
28218									each(document.forms, function(f) {
28219										each(f.elements, function(e) {
28220											if (e.name === v) {
28221												v = 'mce_editor_' + instanceCounter++;
28222												DOM.setAttrib(e, 'id', v);
28223
28224												editor = new Editor(v, settings, self);
28225												editors.push(editor);
28226												editor.render(1);
28227											}
28228										});
28229									});
28230								}
28231							});
28232						}
28233						break;
28234
28235					case "textareas":
28236					case "specific_textareas":
28237						each(DOM.select('textarea'), function(elm) {
28238							if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) {
28239								return;
28240							}
28241
28242							if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) {
28243								editor = new Editor(createId(elm), settings, self);
28244								editors.push(editor);
28245								editor.render(true);
28246							}
28247						});
28248						break;
28249				}
28250
28251				// Call onInit when all editors are initialized
28252				if (settings.oninit) {
28253					l = co = 0;
28254
28255					each(editors, function(ed) {
28256						co++;
28257
28258						if (!ed.initialized) {
28259							// Wait for it
28260							ed.on('init', function() {
28261								l++;
28262
28263								// All done
28264								if (l == co) {
28265									execCallback(settings, 'oninit');
28266								}
28267							});
28268						} else {
28269							l++;
28270						}
28271
28272						// All done
28273						if (l == co) {
28274							execCallback(settings, 'oninit');
28275						}
28276					});
28277				}
28278			}
28279
28280			self.settings = settings;
28281
28282			DOM.bind(window, 'ready', readyHandler);
28283		},
28284
28285		/**
28286		 * Returns a editor instance by id.
28287		 *
28288		 * @method get
28289		 * @param {String/Number} id Editor instance id or index to return.
28290		 * @return {tinymce.Editor} Editor instance to return.
28291		 * @example
28292		 * // Adds an onclick event to an editor by id (shorter version)
28293		 * tinymce.get('mytextbox').on('click', function(e) {
28294		 *    ed.windowManager.alert('Hello world!');
28295		 * });
28296		 *
28297		 * // Adds an onclick event to an editor by id (longer version)
28298		 * tinymce.EditorManager.get('mytextbox').on('click', function(e) {
28299		 *    ed.windowManager.alert('Hello world!');
28300		 * });
28301		 */
28302		get: function(id) {
28303			if (id === undefined) {
28304				return this.editors;
28305			}
28306
28307			return this.editors[id];
28308		},
28309
28310		/**
28311		 * Adds an editor instance to the editor collection. This will also set it as the active editor.
28312		 *
28313		 * @method add
28314		 * @param {tinymce.Editor} editor Editor instance to add to the collection.
28315		 * @return {tinymce.Editor} The same instance that got passed in.
28316		 */
28317		add: function(editor) {
28318			var self = this, editors = self.editors;
28319
28320			// Add named and index editor instance
28321			editors[editor.id] = editor;
28322			editors.push(editor);
28323
28324			self.activeEditor = editor;
28325
28326			/**
28327			 * Fires when an editor is added to the EditorManager collection.
28328			 *
28329			 * @event AddEditor
28330			 * @param {Object} e Event arguments.
28331			 */
28332			self.fire('AddEditor', {editor: editor});
28333
28334			if (!beforeUnloadDelegate) {
28335				beforeUnloadDelegate = function() {
28336					self.fire('BeforeUnload');
28337				};
28338
28339				DOM.bind(window, 'beforeunload', beforeUnloadDelegate);
28340			}
28341
28342			return editor;
28343		},
28344
28345		/**
28346		 * Creates an editor instance and adds it to the EditorManager collection.
28347		 *
28348		 * @method createEditor
28349		 * @param {String} id Instance id to use for editor.
28350		 * @param {Object} settings Editor instance settings.
28351		 * @return {tinymce.Editor} Editor instance that got created.
28352		 */
28353		createEditor: function(id, settings) {
28354			return this.add(new Editor(id, settings, this));
28355		},
28356
28357		/**
28358		 * Removes a editor or editors form page.
28359		 *
28360		 * @example
28361		 * // Remove all editors bound to divs
28362		 * tinymce.remove('div');
28363		 *
28364		 * // Remove all editors bound to textareas
28365		 * tinymce.remove('textarea');
28366		 *
28367		 * // Remove all editors
28368		 * tinymce.remove();
28369		 *
28370		 * // Remove specific instance by id
28371		 * tinymce.remove('#id');
28372		 *
28373		 * @method remove
28374		 * @param {tinymce.Editor/String/Object}
28374 [selector] CSS selector or editor instance to remove.
28375		 * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
28376		 */
28377		remove: function(selector) {
28378			var self = this, i, editors = self.editors, editor, removedFromList;
28379
28380			// Remove all editors
28381			if (!selector) {
28382				for (i = editors.length - 1; i >= 0; i--) {
28383					self.remove(editors[i]);
28384				}
28385
28386				return;
28387			}
28388
28389			// Remove editors by selector
28390			if (typeof(selector) == "string") {
28391				selector = selector.selector || selector;
28392
28393				each(DOM.select(selector), function(elm) {
28394					self.remove(editors[elm.id]);
28395				});
28396
28397				return;
28398			}
28399
28400			// Remove specific editor
28401			editor = selector;
28402
28403			// Not in the collection
28404			if (!editors[editor.id]) {
28405				return null;
28406			}
28407
28408			delete editors[editor.id];
28409
28410			for (i = 0; i < editors.length; i++) {
28411				if (editors[i] == editor) {
28412					editors.splice(i, 1);
28413					removedFromList = true;
28414					break;
28415				}
28416			}
28417
28418			// Select another editor since the active one was removed
28419			if (self.activeEditor == editor) {
28420				self.activeEditor = editors[0];
28421			}
28422
28423			/**
28424			 * Fires when an editor is removed from EditorManager collection.
28425			 *
28426			 * @event RemoveEditor
28427			 * @param {Object} e Event arguments.
28428			 */
28429			if (removedFromList) {
28430				self.fire('RemoveEditor', {editor: editor});
28431			}
28432
28433			if (!editors.length) {
28434				DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
28435			}
28436
28437			editor.remove();
28438
28439			return editor;
28440		},
28441
28442		/**
28443		 * Executes a specific command on the currently active editor.
28444		 *
28445		 * @method execCommand
28446		 * @param {String} c Command to perform for example Bold.
28447		 * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
28448		 * @param {String} v Optional value parameter like for example an URL to a link.
28449		 * @return {Boolean} true/false if the command was executed or not.
28450		 */
28451		execCommand: function(cmd, ui, value) {
28452			var self = this, editor = self.get(value);
28453
28454			// Manager commands
28455			switch (cmd) {
28456				case "mceAddEditor":
28457					if (!self.get(value)) {
28458						new Editor(value, self.settings, self).render();
28459					}
28460
28461					return true;
28462
28463				case "mceRemoveEditor":
28464					if (editor) {
28465						editor.remove();
28466					}
28467
28468					return true;
28469
28470				case 'mceToggleEditor':
28471					if (!editor) {
28472						self.execCommand('mceAddEditor', 0, value);
28473						return true;
28474					}
28475
28476					if (editor.isHidden()) {
28477						editor.show();
28478					} else {
28479						editor.hide();
28480					}
28481
28482					return true;
28483			}
28484
28485			// Run command on active editor
28486			if (self.activeEditor) {
28487				return self.activeEditor.execCommand(cmd, ui, value);
28488			}
28489
28490			return false;
28491		},
28492
28493		/**
28494		 * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
28495		 *
28496		 * @method triggerSave
28497		 * @example
28498		 * // Saves all contents
28499		 * tinyMCE.triggerSave();
28500		 */
28501		triggerSave: function() {
28502			each(this.editors, function(editor) {
28503				editor.save();
28504			});
28505		},
28506
28507		/**
28508		 * Adds a language pack, this gets called by the loaded language files like en.js.
28509		 *
28510		 * @method addI18n
28511		 * @param {String} code Optional language code.
28512		 * @param {Object} items Name/value object with translations.
28513		 */
28514		addI18n: function(code, items) {
28515			I18n.add(code, items);
28516		},
28517
28518		/**
28519		 * Translates the specified string using the language pack items.
28520		 *
28521		 * @method translate
28522		 * @param {String/Array/Object} text String to translate
28523		 * @return {String} Translated string.
28524		 */
28525		translate: function(text) {
28526			return I18n.translate(text);
28527		}
28528	};
28529
28530	extend(EditorManager, Observable);
28531
28532	EditorManager.setup();
28533
28534	// Export EditorManager as tinymce/tinymce in global namespace
28535	window.tinymce = window.tinyMCE = EditorManager;
28536
28537	return EditorManager;
28538});
28539
28540// Included from: js/tinymce/classes/LegacyInput.js
28541
28542/**
28543 * LegacyInput.js
28544 *
28545 * Copyright, Moxiecode Systems AB
28546 * Released under LGPL License.
28547 *
28548 * License: http://www.tinymce.com/license
28549 * Contributing: http://www.tinymce.com/contributing
28550 */
28551
28552define("tinymce/LegacyInput", [
28553	"tinymce/EditorManager",
28554	"tinymce/util/Tools"
28555], function(EditorManager, Tools) {
28556	var each = Tools.each, explode = Tools.explode;
28557
28558	EditorManager.on('AddEditor', function(e) {
28559		var editor = e.editor;
28560
28561		editor.on('preInit', function() {
28562			var filters, fontSizes, dom, settings = editor.settings;
28563
28564			function replaceWithSpan(node, styles) {
28565				each(styles, function(value, name) {
28566					if (value) {
28567						dom.setStyle(node, name, value);
28568					}
28569				});
28570
28571				dom.rename(node, 'span');
28572			}
28573
28574			function convert(e) {
28575				dom = editor.dom;
28576
28577				if (settings.convert_fonts_to_spans) {
28578					each(dom.select('font,u,strike', e.node), function(node) {
28579						filters[node.nodeName.toLowerCase()](dom, node);
28580					});
28581				}
28582			}
28583
28584			if (settings.inline_styles) {
28585				fontSizes = explode(settings.font_size_legacy_values);
28586
28587				filters = {
28588					font: function(dom, node) {
28589						replaceWithSpan(node, {
28590							backgroundColor: node.style.backgroundColor,
28591							color: node.color,
28592							fontFamily: node.face,
28593							fontSize: fontSizes[parseInt(node.size, 10) - 1]
28594						});
28595					},
28596
28597					u: function(dom, node) {
28598						replaceWithSpan(node, {
28599							textDecoration: 'underline'
28600						});
28601					},
28602
28603					strike: function(dom, node) {
28604						replaceWithSpan(node, {
28605							textDecoration: 'line-through'
28606						});
28607					}
28608				};
28609
28610				editor.on('PreProcess SetContent', convert);
28611			}
28612		});
28613	});
28614});
28615
28616// Included from: js/tinymce/classes/util/XHR.js
28617
28618/**
28619 * XHR.js
28620 *
28621 * Copyright, Moxiecode Systems AB
28622 * Released under LGPL License.
28623 *
28624 * License: http://www.tinymce.com/license
28625 * Contributing: http://www.tinymce.com/contributing
28626 */
28627
28628/**
28629 * This class enables you to send XMLHTTPRequests cross browser.
28630 * @class tinymce.util.XHR
28631 * @static
28632 * @example
28633 * // Sends a low level Ajax request
28634 * tinymce.util.XHR.send({
28635 *    url: 'someurl',
28636 *    success: function(text) {
28637 *       console.debug(text);
28638 *    }
28639 * });
28640 */
28641define("tinymce/util/XHR", [], function() {
28642	return {
28643		/**
28644		 * Sends a XMLHTTPRequest.
28645		 * Consult the Wiki for details on what settings this method takes.
28646		 *
28647		 * @method send
28648		 * @param {Object} settings Object will target URL, callbacks and other info needed to make the request.
28649		 */
28650		send: function(settings) {
28651			var xhr, count = 0;
28652
28653			function ready() {
28654				if (!settings.async || xhr.readyState == 4 || count++ > 10000) {
28655					if (settings.success && count < 10000 && xhr.status == 200) {
28656						settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings);
28657					} else if (settings.error) {
28658						settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings);
28659					}
28660
28661					xhr = null;
28662				} else {
28663					setTimeout(ready, 10);
28664				}
28665			}
28666
28667			// Default settings
28668			settings.scope = settings.scope || this;
28669			settings.success_scope = settings.success_scope || settings.scope;
28670			settings.error_scope = settings.error_scope || settings.scope;
28671			settings.async = settings.async === false ? false : true;
28672			settings.data = settings.data || '';
28673
28674			xhr = new XMLHttpRequest();
28675
28676			if (xhr) {
28677				if (xhr.overrideMimeType) {
28678					xhr.overrideMimeType(settings.content_type);
28679				}
28680
28681				xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
28682
28683				if (settings.content_type) {
28684					xhr.setRequestHeader('Content-Type', settings.content_type);
28685				}
28686
28687				xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
28688
28689				xhr.send(settings.data);
28690
28691				// Syncronous request
28692				if (!settings.async) {
28693					return ready();
28694				}
28695
28696				// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
28697				setTimeout(ready, 10);
28698			}
28699		}
28700	};
28701});
28702
28703// Included from: js/tinymce/classes/util/JSON.js
28704
28705/**
28706 * JSON.js
28707 *
28708 * Copyright, Moxiecode Systems AB
28709 * Released under LGPL License.
28710 *
28711 * License: http://www.tinymce.com/license
28712 * Contributing: http://www.tinymce.com/contributing
28713 */
28714
28715/**
28716 * JSON parser and serializer class.
28717 *
28718 * @class tinymce.util.JSON
28719 * @static
28720 * @example
28721 * // JSON parse a string into an object
28722 * var obj = tinymce.util.JSON.parse(somestring);
28723 *
28724 * // JSON serialize a object into an string
28725 * var str = tinymce.util.JSON.serialize(obj);
28726 */
28727define("tinymce/util/JSON", [], function() {
28728	function serialize(o, quote) {
28729		var i, v, t, name;
28730
28731		quote = quote || '"';
28732
28733		if (o === null) {
28734			return 'null';
28735		}
28736
28737		t = typeof o;
28738
28739		if (t == 'string') {
28740			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
28741
28742			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
28743				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
28744				if (quote === '"' && a === "'") {
28745					return a;
28746				}
28747
28748				i = v.indexOf(b);
28749
28750				if (i + 1) {
28751					return '\\' + v.charAt(i + 1);
28752				}
28753
28754				a = b.charCodeAt().toString(16);
28755
28756				return '\\u' + '0000'.substring(a.length) + a;
28757			}) + quote;
28758		}
28759
28760		if (t == 'object') {
28761			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
28762					for (i=0, v = '['; i<o.length; i++) {
28763						v += (i > 0 ? ',' : '') + serialize(o[i], quote);
28764					}
28765
28766					return v + ']';
28767				}
28768
28769				v = '{';
28770
28771				for (name in o) {
28772					if (o.hasOwnProperty(name)) {
28773						v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name +
28774							quote +':' + serialize(o[name], quote) : '';
28775					}
28776				}
28777
28778				return v + '}';
28779		}
28780
28781		return '' + o;
28782	}
28783
28784	return {
28785		/**
28786		 * Serializes the specified object as a JSON string.
28787		 *
28788		 * @method serialize
28789		 * @param {Object} obj Object to serialize as a JSON string.
28790		 * @param {String} quote Optional quote string defaults to ".
28791		 * @return {string} JSON string serialized from input.
28792		 */
28793		serialize: serialize,
28794
28795		/**
28796		 * Unserializes/parses the specified JSON string into a object.
28797		 *
28798		 * @method parse
28799		 * @param {string} s JSON String to parse into a JavaScript object.
28800		 * @return {Object} Object from input JSON string or undefined if it failed.
28801		 */
28802		parse: function(text) {
28803			try {
28804				// Trick uglify JS
28805				return window[String.fromCharCode(101) + 'val']('(' + text + ')');
28806			} catch (ex) {
28807				// Ignore
28808			}
28809		}
28810
28811		/**#@-*/
28812	};
28813});
28814
28815// Included from: js/tinymce/classes/util/JSONRequest.js
28816
28817/**
28818 * JSONRequest.js
28819 *
28820 * Copyright, Moxiecode Systems AB
28821 * Released under LGPL License.
28822 *
28823 * License: http://www.tinymce.com/license
28824 * Contributing: http://www.tinymce.com/contributing
28825 */
28826
28827/**
28828 * This class enables you to use JSON-RPC to call backend methods.
28829 *
28830 * @class tinymce.util.JSONRequest
28831 * @example
28832 * var json = new tinymce.util.JSONRequest({
28833 *     url: 'somebackend.php'
28834 * });
28835 *
28836 * // Send RPC call 1
28837 * json.send({
28838 *     method: 'someMethod1',
28839 *     params: ['a', 'b'],
28840 *     success: function(result) {
28841 *         console.dir(result);
28842 *     }
28843 * });
28844 *
28845 * // Send RPC call 2
28846 * json.send({
28847 *     method: 'someMethod2',
28848 *     params: ['a', 'b'],
28849 *     success: function(result) {
28850 *         console.dir(result);
28851 *     }
28852 * });
28853 */
28854define("tinymce/util/JSONRequest", [
28855	"tinymce/util/JSON",
28856	"tinymce/util/XHR",
28857	"tinymce/util/Tools"
28858], function(JSON, XHR, Tools) {
28859	var extend = Tools.extend;
28860
28861	function JSONRequest(settings) {
28862		this.settings = extend({}, settings);
28863		this.count = 0;
28864	}
28865
28866	/**
28867	 * Simple helper function to send a JSON-RPC request without the need to initialize an object.
28868	 * Consult the Wiki API documentation for more details on what you can pass to this function.
28869	 *
28870	 * @method sendRPC
28871	 * @static
28872	 * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc.
28873	 */
28874	JSONRequest.sendRPC = function(o) {
28875		return new JSONRequest().send(o);
28876	};
28877
28878	JSONRequest.prototype = {
28879		/**
28880		 * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function.
28881		 *
28882		 * @method send
28883		 * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc.
28884		 */
28885		send: function(args) {
28886			var ecb = args.error, scb = args.success;
28887
28888			args = extend(this.settings, args);
28889
28890			args.success = function(c, x) {
28891				c = JSON.parse(c);
28892
28893				if (typeof(c) == 'undefined') {
28894					c = {
28895						error : 'JSON Parse error.'
28896					};
28897				}
28898
28899				if (c.error) {
28900					ecb.call(args.error_scope || args.scope, c.error, x);
28901				} else {
28902					scb.call(args.success_scope || args.scope, c.result);
28903				}
28904			};
28905
28906			args.error = function(ty, x) {
28907				if (ecb) {
28908					ecb.call(args.error_scope || args.scope, ty, x);
28909				}
28910			};
28911
28912			args.data = JSON.serialize({
28913				id: args.id || 'c' + (this.count++),
28914				method: args.method,
28915				params: args.params
28916			});
28917
28918			// JSON content type for Ruby on rails. Bug: #1883287
28919			args.content_type = 'application/json';
28920
28921			XHR.send(args);
28922		}
28923	};
28924
28925	return JSONRequest;
28926});
28927
28928// Included from: js/tinymce/classes/util/JSONP.js
28929
28930/**
28931 * JSONP.js
28932 *
28933 * Copyright, Moxiecode Systems AB
28934 * Released under LGPL License.
28935 *
28936 * License: http://www.tinymce.com/license
28937 * Contributing: http://www.tinymce.com/contributing
28938 */
28939
28940define("tinymce/util/JSONP", [
28941	"tinymce/dom/DOMUtils"
28942], function(DOMUtils) {
28943	return {
28944		callbacks: {},
28945		count: 0,
28946
28947		send: function(settings) {
28948			var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count;
28949			var id = 'tinymce_jsonp_' + count;
28950
28951			self.callbacks[count] = function(json) {
28952				dom.remove(id);
28953				delete self.callbacks[count];
28954
28955				settings.callback(json);
28956			};
28957
28958			dom.add(dom.doc.body, 'script', {
28959				id: id,
28960				src: settings.url,
28961				type: 'text/javascript'
28962			});
28963
28964			self.count++;
28965		}
28966	};
28967});
28968
28969// Included from: js/tinymce/classes/util/LocalStorage.js
28970
28971/**
28972 * LocalStorage.js
28973 *
28974 * Copyright, Moxiecode Systems AB
28975 * Released under LGPL License.
28976 *
28977 * License: http://www.tinymce.com/license
28978 * Contributing: http://www.tinymce.com/contributing
28979 */
28980
28981/**
28982 * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
28983 * Storage is done using userData on IE 7 and a special serialization format. The format is designed
28984 * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
28985 * makes it possible to store for example HTML data.
28986 *
28987 * Storage format for userData:
28988 * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
28989 *
28990 * For example this data key1=value1,key2=value2 would be:
28991 * 4,key1,6,value1,4,key2,6,value2
28992 *
28993 * @class tinymce.util.LocalStorage
28994 * @static
28995 * @version 4.0
28996 * @example
28997 * tinymce.util.LocalStorage.setItem('key', 'value');
28998 * var value = tinymce.util.LocalStorage.getItem('key');
28999 */
29000define("tinymce/util/LocalStorage", [], function() {
29001	var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
29002
29003	// Check for native support
29004	try {
29005		if (window.localStorage) {
29006			return localStorage;
29007		}
29008	} catch (ex) {
29009		// Ignore
29010	}
29011
29012	userDataKey = "tinymce";
29013	storageElm = document.documentElement;
29014	hasOldIEDataSupport = !!storageElm.addBehavior;
29015
29016	if (hasOldIEDataSupport) {
29017		storageElm.addBehavior('#default#userData');
29018	}
29019
29020	/**
29021	 * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
29022	 */
29023	function updateKeys() {
29024		keys = [];
29025
29026		for (var key in items) {
29027			keys.push(key);
29028		}
29029
29030		LocalStorage.length = keys.length;
29031	}
29032
29033	/**
29034	 * Loads the userData string and parses it into the items structure.
29035	 */
29036	function load() {
29037		var key, data, value, pos = 0;
29038
29039		items = {};
29040
29041		// localStorage can be disabled on WebKit/Gecko s
29041o make a dummy storage
29042		if (!hasOldIEDataSupport) {
29043			return;
29044		}
29045
29046		function next(end) {
29047			var value, nextPos;
29048
29049			nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
29050			if (nextPos === -1 || nextPos > data.length) {
29051				return null;
29052			}
29053
29054			value = data.substring(pos, nextPos);
29055			pos = nextPos + 1;
29056
29057			return value;
29058		}
29059
29060		storageElm.load(userDataKey);
29061		data = storageElm.getAttribute(userDataKey) || '';
29062
29063		do {
29064			var offset = next();
29065			if (offset === null) {
29066				break;
29067			}
29068
29069			key = next(parseInt(offset, 32) || 0);
29070			if (key !== null) {
29071				offset = next();
29072				if (offset === null) {
29073					break;
29074				}
29075
29076				value = next(parseInt(offset, 32) || 0);
29077
29078				if (key) {
29079					items[key] = value;
29080				}
29081			}
29082		} while (key !== null);
29083
29084		updateKeys();
29085	}
29086
29087	/**
29088	 * Saves the items structure into a the userData format.
29089	 */
29090	function save() {
29091		var value, data = '';
29092
29093		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
29094		if (!hasOldIEDataSupport) {
29095			return;
29096		}
29097
29098		for (var key in items) {
29099			value = items[key];
29100			data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
29101		}
29102
29103		storageElm.setAttribute(userDataKey, data);
29104
29105		try {
29106			storageElm.save(userDataKey);
29107		} catch (ex) {
29108			// Ignore disk full
29109		}
29110
29111		updateKeys();
29112	}
29113
29114	LocalStorage = {
29115		/**
29116		 * Length of the number of items in storage.
29117		 *
29118		 * @property length
29119		 * @type Number
29120		 * @return {Number} Number of items in storage.
29121		 */
29122		//length:0,
29123
29124		/**
29125		 * Returns the key name by index.
29126		 *
29127		 * @method key
29128		 * @param {Number} index Index of key to return.
29129		 * @return {String} Key value or null if it wasn't found.
29130		 */
29131		key: function(index) {
29132			return keys[index];
29133		},
29134
29135		/**
29136		 * Returns the value if the specified key or null if it wasn't found.
29137		 *
29138		 * @method getItem
29139		 * @param {String} key Key of item to retrive.
29140		 * @return {String} Value of the specified item or null if it wasn't found.
29141		 */
29142		getItem: function(key) {
29143			return key in items ? items[key] : null;
29144		},
29145
29146		/**
29147		 * Sets the value of the specified item by it's key.
29148		 *
29149		 * @method setItem
29150		 * @param {String} key Key of the item to set.
29151		 * @param {String} value Value of the item to set.
29152		 */
29153		setItem: function(key, value) {
29154			items[key] = "" + value;
29155			save();
29156		},
29157
29158		/**
29159		 * Removes the specified item by key.
29160		 *
29161		 * @method removeItem
29162		 * @param {String} key Key of item to remove.
29163		 */
29164		removeItem: function(key) {
29165			delete items[key];
29166			save();
29167		},
29168
29169		/**
29170		 * Removes all items.
29171		 *
29172		 * @method clear
29173		 */
29174		clear: function() {
29175			items = {};
29176			save();
29177		}
29178	};
29179
29180	load();
29181
29182	return LocalStorage;
29183});
29184
29185// Included from: js/tinymce/classes/Compat.js
29186
29187/**
29188 * Compat.js
29189 *
29190 * Copyright, Moxiecode Systems AB
29191 * Released under LGPL License.
29192 *
29193 * License: http://www.tinymce.com/license
29194 * Contributing: http://www.tinymce.com/contributing
29195 */
29196
29197/**
29198 * TinyMCE core class.
29199 *
29200 * @static
29201 * @class tinymce
29202 * @borrow-members tinymce.EditorManager
29203 * @borrow-members tinymce.util.Tools
29204 */
29205define("tinymce/Compat", [
29206	"tinymce/dom/DOMUtils",
29207	"tinymce/dom/EventUtils",
29208	"tinymce/dom/ScriptLoader",
29209	"tinymce/AddOnManager",
29210	"tinymce/util/Tools",
29211	"tinymce/Env"
29212], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) {
29213	var tinymce = window.tinymce;
29214
29215	/**
29216	 * @property {tinymce.dom.DOMUtils} DOM Global DOM instance.
29217	 * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance.
29218	 * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance.
29219	 * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance.
29220	 */
29221	tinymce.DOM = DOMUtils.DOM;
29222	tinymce.ScriptLoader = ScriptLoader.ScriptLoader;
29223	tinymce.PluginManager = AddOnManager.PluginManager;
29224	tinymce.ThemeManager = AddOnManager.ThemeManager;
29225
29226	tinymce.dom = tinymce.dom || {};
29227	tinymce.dom.Event = EventUtils.Event;
29228
29229	Tools.each(Tools, function(func, key) {
29230		tinymce[key] = func;
29231	});
29232
29233	Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
29234		tinymce[name] = Env[name.substr(2).toLowerCase()];
29235	});
29236
29237	return {};
29238});
29239
29240// Describe the different namespaces
29241
29242/**
29243 * Root level namespace this contains classes directly releated to the TinyMCE editor.
29244 *
29245 * @namespace tinymce
29246 */
29247
29248/**
29249 * Contains classes for handling the browsers DOM.
29250 *
29251 * @namespace tinymce.dom
29252 */
29253
29254/**
29255 * Contains html parser and serializer logic.
29256 *
29257 * @namespace tinymce.html
29258 */
29259
29260/**
29261 * Contains the different UI types such as buttons, listboxes etc.
29262 *
29263 * @namespace tinymce.ui
29264 */
29265
29266/**
29267 * Contains various utility classes such as json parser, cookies etc.
29268 *
29269 * @namespace tinymce.util
29270 */
29271
29272// Included from: js/tinymce/classes/ui/Layout.js
29273
29274/**
29275 * Layout.js
29276 *
29277 * Copyright, Moxiecode Systems AB
29278 * Released under LGPL License.
29279 *
29280 * License: http://www.tinymce.com/license
29281 * Contributing: http://www.tinymce.com/contributing
29282 */
29283
29284/**
29285 * Base layout manager class.
29286 *
29287 * @class tinymce.ui.Layout
29288 */
29289define("tinymce/ui/Layout", [
29290	"tinymce/util/Class",
29291	"tinymce/util/Tools"
29292], function(Class, Tools) {
29293	"use strict";
29294
29295	return Class.extend({
29296		Defaults: {
29297			firstControlClass: 'first',
29298			lastControlClass: 'last'
29299		},
29300
29301		/**
29302		 * Constructs a layout instance with the specified settings.
29303		 *
29304		 * @constructor
29305		 * @param {Object} settings Name/value object with settings.
29306		 */
29307		init: function(settings) {
29308			this.settings = Tools.extend({}, this.Defaults, settings);
29309		},
29310
29311		/**
29312		 * This method gets invoked before the layout renders the controls.
29313		 *
29314		 * @method preRender
29315		 * @param {tinymce.ui.Container} container Container instance to preRender.
29316		 */
29317		preRender: function(container) {
29318			container.addClass(this.settings.containerClass, 'body');
29319		},
29320
29321		/**
29322		 * Applies layout classes to the container.
29323		 *
29324		 * @private
29325		 */
29326		applyClasses: function(container) {
29327			var self = this, settings = self.settings, items, firstClass, lastClass;
29328
29329			items = container.items().filter(':visible');
29330			firstClass = settings.firstControlClass;
29331			lastClass = settings.lastControlClass;
29332
29333			items.each(function(item) {
29334				item.removeClass(firstClass).removeClass(lastClass);
29335
29336				if (settings.controlClass) {
29337					item.addClass(settings.controlClass);
29338				}
29339			});
29340
29341			items.eq(0).addClass(firstClass);
29342			items.eq(-1).addClass(lastClass);
29343		},
29344
29345		/**
29346		 * Renders the specified container and any layout specific HTML.
29347		 *
29348		 * @method renderHtml
29349		 * @param {tinymce.ui.Container} container Container to render HTML for.
29350		 */
29351		renderHtml: function(container) {
29352			var self = this, settings = self.settings, items, html = '';
29353
29354			items = container.items();
29355			items.eq(0).addClass(settings.firstControlClass);
29356			items.eq(-1).addClass(settings.lastControlClass);
29357
29358			items.each(function(item) {
29359				if (settings.controlClass) {
29360					item.addClass(settings.controlClass);
29361				}
29362
29363				html += item.renderHtml();
29364			});
29365
29366			return html;
29367		},
29368
29369		/**
29370		 * Recalculates the positions of the controls in the specified container.
29371		 *
29372		 * @method recalc
29373		 * @param {tinymce.ui.Container} container Container instance to recalc.
29374		 */
29375		recalc: function() {
29376		},
29377
29378		/**
29379		 * This method gets invoked after the layout renders the controls.
29380		 *
29381		 * @method postRender
29382		 * @param {tinymce.ui.Container} container Container instance to postRender.
29383		 */
29384		postRender: function() {
29385		}
29386	});
29387});
29388
29389// Included from: js/tinymce/classes/ui/AbsoluteLayout.js
29390
29391/**
29392 * AbsoluteLayout.js
29393 *
29394 * Copyright, Moxiecode Systems AB
29395 * Released under LGPL License.
29396 *
29397 * License: http://www.tinymce.com/license
29398 * Contributing: http://www.tinymce.com/contributing
29399 */
29400
29401/**
29402 * LayoutManager for absolute positioning. This layout manager is more of
29403 * a base class for other layouts but can be created and used directly.
29404 *
29405 * @-x-less AbsoluteLayout.less
29406 * @class tinymce.ui.AbsoluteLayout
29407 * @extends tinymce.ui.Layout
29408 */
29409define("tinymce/ui/AbsoluteLayout", [
29410	"tinymce/ui/Layout"
29411], function(Layout) {
29412	"use strict";
29413
29414	return Layout.extend({
29415		Defaults: {
29416			containerClass: 'abs-layout',
29417			controlClass: 'abs-layout-item'
29418		},
29419
29420		/**
29421		 * Recalculates the positions of the controls in the specified container.
29422		 *
29423		 * @method recalc
29424		 * @param {tinymce.ui.Container} container Container instance to recalc.
29425		 */
29426		recalc: function(container) {
29427			container.items().filter(':visible').each(function(ctrl) {
29428				var settings = ctrl.settings;
29429
29430				ctrl.layoutRect({
29431					x: settings.x,
29432					y: settings.y,
29433					w: settings.w,
29434					h: settings.h
29435				});
29436
29437				if (ctrl.recalc) {
29438					ctrl.recalc();
29439				}
29440			});
29441		},
29442
29443		/**
29444		 * Renders the specified container and any layout specific HTML.
29445		 *
29446		 * @method renderHtml
29447		 * @param {tinymce.ui.Container} container Container to render HTML for.
29448		 */
29449		renderHtml: function(container) {
29450			return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container);
29451		}
29452	});
29453});
29454
29455// Included from: js/tinymce/classes/ui/Tooltip.js
29456
29457/**
29458 * Tooltip.js
29459 *
29460 * Copyright, Moxiecode Systems AB
29461 * Released under LGPL License.
29462 *
29463 * License: http://www.tinymce.com/license
29464 * Contributing: http://www.tinymce.com/contributing
29465 */
29466
29467/**
29468 * Creates a tooltip instance.
29469 *
29470 * @-x-less ToolTip.less
29471 * @class tinymce.ui.ToolTip
29472 * @extends tinymce.ui.Control
29473 * @mixes tinymce.ui.Movable
29474 */
29475define("tinymce/ui/Tooltip", [
29476	"tinymce/ui/Control",
29477	"tinymce/ui/Movable"
29478], function(Control, Movable) {
29479	return Control.extend({
29480		Mixins: [Movable],
29481
29482		Defaults: {
29483			classes: 'widget tooltip tooltip-n'
29484		},
29485
29486		/**
29487		 * Sets/gets the current label text.
29488		 *
29489		 * @method text
29490		 * @param {String} [text] New label text.
29491		 * @return {String|tinymce.ui.Tooltip} Current text or current label instance.
29492		 */
29493		text: function(value) {
29494			var self = this;
29495
29496			if (typeof(value) != "undefined") {
29497				self._value = value;
29498
29499				if (self._rendered) {
29500					self.getEl().lastChild.innerHTML = self.encode(value);
29501				}
29502
29503				return self;
29504			}
29505
29506			return self._value;
29507		},
29508
29509		/**
29510		 * Renders the control as a HTML string.
29511		 *
29512		 * @method renderHtml
29513		 * @return {String} HTML representing the control.
29514		 */
29515		renderHtml: function() {
29516			var self = this, prefix = self.classPrefix;
29517
29518			return (
29519				'<div id="' + self._id + '" class="' + self.classes() + '" role="presentation">' +
29520					'<div class="' + prefix + 'tooltip-arrow"></div>' +
29521					'<div class="' + prefix + 'tooltip-inner">' + self.encode(self._text) + '</div>' +
29522				'</div>'
29523			);
29524		},
29525
29526		/**
29527		 * Repaints the control after a layout operation.
29528		 *
29529		 * @method repaint
29530		 */
29531		repaint: function() {
29532			var self = this, style, rect;
29533
29534			style = self.getEl().style;
29535			rect = self._layoutRect;
29536
29537			style.left = rect.x + 'px';
29538			style.top = rect.y + 'px';
29539			style.zIndex = 0xFFFF + 0xFFFF;
29540		}
29541	});
29542});
29543
29544// Included from: js/tinymce/classes/ui/Widget.js
29545
29546/**
29547 * Widget.js
29548 *
29549 * Copyright, Moxiecode Systems AB
29550 * Released under LGPL License.
29551 *
29552 * License: http://www.tinymce.com/license
29553 * Contributing: http://www.tinymce.com/contributing
29554 */
29555
29556/**
29557 * Widget base class a widget is a control that has a tooltip and s
29557ome basic states.
29558 *
29559 * @class tinymce.ui.Widget
29560 * @extends tinymce.ui.Control
29561 */
29562define("tinymce/ui/Widget", [
29563	"tinymce/ui/Control",
29564	"tinymce/ui/Tooltip"
29565], function(Control, Tooltip) {
29566	"use strict";
29567
29568	var tooltip;
29569
29570	var Widget = Control.extend({
29571		/**
29572		 * Constructs a instance with the specified settings.
29573		 *
29574		 * @constructor
29575		 * @param {Object} settings Name/value object with settings.
29576		 * @setting {String} tooltip Tooltip text to display when hovering.
29577		 * @setting {Boolean} autofocus True if the control should be focused when rendered.
29578		 * @setting {String} text Text to display inside widget.
29579		 */
29580		init: function(settings) {
29581			var self = this;
29582
29583			self._super(settings);
29584			self.canFocus = true;
29585
29586			if (settings.tooltip && Widget.tooltips !== false) {
29587				self.on('mouseenter', function(e) {
29588					var tooltip = self.tooltip().moveTo(-0xFFFF);
29589
29590					if (e.control == self) {
29591						var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
29592
29593						tooltip.toggleClass('tooltip-n', rel == 'bc-tc');
29594						tooltip.toggleClass('tooltip-nw', rel == 'bc-tl');
29595						tooltip.toggleClass('tooltip-ne', rel == 'bc-tr');
29596
29597						tooltip.moveRel(self.getEl(), rel);
29598					} else {
29599						tooltip.hide();
29600					}
29601				});
29602
29603				self.on('mouseleave mousedown click', function() {
29604					self.tooltip().hide();
29605				});
29606
29607			}
29608
29609			self.aria('label', settings.tooltip);
29610		},
29611
29612		/**
29613		 * Returns the current tooltip instance.
29614		 *
29615		 * @method tooltip
29616		 * @return {tinymce.ui.Tooltip} Tooltip instance.
29617		 */
29618		tooltip: function() {
29619			var self = this;
29620
29621			if (!tooltip) {
29622				tooltip = new Tooltip({type: 'tooltip'});
29623				tooltip.renderTo(self.getContainerElm());
29624			}
29625
29626			return tooltip;
29627		},
29628
29629		/**
29630		 * Sets/gets the active state of the widget.
29631		 *
29632		 * @method active
29633		 * @param {Boolean} [state] State if the control is active.
29634		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
29635		 */
29636		active: function(state) {
29637			var self = this, undef;
29638
29639			if (state !== undef) {
29640				self.aria('pressed', state);
29641				self.toggleClass('active', state);
29642			}
29643
29644			return self._super(state);
29645		},
29646
29647		/**
29648		 * Sets/gets the disabled state of the widget.
29649		 *
29650		 * @method disabled
29651		 * @param {Boolean} [state] State if the control is disabled.
29652		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
29653		 */
29654		disabled: function(state) {
29655			var self = this, undef;
29656
29657			if (state !== undef) {
29658				self.aria('disabled', state);
29659				self.toggleClass('disabled', state);
29660			}
29661
29662			return self._super(state);
29663		},
29664
29665		/**
29666		 * Called after the control has been rendered.
29667		 *
29668		 * @method postRender
29669		 */
29670		postRender: function() {
29671			var self = this, settings = self.settings;
29672
29673			self._rendered = true;
29674
29675			self._super();
29676
29677			if (!self.parent() && (settings.width || settings.height)) {
29678				self.initLayoutRect();
29679				self.repaint();
29680			}
29681
29682			if (settings.autofocus) {
29683				setTimeout(function() {
29684					self.focus();
29685				}, 0);
29686			}
29687		},
29688
29689		/**
29690		 * Removes the current control from DOM and from UI collections.
29691		 *
29692		 * @method remove
29693		 * @return {tinymce.ui.Control} Current control instance.
29694		 */
29695		remove: function() {
29696			this._super();
29697
29698			if (tooltip) {
29699				tooltip.remove();
29700				tooltip = null;
29701			}
29702		}
29703	});
29704
29705	return Widget;
29706});
29707
29708// Included from: js/tinymce/classes/ui/Button.js
29709
29710/**
29711 * Button.js
29712 *
29713 * Copyright, Moxiecode Systems AB
29714 * Released under LGPL License.
29715 *
29716 * License: http://www.tinymce.com/license
29717 * Contributing: http://www.tinymce.com/contributing
29718 */
29719
29720/**
29721 * This class is used to create buttons. You can create them directly or through the Factory.
29722 *
29723 * @example
29724 * // Create and render a button to the body element
29725 * tinymce.ui.Factory.create({
29726 *     type: 'button',
29727 *     text: 'My button'
29728 * }).renderTo(document.body);
29729 *
29730 * @-x-less Button.less
29731 * @class tinymce.ui.Button
29732 * @extends tinymce.ui.Widget
29733 */
29734define("tinymce/ui/Button", [
29735	"tinymce/ui/Widget"
29736], function(Widget) {
29737	"use strict";
29738
29739	return Widget.extend({
29740		Defaults: {
29741			classes: "widget btn",
29742			role: "button"
29743		},
29744
29745		/**
29746		 * Constructs a new button instance with the specified settings.
29747		 *
29748		 * @constructor
29749		 * @param {Object} settings Name/value object with settings.
29750		 * @setting {String} size Size of the button small|medium|large.
29751		 * @setting {String} image Image to use for icon.
29752		 * @setting {String} icon Icon to use for button.
29753		 */
29754		init: function(settings) {
29755			var self = this, size;
29756
29757			self.on('click mousedown', function(e) {
29758				e.preventDefault();
29759			});
29760
29761			self._super(settings);
29762			size = settings.size;
29763
29764			if (settings.subtype) {
29765				self.addClass(settings.subtype);
29766			}
29767
29768			if (size) {
29769				self.addClass('btn-' + size);
29770			}
29771		},
29772
29773		/**
29774		 * Sets/gets the current button icon.
29775		 *
29776		 * @method icon
29777		 * @param {String} [icon] New icon identifier.
29778		 * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
29779		 */
29780		icon: function(icon) {
29781			var self = this, prefix = self.classPrefix;
29782
29783			if (typeof(icon) == 'undefined') {
29784				return self.settings.icon;
29785			}
29786
29787			self.settings.icon = icon;
29788			icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
29789
29790			if (self._rendered) {
29791				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
29792
29793				if (icon) {
29794					if (!iconElm || iconElm != btnElm.firstChild) {
29795						iconElm = document.createElement('i');
29796						btnElm.insertBefore(iconElm, btnElm.firstChild);
29797					}
29798
29799					iconElm.className = icon;
29800				} else if (iconElm) {
29801					btnElm.removeChild(iconElm);
29802				}
29803
29804				self.text(self._text); // Set text again to fix whitespace between icon + text
29805			}
29806
29807			return self;
29808		},
29809
29810		/**
29811		 * Repaints the button for example after it's been resizes by a layout engine.
29812		 *
29813		 * @method repaint
29814		 */
29815		repaint: function() {
29816			var btnStyle = this.getEl().firstChild.style;
29817
29818			btnStyle.width = btnStyle.height = "100%";
29819
29820			this._super();
29821		},
29822
29823		/**
29824		 * Renders the control as a HTML string.
29825		 *
29826		 * @method renderHtml
29827		 * @return {String} HTML representing the control.
29828		 */
29829		renderHtml: function() {
29830			var self = this, id = self._id, prefix = self.classPrefix;
29831			var icon = self.settings.icon, image = '';
29832
29833			if (self.settings.image) {
29834				icon = 'none';
29835				image = ' style="background-image: url(\'' + self.settings.image + '\')"';
29836			}
29837
29838			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
29839
29840			return (
29841				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
29842					'<button role="presentation" type="button" tabindex="-1">' +
29843						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
29844						(self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') +
29845					'</button>' +
29846				'</div>'
29847			);
29848		}
29849	});
29850});
29851
29852// Included from: js/tinymce/classes/ui/ButtonGroup.js
29853
29854/**
29855 * ButtonGroup.js
29856 *
29857 * Copyright, Moxiecode Systems AB
29858 * Released under LGPL License.
29859 *
29860 * License: http://www.tinymce.com/license
29861 * Contributing: http://www.tinymce.com/contributing
29862 */
29863
29864/**
29865 * This control enables you to put multiple buttons into a group. This is
29866 * useful when you want to combine similar toolbar buttons into a group.
29867 *
29868 * @example
29869 * // Create and render a buttongroup with two buttons to the body element
29870 * tinymce.ui.Factory.create({
29871 *     type: 'buttongroup',
29872 *     items: [
29873 *         {text: 'Button A'},
29874 *         {text: 'Button B'}
29875 *     ]
29876 * }).renderTo(document.body);
29877 *
29878 * @-x-less ButtonGroup.less
29879 * @class tinymce.ui.ButtonGroup
29880 * @extends tinymce.ui.Container
29881 */
29882define("tinymce/ui/ButtonGroup", [
29883	"tinymce/ui/Container"
29884], function(Container) {
29885	"use strict";
29886
29887	return Container.extend({
29888		Defaults: {
29889			defaultType: 'button',
29890			role: 'toolbar'
29891		},
29892
29893		/**
29894		 * Renders the control as a HTML string.
29895		 *
29896		 * @method renderHtml
29897		 * @return {String} HTML representing the control.
29898		 */
29899		renderHtml: function() {
29900			var self = this, layout = self._layout;
29901
29902			self.addClass('btn-group');
29903			self.preRender();
29904			layout.preRender(self);
29905
29906			return (
29907				'<div id="' + self._id + '" class="' + self.classes() + '">'+
29908					'<div id="' + self._id + '-body">'+
29909						(self.settings.html || '') + layout.renderHtml(self) +
29910					'</div>' +
29911				'</div>'
29912			);
29913		}
29914	});
29915});
29916
29917// Included from: js/tinymce/classes/ui/Checkbox.js
29918
29919/**
29920 * Checkbox.js
29921 *
29922 * Copyright, Moxiecode Systems AB
29923 * Released under LGPL License.
29924 *
29925 * License: http://www.tinymce.com/license
29926 * Contributing: http://www.tinymce.com/contributing
29927 */
29928
29929/**
29930 * This control creates a custom checkbox.
29931 *
29932 * @example
29933 * // Create and render a checkbox to the body element
29934 * tinymce.ui.Factory.create({
29935 *     type: 'checkbox',
29936 *     checked: true,
29937 *     text: 'My checkbox'
29938 * }).renderTo(document.body);
29939 *
29940 * @-x-less Checkbox.less
29941 * @class tinymce.ui.Checkbox
29942 * @extends tinymce.ui.Widget
29943 */
29944define("tinymce/ui/Checkbox", [
29945	"tinymce/ui/Widget"
29946], function(Widget) {
29947	"use strict";
29948
29949	return Widget.extend({
29950		Defaults: {
29951			classes: "checkbox",
29952			role: "checkbox",
29953			checked: false
29954		},
29955
29956		/**
29957		 * Constructs a new Checkbox instance with the specified settings.
29958		 *
29959		 * @constructor
29960		 * @param {Object} settings Name/value object with settings.
29961		 * @setting {Boolean} checked True if the checkbox should be checked by default.
29962		 */
29963		init: function(settings) {
29964			var self = this;
29965
29966			self._super(settings);
29967
29968			self.on('click mousedown', function(e) {
29969				e.preventDefault();
29970			});
29971
29972			self.on('click', function(e) {
29973				e.preventDefault();
29974
29975				if (!self.disabled()) {
29976					self.checked(!self.checked());
29977				}
29978			});
29979
29980			self.checked(self.settings.checked);
29981		},
29982
29983		/**
29984		 * Getter/setter function for the checked state.
29985		 *
29986		 * @method checked
29987		 * @param {Boolean} [state] State to be set.
29988		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
29989		 */
29990		checked: function(state) {
29991			var self = this;
29992
29993			if (typeof state != "undefined") {
29994				if (state) {
29995					self.addClass('checked');
29996				} else {
29997					self.removeClass('checked');
29998				}
29999
30000				self._checked = state;
30001				self.aria('checked', state);
30002
30003				return self;
30004			}
30005
30006			return self._checked;
30007		},
30008
30009		/**
30010		 * Getter/setter function for the value state.
30011		 *
30012		 * @method value
30013		 * @param {Boolean} [state] State to be set.
30014		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
30015		 */
30016		value: function(state) {
30017			return this.checked(state);
30018		},
30019
30020		/**
30021		 * Renders the control as a HTML string.
30022		 *
30023		 * @method renderHtml
30024		 * @return {String} HTML representing the control.
30025		 */
30026		renderHtml: function() {
30027			var self = this, id = self._id, prefix = self.classPrefix;
30028
30029			return (
30030				'<div id="' + id + '" class="' + self.classes() + '" unselectable="on" aria-labeledby="' + id + '-al" tabindex="-1">' +
30031					'<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' +
30032					'<span id="' + id +'-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' +
30033				'</div>'
30034			);
30035		}
30036	});
30037});
30038
30039// Included from: js/tinymce/classes/ui/PanelButton.js
30040
30041/**
30042 * PanelButton.js
30043 *
30044 * Copyright, Moxiecode Systems AB
30045 * Released under LGPL License.
30046 *
30047 * License: http://www.tinymce.com/license
30048 * Contributing: http://www.tinymce.com/contributing
30049 */
30050
30051/**
30052 * Creates a new panel button.
30053 *
30054 * @class tinymce.ui.PanelButton
30055 * @extends tinymce.ui.Button
30056 */
30057define("tinymce/ui/PanelButton", [
30058	"tinymce/ui/Button",
30059	"tinymce/ui/FloatPanel"
30060], function(Button, FloatPanel) {
30061	"use strict";
30062
30063	return Button.extend({
30064		/**
30065		 * Shows the panel for the button.
30066		 *
30067		 * @method showPanel
30068		 */
30069		showPanel: function() {
30070			var self = this, settings = self.settings;
30071
30072			self.active(true);
30073
30074			if (!self.panel) {
30075				var panelSettings = settings.panel;
30076
30077				// Wrap panel in grid layout if type if specified
30078				// This makes it possible to add forms or other containers directly in the panel option
30079				if (panelSettings.type) {
30080					panelSettings = {
30081						layout: 'grid',
30082						items: panelSettings
30083					};
30084				}
30085
30086				panelSettings.popover = true;
30087				panelSettings.autohide = true;
30088
30089				self.panel = new FloatPanel(panelSettings).on('hide', function() {
30090					self.active(false);
30091				}).parent(self).renderTo(self.getContainerElm());
30092				self.panel.fire('show');
30093				self.panel.reflow();
30094			} else {
30095				self.panel.show();
30096			}
30097
30098			self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc']));
30099		},
30100
30101		/**
30102		 * Hides the panel for the button.
30103		 *
30104		 * @method hidePanel
30105		 */
30106		hidePanel: function() {
30107			var self = this;
30108
30109			if (self.panel) {
30110				self.panel.hide();
30111			}
30112		},
30113
30114		/**
30115		 * Called after the control has been rendered.
30116		 *
30117		 * @method postRender
30118		 */
30119		postRender: function() {
30120			var self = this;
30121
30122			self.on('click', function(e) {
30123				if (e.control === self) {
30124					if (self.panel && self.panel.visible()) {
30125						self.hidePanel();
30126					} else {
30127						self.showPanel();
30128					}
30129				}
30130			});
30131
30132			return self._super();
30133		}
30134	});
30135});
30136
30137// Included from: js/tinymce/classes/ui/ColorButton.js
30138
30139/**
30140 * ColorButton.js
30141 *
30142 * Copyright, Moxiecode Systems AB
30143 * Released under LGPL License.
30144 *
30145 * License: http://www.tinymce.com/license
30146 * Contributing: http://www.tinymce.com/contributing
30147 */
30148
30149/**
30150 * This class creates a color button control. This is a split button in which the main
30151 * button has a visual representation of the currently selected color. When clicked 
30152 * the caret button displays a color picker, allowing the user to select a new color.
30153 *
30154 * @-x-less ColorButton.less
30155 * @class tinymce.ui.ColorButton
30156 * @extends tinymce.ui.PanelButton
30157 */
30158define("tinymce/ui/ColorButton", [
30159	"tinymce/ui/PanelButton",
30160	"tinymce/dom/DOMUtils"
30161], function(PanelButton, DomUtils) {
30162	"use strict";
30163	
30164	var DOM = DomUtils.DOM;
30165
30166	return PanelButton.extend({
30167		/**
30168		 * Constructs a new ColorButton instance with the specified settings.
30169		 *
30170		 * @constructor
30171		 * @param {Object} settings Name/value object with settings.
30172		 */
30173		init: function(settings) {
30174			this._super(settings);
30175			this.addClass('colorbutton');
30176		},
30177
30178		/**
30179		 * Getter/setter for the current color.
30180		 *
30181		 * @method color
30182		 * @param {String} [color] Color to set.
30183		 * @return {String|tinymce.ui.ColorButton} Current color or current instance.
30184		 */
30185		color: function(color) {
30186			if (color) {
30187				this._color = color;
30188				this.getEl('preview').style.backgroundColor = color;
30189				return this;
30190			}
30191
30192			return this._color;
30193		},
30194
30195		/**
30196		 * Renders the control as a HTML string.
30197		 *
30198		 * @method renderHtml
30199		 * @return {String} HTML representing the control.
30200		 */
30201		renderHtml: function() {
30202			var self = this, id = self._id, prefix = self.classPrefix;
30203			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
30204			var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '';
30205
30206			return (
30207				'<div id="' + id + '" class="' + self.classes() + '">' +
30208					'<button role="presentation" hidefocus type="button" tabindex="-1">' +
30209						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
30210						'<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' +
30211						(self._text ? (icon ? ' ' : '') + (self._text) : '') +
30212					'</button>' +
30213					'<button type="button" class="' + prefix + 'open" hidefocus tabindex="-1">' +
30214						' <i class="' + prefix + 'caret"></i>' +
30215					'</button>' +
30216				'</div>'
30217			);
30218		},
30219		
30220		/**
30221		 * Called after the control has been rendered.
30222		 *
30223		 * @method postRender
30224		 */
30225		postRender: function() {
30226			var self = this, onClickHandler = self.settings.onclick;
30227
30228			self.on('click', function(e) {
30229				if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) {
30230					e.stopImmediatePropagation();
30231					onClickHandler.call(self, e);
30232				}
30233			});
30234
30235			delete self.settings.onclick;
30236
30237			return self._super();
30238		}
30239		
30240	});
30241});
30242
30243// Included from: js/tinymce/classes/ui/ComboBox.js
30244
30245/**
30246 * ComboBox.js
30247 *
30248 * Copyright, Moxiecode Systems AB
30249 * Released under LGPL License.
30250 *
30251 * License: http://www.tinymce.com/license
30252 * Contributing: http://www.tinymce.com/contributing
30253 */
30254
30255/**
30256 * This class creates a combobox control. Select box that you select a value from or
30257 * type a value into.
30258 *
30259 * @-x-less ComboBox.less
30260 * @class tinymce.ui.ComboBox
30261 * @extends tinymce.ui.Widget
30262 */
30263define("tinymce/ui/ComboBox", [
30264	"tinymce/ui/Widget",
30265	"tinymce/ui/DomUtils"
30266], function(Widget, DomUtils) {
30267	"use strict";
30268
30269	return Widget.extend({
30270		/**
30271		 * Constructs a new control instance with the specified settings.
30272		 *
30273		 * @constructor
30274		 * @param {Object} settings Name/value object with settings.
30275		 * @setting {String} placeholder Placeholder text to display.
30276		 */
30277		init: function(settings) {
30278			var self = this;
30279
30280			self._super(settings);
30281			self.addClass('combobox');
30282
30283			self.on('click', function(e) {
30284				var elm = e.target;
30285
30286				while (elm) {
30287					if (elm.id && elm.id.indexOf('-open') != -1) {
30288						self.fire('action');
30289					}
30290
30291					elm = elm.parentNode;
30292				}
30293			});
30294
30295			// TODO: Rework this
30296			self.on('keydown', function(e) {
30297				if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
30298					self.parents().reverse().each(function(ctrl) {
30299						e.preventDefault();
30300						self.fire('change');
30301
30302						if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
30303							ctrl.fire('submit', {data: ctrl.toJSON()});
30304							return false;
30305						}
30306					});
30307				}
30308			});
30309
30310			if (settings.placeholder) {
30311				self.addClass('placeholder');
30312
30313				self.on('focusin', function() {
30314					if (!self._hasOnChange) {
30315						DomUtils.on(self.getEl('inp'), 'change', function() {
30316							self.fire('change');
30317						});
30318
30319						self._hasOnChange = true;
30320					}
30321
30322					if (self.hasClass('placeholder')) {
30323						self.getEl('inp').value = '';
30324						self.removeClass('placeholder');
30325					}
30326				});
30327
30328				self.on('focusout', function() {
30329					if (self.value().length === 0) {
30330						self.getEl('inp').value = settings.placeholder;
30331						self.addClass('placeholder');
30332					}
30333				});
30334			}
30335		},
30336
30337		/**
30338		 * Getter/setter function for the control value.
30339		 *
30340		 * @method value
30341		 * @param {String} [value] Value to be set.
30342		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
30343		 */
30344		value: function(value) {
30345			var self = this;
30346
30347			if (typeof(value) != "undefined") {
30348				self._value = value;
30349				self.removeClass('placeholder');
30350
30351				if (self._rendered) {
30352					self.getEl('inp').value = value;
30353				}
30354
30355				return self;
30356			}
30357
30358			if (self._rendered) {
30359				value = self.getEl('inp').value;
30360
30361				if (value != self.settings.placeholder) {
30362					return value;
30363				}
30364
30365				return '';
30366			}
30367
30368			return self._value;
30369		},
30370
30371		/**
30372		 * Getter/setter function for the disabled state.
30373		 *
30374		 * @method value
30375		 * @param {Boolean} [state] State to be set.
30376		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
30377		 */
30378		disabled: function(state) {
30379			var self = this;
30380
30381			if (self._rendered && typeof(state) != 'undefined') {
30382				self.getEl('inp').disabled = state;
30383			}
30384
30385			return self._super(state);
30386		},
30387
30388		/**
30389		 * Focuses the input area of the control.
30390		 *
30391		 * @method focus
30392		 */
30393		focus: function() {
30394			this.getEl('inp').focus();
30395		},
30396
30397		/**
30398		 * Repaints the control after a layout operation.
30399		 *
30400		 * @method repaint
30401		 */
30402		repaint: function() {
30403			var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
30404			var width, lineHeight;
30405
30406			if (openElm) {
30407				width = rect.w - DomUtils.getSize(openElm).width - 10;
30408			} else {
30409				width = rect.w - 10;
30410			}
30411
30412			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
30413			var doc = document;
30414			if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
30415				lineHeight = (self.layoutRect().h - 2) + 'px';
30416			}
30417
30418			DomUtils.css(elm.firstChild, {
30419				width: width,
30420				lineHeight: lineHeight
30421			});
30422
30423			self._super();
30424
30425			return self;
30426		},
30427
30428		/**
30429		 * Post render method. Called after the control has been rendered to the target.
30430		 *
30431		 * @method postRender
30432		 * @return {tinymce.ui.ComboBox} Current combobox instance.
30433		 */
30434		postRender: function() {
30435			var self = this;
30436
30437			DomUtils.on(this.getEl('inp'), 'change', function() {
30438				self.fire('change');
30439			});
30440
30441			return self._super();
30442		},
30443
30444		remove: function() {
30445			DomUtils.off(this.getEl('inp'));
30446			this._super();
30447		},
30448
30449		/**
30450		 * Renders the control as a HTML string.
30451		 *
30452		 * @method renderHtml
30453		 * @return {String} HTML representing the control.
30454		 */
30455		renderHtml: function() {
30456			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
30457			var value = settings.value || settings.placeholder || '';
30458			var icon, text, openBtnHtml = '';
30459
30460			icon = settings.icon ? prefix + 'ico ' + prefix + 'i-' + settings.icon : '';
30461			text = self._text;
30462
30463			if (icon || text) {
30464				openBtnHtml = (
30465					'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1">' +
30466						'<button id="' + id + '-action" type="button" hidefocus tabindex="-1">' +
30467							(icon ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') +
30468							(text ? (icon ? ' ' : '') + text : '') +
30469						'</button>' +
30470					'</div>'
30471				);
30472
30473				self.addClass('has-open');
30474			}
30475
30476			return (
30477				'<div id="' + id + '" class="' + self.classes() + '">' +
30478					'<input id="' + id + '-inp" class="' + prefix + 'textbox ' + prefix + 'placeholder" value="' +
30479					value + '" hidefocus="true"' + (self.disabled() ? ' disabled="disabled"' : '') + '>' +
30480					openBtnHtml +
30481				'</div>'
30482			);
30483		}
30484	});
30485});
30486
30487// Included from: js/tinymce/classes/ui/Path.js
30488
30489/**
30490 * Path.js
30491 *
30492 * Copyright, Moxiecode Systems AB
30493 * Released under LGPL License.
30494 *
30495 * License: http://www.tinymce.com/license
30496 * Contributing: http://www.tinymce.com/contributing
30497 */
30498
30499/**
30500 * Creates a new path control.
30501 *
30502 * @-x-less Path.less
30503 * @class tinymce.ui.Path
30504 * @extends tinymce.ui.Widget
30505 */
30506define("tinymce/ui/Path", [
30507	"tinymce/ui/Widget",
30508	"tinymce/ui/KeyboardNavigation"
30509], function(Widget, KeyboardNavigation) {
30510	"use strict";
30511
30512	return Widget.extend({
30513		/**
30514		 * Constructs a instance with the specified settings.
30515		 *
30516		 * @constructor
30517		 * @param {Object} settings Name/value object with settings.
30518		 * @setting {String} delimiter Delimiter to display between items in path.
30519		 */
30520		init: function(settings) {
30521			var self = this;
30522
30523			if (!settings.delimiter) {
30524				settings.delimiter = '\u00BB';
30525			}
30526
30527			self._super(settings);
30528			self.addClass('path');
30529			self.canFocus = true;
30530
30531			self.on('click', function(e) {
30532				var index, target = e.target;
30533
30534				if ((index = target.getAttribute('data-index'))) {
30535					self.fire('select', {value: self.data()[index], index: index});
30536				}
30537			});
30538		},
30539
30540		/**
30541		 * Focuses the current control.
30542		 *
30543		 * @method focus
30544		 * @return {tinymce.ui.Control} Current control instance.
30545		 */
30546		focus: function() {
30547			var self = this;
30548
30549			self.keyNav = new KeyboardNavigation({
30550				root: self,
30551				enableLeftRight: true
30552			});
30553
30554			self.keyNav.focusFirst();
30555
30556			return self;
30557		},
30558
30559		/**
30560		 * Sets/gets the data to be used for the path.
30561		 *
30562		 * @method data
30563		 * @param {Array} data Array with items name is rendered to path.
30564		 */
30565		data: function(data) {
30566			var self = this;
30567
30568			if (typeof(data) !== "undefined") {
30569				self._data = data;
30570				self.update();
30571
30572				return self;
30573			}
30574
30575			return self._data;
30576		},
30577
30578		/**
30579		 * Updated the path.
30580		 *
30581		 * @private
30582		 */
30583		update: function() {
30584			this.innerHtml(this._getPathHtml());
30585		},
30586
30587		/**
30588		 * Called after the control has been rendered.
30589		 *
30590		 * @method postRender
30591		 */
30592		postRender: function() {
30593			var self = this;
30594
30595			self._super();
30596
30597			self.data(self.settings.data);
30598		},
30599
30600		/**
30601		 * Renders the control as a HTML string.
30602		 *
30603		 * @method renderHtml
30604		 * @return {String} HTML representing the control.
30605		 */
30606		renderHtml: function() {
30607			var self = this;
30608
30609			return (
30610				'<div id="' + self._id + '" class="' + self.classes() + '">' +
30611					self._getPathHtml() +
30612				'</div>'
30613			);
30614		},
30615
30616		_getPathHtml: function() {
30617			var self = this, parts = self._data || [], i, l, html = '', prefix = self.classPrefix;
30618
30619			for (i = 0, l = parts.length; i < l; i++) {
30620				html += (
30621					(i > 0 ? '<div class="'+ prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') +
30622					'<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' +
30623					i + '" tabindex="-1" id="' + self._id + '-' + i +'">' + parts[i].name + '</div>'
30624				);
30625			}
30626
30627			if (!html) {
30628				html = '<div class="' + prefix + 'path-item">&nbsp;</div>';
30629			}
30630
30631			return html;
30632		}
30633	});
30634});
30635
30636// Included from: js/tinymce/classes/ui/ElementPath.js
30637
30638/**
30639 * ElementPath.js
30640 *
30641 * Copyright, Moxiecode Systems AB
30642 * Released under LGPL License.
30643 *
30644 * License: http://www.tinymce.com/license
30645 * Contributing: http://www.tinymce.com/contributing
30646 */
30647
30648/**
30649 * This control creates an path for the current selections parent elements in TinyMCE.
30650 *
30651 * @class tinymce.ui.ElementPath
30652 * @extends tinymce.ui.Path
30653 */
30654define("tinymce/ui/ElementPath", [
30655	"tinymce/ui/Path",
30656	"tinymce/EditorManager"
30657], function(Path, EditorManager) {
30658	return Path.extend({
30659		/**
30660		 * Post render method. Called after the control has been rendered to the target.
30661		 *
30662		 * @method postRender
30663		 * @return {tinymce.ui.ElementPath} Current combobox instance.
30664		 */
30665		postRender: function() {
30666			var self = this, editor = EditorManager.activeEditor;
30667
30668			function isHidden(elm) {
30669				if (elm.nodeType === 1) {
30670					if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) {
30671						return true;
30672					}
30673
30674					if (elm.getAttribute('data-mce-type') === 'bookmark') {
30675						return true;
30676					}
30677				}
30678
30679				return false;
30680			}
30681
30682			self.on('select', function(e) {
30683				var parents = [], node, body = editor.getBody();
30684
30685				editor.focus();
30686
30687				node = editor.selection.getStart();
30688				while (node && node != body) {
30689					if (!isHidden(node)) {
30690						parents.push(node);
30691					}
30692
30693					node = node.parentNode;
30694				}
30695
30696				editor.selection.select(parents[parents.length - 1 - e.index]);
30697				editor.nodeChanged();
30698			});
30699
30700			editor.on('nodeChange', function(e) {
30701				var parents = [], selectionParents = e.parents, i = selectionParents.length;
30702
30703				while (i--) {
30704					if (selectionParents[i].nodeType == 1 && !isHidden(selectionParents[i])) {
30705						var args = editor.fire('ResolveName', {
30706							name: selectionParents[i].nodeName.toLowerCase(),
30707							target: selectionParents[i]
30708						});
30709
30710						parents.push({name: args.name});
30711					}
30712				}
30713
30714				self.data(parents);
30715			});
30716
30717			return self._super();
30718		}
30719	});
30720});
30721
30722// Included from: js/tinymce/classes/ui/FormItem.js
30723
30724/**
30725 * FormItem.js
30726 *
30727 * Copyright, Moxiecode Systems AB
30728 * Released under LGPL License.
30729 *
30730 * License: http://www.tinymce.com/license
30731 * Contributing: http://www.tinymce.com/contributing
30732 */
30733
30734/**
30735 * This class is a container created by the form element with
30736 * a label and control item.
30737 *
30738 * @class tinymce.ui.FormItem
30739 * @extends tinymce.ui.Container
30740 * @setting {String} label Label to display for the form item.
30741 */
30742define("tinymce/ui/FormItem", [
30743	"tinymce/ui/Container"
30744], function(Container) {
30745	"use strict";
30746
30747	return Container.extend({
30748		Defaults: {
30749			layout: 'flex',
30750			align: 'center',
30751			defaults: {
30752				flex: 1
30753			}
30754		},
30755
30756		/**
30757		 * Renders the control as a HTML string.
30758		 *
30759		 * @method renderHtml
30760		 * @return {String} HTML representing the control.
30761		 */
30762		renderHtml: function() {
30763			var self = this, layout = self._layout, prefix = self.classPrefix;
30764
30765			self.addClass('formitem');
30766			layout.preRender(self);
30767
30768			return (
30769				'<div id="' + self._id + '" class="' + self.classes() + '" hideFocus="1" tabIndex="-1">' +
30770					(self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' +
30771						self.settings.title + '</div>') : '') +
30772					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
30773						(self.settings.html || '') + layout.renderHtml(self) +
30774					'</div>' +
30775				'</div>'
30776			);
30777		}
30778	});
30779});
30780
30781// Included from: js/tinymce/classes/ui/Form.js
30782
30783/**
30784 * Form.js
30785 *
30786 * Copyright, Moxiecode Systems AB
30787 * Released under LGPL License.
30788 *
30789 * License: http://www.tinymce.com/license
30790 * Contributing: http://www.tinymce.com/contributing
30791 */
30792
30793/**
30794 * This class creates a form container. A form container has the ability
30795 * to automatically wrap items in tinymce.ui.FormItem instances.
30796 *
30797 * Each FormItem instance is a container for the label and the item.
30798 *
30799 * @example
30800 * tinymce.ui.Factory.create({
30801 *     type: 'form',
30802 *     items: [
30803 *         {type: 'textbox', label: 'My text box'}
30804 *     ]
30805 * }).renderTo(document.body);
30806 *
30807 * @class tinymce.ui.Form
30808 * @extends tinymce.ui.Container
30809 */
30810define("tinymce/ui/Form", [
30811	"tinymce/ui/Container",
30812	"tinymce/ui/FormItem"
30813], function(Container, FormItem) {
30814	"use strict";
30815
30816	return Container.extend({
30817		Defaults: {
30818			containerCls: 'form',
30819			layout: 'flex',
30820			direction: 'column',
30821			align: 'stretch',
30822			flex: 1,
30823			padding: 20,
30824			labelGap: 30,
30825			spacing: 10,
30826			callbacks: {
30827				submit: function() {
30828					this.submit();
30829				}
30830			}
30831		},
30832
30833		/**
30834		 * This method gets invoked before the control is rendered.
30835		 *
30836		 * @method preRender
30837		 */
30838		preRender: function() {
30839			var self = this, items = self.items();
30840
30841			// Wrap any labeled items in FormItems
30842			items.each(function(ctrl) {
30843				var formItem, label = ctrl.settings.label;
30844
30845				if (label) {
30846					formItem = new FormItem({
30847						layout: 'flex',
30848						autoResize: "overflow",
30849						defaults: {flex: 1},
30850						items: [
30851							{type: 'label', text: label, flex: 0, forId: ctrl._id}
30852						]
30853					});
30854
30855					formItem.type = 'formitem';
30856
30857					if (typeof(ctrl.settings.flex) == "undefined") {
30858						ctrl.settings.flex = 1;
30859					}
30860
30861					self.replace(ctrl, formItem);
30862					formItem.add(ctrl);
30863				}
30864			});
30865		},
30866
30867		/**
30868		 * Recalcs label widths.
30869		 *
30870		 * @private
30871		 */
30872		recalcLabels: function() {
30873			var self = this, maxLabelWidth = 0, labels = [], i, labelGap;
30874
30875			if (self.settings.labelGapCalc === false) {
30876				return;
30877			}
30878
30879			self.items().filter('formitem').each(function(item) {
30880				var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
30881
30882				maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
30883				labels.push(labelCtrl);
30884			});
30885
30886			labelGap = self.settings.labelGap || 0;
30887
30888			i = labels.length;
30889			while (i--) {
30890				labels[i].settings.minWidth = maxLabelWidth + labelGap;
30891			}
30892		},
30893
30894		/**
30895		 * Getter/setter for the visibility state.
30896		 *
30897		 * @method visible
30898		 * @param {Boolean} [state] True/false state to show/hide.
30899		 * @return {tinymce.ui.Form|Boolean} True/false state or current control.
30900		 */
30901		visible: function(state) {
30902			var val = this._super(state);
30903
30904			if (state === true && this._rendered) {
30905				this.recalcLabels();
30906			}
30907
30908			return val;
30909		},
30910
30911		/**
30912		 * Fires a submit event with the serialized form.
30913		 *
30914		 * @method submit
30915		 * @return {Object} Event arguments object.
30916		 */
30917		submit: function() {
30918			return this.fire('submit', {data: this.toJSON()});
30919		},
30920
30921		/**
30922		 * Post render method. Called after the control has been rendered to the target.
30923		 *
30924		 * @method postRender
30925		 * @return {tinymce.ui.ComboBox} Current combobox instance.
30926		 */
30927		postRender: function() {
30928			var self = this;
30929
30930			self._super();
30931			self.recalcLabels();
30932			self.fromJSON(self.settings.data);
30933		}
30934	});
30935});
30936
30937// Included from: js/tinymce/classes/ui/FieldSet.js
30938
30939/**
30940 * FieldSet.js
30941 *
30942 * Copyright, Moxiecode Systems AB
30943 * Released under LGPL License.
30944 *
30945 * License: http://www.tinymce.com/license
30946 * Contributing: http://www.tinymce.com/contributing
30947 */
30948
30949/**
30950 * This class creates fieldset containers.
30951 *
30952 * @-x-less FieldSet.less
30953 * @class tinymce.ui.FieldSet
30954 * @extends tinymce.ui.Form
30955 */
30956define("tinymce/ui/FieldSet", [
30957	"tinymce/ui/Form"
30958], function(Form) {
30959	"use strict";
30960
30961	return Form.extend({
30962		Defaults: {
30963			containerCls: 'fieldset',
30964			layout: 'flex',
30965			direction: 'column',
30966			align: 'stretch',
30967			flex: 1,
30968			padding: "25 15 5 15",
30969			labelGap: 30,
30970			spacing: 10,
30971			border: 1
30972		},
30973
30974		/**
30975		 * Renders the control as a HTML string.
30976		 *
30977		 * @method renderHtml
30978		 * @return {String} HTML representing the control.
30979		 */
30980		renderHtml: function() {
30981			var self = this, layout = self._layout, prefix = self.classPrefix;
30982
30983			self.preRender();
30984			layout.preRender(self);
30985
30986			return (
30987				'<fieldset id="' + self._id + '" class="' + self.classes() + '" hideFocus="1" tabIndex="-1">' +
30988					(self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' +
30989						self.settings.title + '</legend>') : '') +
30990					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
30991						(self.settings.html || '') + layout.renderHtml(self) +
30992					'</div>' +
30993				'</fieldset>'
30994			);
30995		}
30996	});
30997});
30998
30999// Included from: js/tinymce/classes/ui/FilePicker.js
31000
31001/**
31002 * FilePicker.js
31003 *
31004 * Copyright, Moxiecode Systems AB
31005 * Released under LGPL License.
31006 *
31007 * License: http://www.tinymce.com/license
31008 * Contributing: http://www.tinymce.com/contributing
31009 */
31010
31011/*global tinymce:true */
31012
31013/**
31014 * This class creates a file picker control.
31015 *
31016 * @class tinymce.ui.FilePicker
31017 * @extends tinymce.ui.ComboBox
31018 */
31019define("tinymce/ui/FilePicker", [
31020	"tinymce/ui/ComboBox"
31021], function(ComboBox) {
31022	"use strict";
31023
31024	return ComboBox.extend({
31025		/**
31026		 * Constructs a new control instance with the specified settings.
31027		 *
31028		 * @constructor
31029		 * @param {Object} settings Name/value object with settings.
31030		 */
31031		init: function(settings) {
31032			var self = this, editor = tinymce.activeEditor, fileBrowserCallback;
31033
31034			settings.spellcheck = false;
31035
31036			fileBrowserCallback = editor.settings.file_browser_callback;
31037			if (fileBrowserCallback) {
31038				settings.icon = 'browse';
31039
31040				settings.onaction = function() {
31041					fileBrowserCallback(
31042						self.getEl('inp').id,
31043						self.getEl('inp').value,
31044						settings.filetype,
31045						window
31046					);
31047				};
31048			}
31049
31050			self._super(settings);
31051		}
31052	});
31053});
31054
31055// Included from: js/tinymce/classes/ui/FitLayout.js
31056
31057/**
31058 * FitLayout.js
31059 *
31060 * Copyright, Moxiecode Systems AB
31061 * Released under LGPL License.
31062 *
31063 * License: http://www.tinymce.com/license
31064 * Contributing: http://www.tinymce.com/contributing
31065 */
31066
31067/**
31068 * This layout manager will resize the control to be the size of it's parent container.
31069 * In other words width: 100% and height: 100%.
31070 *
31071 * @-x-less FitLayout.less
31072 * @class tinymce.ui.FitLayout
31073 * @extends tinymce.ui.AbsoluteLayout
31074 */
31075define("tinymce/ui/FitLayout", [
31076	"tinymce/ui/AbsoluteLayout"
31077], function(AbsoluteLayout) {
31078	"use strict";
31079
31080	return AbsoluteLayout.extend({
31081		/**
31082		 * Recalculates the positions of the controls in the specified container.
31083		 *
31084		 * @method recalc
31085		 * @param {tinymce.ui.Container} container Container instance to recalc.
31086		 */
31087		recalc: function(container) {
31088			var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox();
31089
31090			container.items().filter(':visible').each(function(ctrl) {
31091				ctrl.layoutRect({
31092					x: paddingBox.left,
31093					y: paddingBox.top,
31094					w: contLayoutRect.innerW - paddingBox.right - paddingBox.left,
31095					h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom
31096				});
31097
31098				if (ctrl.recalc) {
31099					ctrl.recalc();
31100				}
31101			});
31102		}
31103	});
31104});
31105
31106// Included from: js/tinymce/classes/ui/FlexLayout.js
31107
31108/**
31109 * FlexLayout.js
31110 *
31111 * Copyright, Moxiecode Systems AB
31112 * Released under LGPL License.
31113 *
31114 * License: http://www.tinymce.com/license
31115 * Contributing: http://www.tinymce.com/contributing
31116 */
31117
31118/**
31119 * This layout manager works similar to the CSS flex box.
31120 *
31121 * @setting {String} direction row|row-reverse|column|column-reverse
31122 * @setting {Number} flex A positive-number to flex by.
31123 * @setting {String} align start|end|center|stretch
31124 * @setting {String} pack start|end|justify
31125 *
31126 * @class tinymce.ui.FlexLayout
31127 * @extends tinymce.ui.AbsoluteLayout
31128 */
31129define("tinymce/ui/FlexLayout", [
31130	"tinymce/ui/AbsoluteLayout"
31131], function(AbsoluteLayout) {
31132	"use strict";
31133
31134	return AbsoluteLayout.extend({
31135		/**
31136		 * Recalculates the positions of the controls in the specified container.
31137		 *
31138		 * @method recalc
31139		 * @param {tinymce.ui.Container} container Container instance to recalc.
31140		 */
31141		recalc: function(container) {
31142			// A ton of variables, needs to be in the same scope for performance
31143			var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
31144			var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
31145			var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, afterName, deltaSizeName, contentSizeName;
31146			var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignMaxSizeName, alignBeforeName, alignAfterName;
31147			var alignDeltaSizeName, alignContentSizeName;
31148			var max = Math.max, min = Math.min;
31149
31150			// Get container items, properties and settings
31151			items = container.items().filter(':visible');
31152			contLayoutRect = container.layoutRect();
31153			contPaddingBox = container._paddingBox;
31154			contSettings = container.settings;
31155			direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
31156			align = contSettings.align;
31157			pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
31158			spacing = contSettings.spacing || 0;
31159
31160			if (direction == "row-reversed" || direction == "column-reverse") {
31161				items = items.set(items.toArray().reverse());
31162				direction = direction.split('-')[0];
31163			}
31164
31165			// Setup axis variable name for row/column direction since the calculations is the same
31166			if (direction == "column") {
31167				posName = "y";
31168				sizeName = "h";
31169				minSizeName = "minH";
31170				maxSizeName = "maxH";
31171				innerSizeName = "innerH";
31172				beforeName = 'top';
31173				afterName = 'bottom';
31174				deltaSizeName = "deltaH";
31175				contentSizeName = "contentH";
31176
31177				alignBeforeName = "left";
31178				alignSizeName = "w";
31179				alignAxisName = "x";
31180				alignInnerSizeName = "innerW";
31181				alignMinSizeName = "minW";
31182				alignMaxSizeName = "maxW";
31183				alignAfterName = "right";
31184				alignDeltaSizeName = "deltaW";
31185				alignContentSizeName = "contentW";
31186			} else {
31187				posName = "x";
31188				sizeName = "w";
31189				minSizeName = "minW";
31190				maxSizeName = "maxW";
31191				innerSizeName = "innerW";
31192				beforeName = 'left';
31193				afterName = 'right';
31194				deltaSizeName = "deltaW";
31195				contentSizeName = "contentW";
31196
31197				alignBeforeName = "top";
31198				alignSizeName = "h";
31199				alignAxisName = "y";
31200				alignInnerSizeName = "innerH";
31201				alignMinSizeName = "minH";
31202				alignMaxSizeName = "maxH";
31203				alignAfterName = "bottom";
31204				alignDeltaSizeName = "deltaH";
31205				alignContentSizeName = "contentH";
31206			}
31207
31208			// Figure out total flex, availableSpace and collect any max size elements
31209			availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
31210			maxAlignEndPos = totalFlex = 0;
31211			for (i = 0, l = items.length; i < l; i++) {
31212				ctrl = items[i];
31213				ctrlLayoutRect = ctrl.layoutRect();
31214				ctrlSettings = ctrl.settings;
31215				flex = ctrlSettings.flex;
31216				availableSpace -= (i < l - 1 ? spacing : 0);
31217
31218				if (flex > 0) {
31219					totalFlex += flex;
31220
31221					// Flexed item has a max size then we need to check if we will hit that size
31222					if (ctrlLayoutRect[maxSizeName]) {
31223						maxSizeItems.push(ctrl);
31224					}
31225
31226					ctrlLayoutRect.flex = flex;
31227				}
31228
31229				availableSpace -= ctrlLayoutRect[minSizeName];
31230
31231				// Calculate the align end position to be used to check for overflow/underflow
31232				size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
31233				if (size > maxAlignEndPos) {
31234					maxAlignEndPos = size;
31235				}
31236			}
31237
31238			// Calculate minW/minH
31239			rect = {};
31240			if (availableSpace < 0) {
31241				rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
31242			} else {
31243				rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
31244			}
31245
31246			rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
31247
31248			rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
31249			rect[alignContentSizeName] = maxAlignEndPos;
31250			rect.minW = min(rect.minW, contLayoutRect.maxW);
31251			rect.minH = min(rect.minH, contLayoutRect.maxH);
31252			rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
31253			rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
31254
31255			// Resize container container if minSize was changed
31256			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
31257				rect.w = rect.minW;
31258				rect.h = rect.minH;
31259
31260				container.layoutRect(rect);
31261				this.recalc(container);
31262
31263				// Forced recalc for example if items are hidden/shown
31264				if (container._lastRect === null) {
31265					var parentCtrl = container.parent();
31266					if (parentCtrl) {
31267						parentCtrl._lastRect = null;
31268						parentCtrl.recalc();
31269					}
31270				}
31271
31272				return;
31273			}
31274
31275			// Handle max size elements, check if they will become to wide with current options
31276			ratio = availableSpace / totalFlex;
31277			for (i = 0, l = maxSizeItems.length; i < l; i++) {
31278				ctrl = maxSizeItems[i];
31279				ctrlLayoutRect = ctrl.layoutRect();
31280				maxSize = ctrlLayoutRect[maxSizeName];
31281				size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
31282
31283				if (size > maxSize) {
31284					availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
31285					totalFlex -= ctrlLayoutRect.flex;
31286					ctrlLayoutRect.flex = 0;
31287					ctrlLayoutRect.maxFlexSize = maxSize;
31288				} else {
31289					ctrlLayoutRect.maxFlexSize = 0;
31290				}
31291			}
31292
31293			// Setup new ratio, target layout rect, start position
31294			ratio = availableSpace / totalFlex;
31295			pos = contPaddingBox[beforeName];
31296			rect = {};
31297
31298			// Handle pack setting moves the start position to end, center
31299			if (totalFlex === 0) {
31300				if (pack == "end") {
31301					pos = availableSpace + contPaddingBox[beforeName];
31302				} else if (pack == "center") {
31303					pos = Math.round(
31304						(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
31305					) + contPaddingBox[beforeName];
31306
31307					if (pos < 0) {
31308						pos = contPaddingBox[beforeName];
31309					}
31310				} else if (pack == "justify") {
31311					pos = contPaddingBox[beforeName];
31312					spacing = Math.floor(availableSpace / (items.length - 1));
31313				}
31314			}
31315
31316			// Default aligning (start) the other ones needs to be calculated while doing the layout
31317			rect[alignAxisName] = contPaddingBox[alignBeforeName];
31318
31319			// Start laying out controls
31320			for (i = 0, l = items.length; i < l; i++) {
31321				ctrl = items[i];
31322				ctrlLayoutRect = ctrl.layoutRect();
31323				size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
31324
31325				// Align the control on the other axis
31326				if (align === "center") {
31327					rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
31328				} else if (align === "stretch") {
31329					rect[alignSizeName] = max(
31330						ctrlLayoutRect[alignMinSizeName] || 0,
31331						contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
31332					);
31333					rect[alignAxisName] = contPaddingBox[alignBeforeName];
31334				} else if (align === "end") {
31335					rect[alignAxisName] = contLayoutRect[alignInnerSizeName]  - ctrlLayoutRect[alignSizeName]  - contPaddingBox.top;
31336				}
31337
31338				// Calculate new size based on flex
31339				if (ctrlLayoutRect.flex > 0) {
31340					size += ctrlLayoutRect.flex * ratio;
31341				}
31342
31343				rect[sizeName] = size;
31344				rect[posName] = pos;
31345				ctrl.layoutRect(rect);
31346
31347				// Recalculate containers
31348				if (ctrl.recalc) {
31349					ctrl.recalc();
31350				}
31351
31352				// Move x/y position
31353				pos += size + spacing;
31354			}
31355		}
31356	});
31357});
31358
31359// Included from: js/tinymce/classes/ui/FlowLayout.js
31360
31361/**
31362 * FlowLayout.js
31363 *
31364 * Copyright, Moxiecode Systems AB
31365 * Released under LGPL License.
31366 *
31367 * License: http://www.tinymce.com/license
31368 * Contributing: http://www.tinymce.com/contributing
31369 */
31370
31371/**
31372 * This layout manager will place the controls by using the browsers native layout.
31373 *
31374 * @-x-less FlowLayout.less
31375 * @class tinymce.ui.FlowLayout
31376 * @extends tinymce.ui.Layout
31377 */
31378define("tinymce/ui/FlowLayout", [
31379	"tinymce/ui/Layout"
31380], function(Layout) {
31381	return Layout.extend({
31382		Defaults: {
31383			containerClass: 'flow-layout',
31384			controlClass: 'flow-layout-item',
31385			endClass : 'break'
31386		},
31387
31388		/**
31389		 * Recalculates the positions of the controls in the specified container.
31390		 *
31391		 * @method recalc
31392		 * @param {tinymce.ui.Container} container Container instance to recalc.
31393		 */
31394		recalc: function(container) {
31395			container.items().filter(':visible').each(function(ctrl) {
31396				if (ctrl.recalc) {
31397					ctrl.recalc();
31398				}
31399			});
31400		}
31401	});
31402});
31403
31404// Included from: js/tinymce/classes/ui/FormatControls.js
31405
31406/**
31407 * FormatControls.js
31408 *
31409 * Copyright, Moxiecode Systems AB
31410 * Released under LGPL License.
31411 *
31412 * License: http://www.tinymce.com/license
31413 * Contributing: http://www.tinymce.com/contributing
31414 */
31415
31416/**
31417 * Internal class containing all TinyMCE specific control types such as
31418 * format listboxes, fontlist boxes, toolbar buttons etc.
31419 *
31420 * @class tinymce.ui.FormatControls
31421 */
31422define("tinymce/ui/FormatControls", [
31423	"tinymce/ui/Control",
31424	"tinymce/ui/Widget",
31425	"tinymce/ui/FloatPanel",
31426	"tinymce/util/Tools",
31427	"tinymce/EditorManager",
31428	"tinymce/Env"
31429], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
31430	var each = Tools.each;
31431
31432	EditorManager.on('AddEditor', function(e) {
31433		if (e.editor.rtl) {
31434			Control.rtl = true;
31435		}
31436
31437		registerControls(e.editor);
31438	});
31439
31440	Control.translate = function(text) {
31441		return EditorManager.translate(text);
31442	};
31443
31444	Widget.tooltips = !Env.iOS;
31445
31446	function registerControls(editor) {
31447		var formatMenu;
31448
31449		// Generates a preview for a format
31450		function getPreviewCss(format) {
31451			var name, previewElm, dom = editor.dom;
31452			var previewCss = '', parentFontSize, previewStyles;
31453
31454			previewStyles = editor.settings.preview_styles;
31455
31456			// No preview forced
31457			if (previewStyles === false) {
31458				return '';
31459			}
31460
31461			// Default preview
31462			if (!previewStyles) {
31463				previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
31464					'text-transform color background-color border border-radius';
31465			}
31466
31467			// Removes any variables since these can't be previewed
31468			function removeVars(val) {
31469				return val.replace(/%(\w+)/g, '');
31470			}
31471
31472			// Create block/inline element to use for preview
31473			format = editor.formatter.get(format);
31474			if (!format) {
31475				return;
31476			}
31477
31478			format = format[0];
31479			name = format.block || format.inline || 'span';
31480			previewElm = dom.create(name);
31481
31482			// Add format styles to preview element
31483			each(format.styles, function(value, name) {
31484				value = removeVars(value);
31485
31486				if (value) {
31487					dom.setStyle(previewElm, name, value);
31488				}
31489			});
31490
31491			// Add attributes to preview element
31492			each(format.attributes, function(value, name) {
31493				value = removeVars(value);
31494
31495				if (value) {
31496					dom.setAttrib(previewElm, name, value);
31497				}
31498			});
31499
31500			// Add classes to preview element
31501			each(format.classes, function(value) {
31502				value = removeVars(value);
31503
31504				if (!dom.hasClass(previewElm, value)) {
31505					dom.addClass(previewElm, value);
31506				}
31507			});
31508
31509			editor.fire('PreviewFormats');
31510
31511			// Add the previewElm outside the visual area
31512			dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
31513			editor.getBody().appendChild(previewElm);
31514
31515			// Get parent container font size so we can compute px values out of em/% for older IE:s
31516			parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
31517			parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
31518
31519			each(previewStyles.split(' '), function(name) {
31520				var value = dom.getStyle(previewElm, name, true);
31521
31522				// If background is transparent then check if the body has a background color we can use
31523				if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
31524					value = dom.getStyle(editor.getBody(), name, true);
31525
31526					// Ignore white since it's the default color, not the nicest fix
31527					// TODO: Fix this by detecting runtime style
31528					if (dom.toHex(value).toLowerCase() == '#ffffff') {
31529						return;
31530					}
31531				}
31532
31533				if (name == 'color') {
31534					// Ignore black since it's the default color, not the nicest fix
31535					// TODO: Fix this by detecting runtime style
31536					if (dom.toHex(value).toLowerCase() == '#000000') {
31537						return;
31538					}
31539				}
31540
31541				// Old IE won't calculate the font size so we need to do that manually
31542				if (name == 'font-size') {
31543					if (/em|%$/.test(value)) {
31544						if (parentFontSize === 0) {
31545							return;
31546						}
31547
31548						// Convert font size from em/% to px
31549						value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
31550						value = (value * parentFontSize) + 'px';
31551					}
31552				}
31553
31554				if (name == "border" && value) {
31555					previewCss += 'padding:0 2px;';
31556				}
31557
31558				previewCss += name + ':' + value + ';';
31559			});
31560
31561			editor.fire('AfterPreviewFormats');
31562
31563			//previewCss += 'line-height:normal';
31564
31565			dom.remove(previewElm);
31566
31567			return previewCss;
31568		}
31569
31570		function createListBoxChangeHandler(items, formatName) {
31571			return function() {
31572				var self = this;
31573
31574				editor.on('nodeChange', function(e) {
31575					var formatter = editor.formatter;
31576					var value = null;
31577
31578					each(e.parents, function(node) {
31579						each(items, function(item) {
31580							if (formatName) {
31581								if (formatter.matchNode(node, formatName, {value: item.value})) {
31582									value = item.value;
31583								}
31584							} else {
31585								if (formatter.matchNode(node, item.value)) {
31586									value = item.value;
31587								}
31588							}
31589
31590							if (value) {
31591								return false;
31592							}
31593						});
31594
31595						if (value) {
31596							return false;
31597						}
31598					});
31599
31600					self.value(value);
31601				});
31602			};
31603		}
31604
31605		function createFormats(formats) {
31606			formats = formats.split(';');
31607
31608			var i = formats.length;
31609			while (i--) {
31610				formats[i] = formats[i].split('=');
31611			}
31612
31613			return formats;
31614		}
31615
31616		function createFormatMenu() {
31617			var count = 0, newFormats = [];
31618
31619			var defaultStyleFormats = [
31620				{title: 'Headers', items: [
31621					{title: 'Header 1', format: 'h1'},
31622					{title: 'Header 2', format: 'h2'},
31623					{title: 'Header 3', format: 'h3'},
31624					{title: 'Header 4', format: 'h4'},
31625					{title: 'Header 5', format: 'h5'},
31626					{title: 'Header 6', format: 'h6'}
31627				]},
31628
31629				{title: 'Inline', items: [
31630					{title: 'Bold', icon: 'bold', format: 'bold'},
31631					{title: 'Italic', icon: 'italic', format: 'italic'},
31632					{title: 'Underline', icon: 'underline', format: 'underline'},
31633					{title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
31634					{title: 'Superscript', icon: 'superscript', format: 'superscript'},
31635					{title: 'Subscript', icon: 'subscript', format: 'subscript'},
31636					{title: 'Code', icon: 'code', format: 'code'}
31637				]},
31638
31639				{title: 'Blocks', items: [
31640					{title: 'Paragraph', format: 'p'},
31641					{title: 'Blockquote', format: 'blockquote'},
31642					{title: 'Div', format: 'div'},
31643					{title: 'Pre', format: 'pre'}
31644				]},
31645
31646				{title: 'Alignment', items: [
31647					{title: 'Left', icon: 'alignleft', format: 'alignleft'},
31648					{title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
31649					{title: 'Right', icon: 'alignright', format: 'alignright'},
31650					{title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
31651				]}
31652			];
31653
31654			function createMenu(formats) {
31655				var menu = [];
31656
31657				if (!formats) {
31658					return;
31659				}
31660
31661				each(formats, function(format) {
31662					var menuItem = {
31663						text: format.title,
31664						icon: format.icon
31665					};
31666
31667					if (format.items) {
31668						menuItem.menu = createMenu(format.items);
31669					} else {
31670						var formatName = format.format || "custom" + count++;
31671
31672						if (!format.format) {
31673							format.name = formatName;
31674							newFormats.push(format);
31675						}
31676
31677						menuItem.format = formatName;
31678					}
31679
31680					menu.push(menuItem);
31681				});
31682
31683				return menu;
31684			}
31685
31686			editor.on('init', function() {
31687				each(newFormats, function(format) {
31688					editor.formatter.register(format.name, format);
31689				});
31690			});
31691
31692			var menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
31693
31694			menu = {
31695				type: 'menu',
31696				items: menu,
31697				onPostRender: function(e) {
31698					editor.fire('renderFormatsMenu', {control: e.control});
31699				},
31700				itemDefaults: {
31701					preview: true,
31702
31703					textStyle: function() {
31704						if (this.settings.format) {
31705							return getPreviewCss(this.settings.format);
31706						}
31707					},
31708
31709					onPostRender: function() {
31710						var self = this, formatName = this.settings.format;
31711
31712						if (formatName) {
31713							self.parent().on('show', function() {
31714								self.disabled(!editor.formatter.canApply(formatName));
31715								self.active(editor.formatter.match(formatName));
31716							});
31717						}
31718					},
31719
31720					onclick: function() {
31721						if (this.settings.format) {
31722							toggleFormat(this.settings.format);
31723						}
31724					}
31725				}
31726			};
31727
31728			return menu;
31729		}
31730
31731		formatMenu = createFormatMenu();
31732
31733		// Simple format controls <control/format>:<UI text>
31734		each({
31735			bold: 'Bold',
31736			italic: 'Italic',
31737			underline: 'Underline',
31738			strikethrough: 'Strikethrough',
31739			subscript: 'Subscript',
31740			superscript: 'Superscript'
31741		}, function(text, name) {
31742			editor.addButton(name, {
31743				tooltip: text,
31744				onPostRender: function() {
31745					var self = this;
31746
31747					// TODO: Fix this
31748					if (editor.formatter) {
31749						editor.formatter.formatChanged(name, function(state) {
31750							self.active(state);
31751						});
31752					} else {
31753						editor.on('init', function() {
31754							editor.formatter.formatChanged(name, function(state) {
31755								self.active(state);
31756							});
31757						});
31758					}
31759				},
31760				onclick: function() {
31761					toggleFormat(name);
31762				}
31763			});
31764		});
31765
31766		// Simple command controls <control>:[<UI text>,<Command>]
31767		each({
31768			outdent: ['Decrease indent', 'Outdent'],
31769			indent: ['Increase indent', 'Indent'],
31770			cut: ['Cut', 'Cut'],
31771			copy: ['Copy', 'Copy'],
31772			paste: ['Paste', 'Paste'],
31773			help: ['Help', 'mceHelp'],
31774			selectall: ['Select all', 'SelectAll'],
31775			hr: ['Insert horizontal rule', 'InsertHorizontalRule'],
31776			removeformat: ['Clear formatting', 'RemoveFormat'],
31777			visualaid: ['Visual aids', 'mceToggleVisualAid'],
31778			newdocument: ['New document', 'mceNewDocument']
31779		}, function(item, name) {
31780			editor.addButton(name, {
31781				tooltip: item[0],
31782				cmd: item[1]
31783			});
31784		});
31785
31786		// Simple command controls with format state
31787		each({
31788			blockquote: ['Toggle blockquote', 'mceBlockQuote'],
31789			numlist: ['Numbered list', 'InsertOrderedList'],
31790			bullist: ['Bullet list', 'InsertUnorderedList'],
31791			subscript: ['Subscript', 'Subscript'],
31792			superscript: ['Superscript', 'Superscript'],
31793			alignleft: ['Align left', 'JustifyLeft'],
31794			aligncenter: ['Align center', 'JustifyCenter'],
31795			alignright: ['Align right', 'JustifyRight'],
31796			alignjustify: ['Justify', 'JustifyFull']
31797		}, function(item, name) {
31798			editor.addButton(name, {
31799				tooltip: item[0],
31800				cmd: item[1],
31801				onPostRender: function() {
31802					var self = this;
31803
31804					// TODO: Fix this
31805					if (editor.formatter) {
31806						editor.formatter.formatChanged(name, function(state) {
31807							self.active(state);
31808						});
31809					} else {
31810						editor.on('init', function() {
31811							editor.formatter.formatChanged(name, function(state) {
31812								self.active(state);
31813							});
31814						});
31815					}
31816				}
31817			});
31818		});
31819
31820		function hasUndo() {
31821			return editor.undoManager ? editor.undoManager.hasUndo() : false;
31822		}
31823
31824		function hasRedo() {
31825			return editor.undoManager ? editor.undoManager.hasRedo() : false;
31826		}
31827
31828		function toggleUndoState() {
31829			var self = this;
31830
31831			self.disabled(!hasUndo());
31832			editor.on('Undo Redo AddUndo TypingUndo', function() {
31833				self.disabled(!hasUndo());
31834			});
31835		}
31836
31837		function toggleRedoState() {
31838			var self = this;
31839
31840			self.disabled(!hasRedo());
31841			editor.on('Undo Redo AddUndo TypingUndo', function() {
31842				self.disabled(!hasRedo());
31843			});
31844		}
31845
31846		function toggleVisualAidState() {
31847			var self = this;
31848
31849			editor.on('VisualAid', function(e) {
31850				self.active(e.hasVisual);
31851			});
31852
31853			self.active(editor.hasVisual);
31854		}
31855
31856		editor.addButton('undo', {
31857			tooltip: 'Undo',
31858			onPostRender: toggleUndoState,
31859			cmd: 'undo'
31860		});
31861
31862		editor.addButton('redo', {
31863			tooltip: 'Redo',
31864			onPostRender: toggleRedoState,
31865			cmd: 'redo'
31866		});
31867
31868		editor.addMenuItem('newdocument', {
31869			text: 'New document',
31870			shortcut: 'Ctrl+N',
31871			icon: 'newdocument',
31872			cmd: 'mceNewDocument'
31873		});
31874
31875		editor.addMenuItem('undo', {
31876			text: 'Undo',
31877			icon: 'undo',
31878			shortcut: 'Ctrl+Z',
31879			onPostRender: toggleUndoState,
31880			cmd: 'undo'
31881		});
31882
31883		editor.addMenuItem('redo', {
31884			text: 'Redo',
31885			icon: 'redo',
31886			shortcut: 'Ctrl+Y',
31887			onPostRender: toggleRedoState,
31888			cmd: 'redo'
31889		});
31890
31891		editor.addMenuItem('visualaid', {
31892			text: 'Visual aids',
31893			selectable: true,
31894			onPostRender: toggleVisualAidState,
31895			cmd: 'mceToggleVisualAid'
31896		});
31897
31898		each({
31899			cut: ['Cut', 'Cut', 'Ctrl+X'],
31900			copy: ['Copy', 'Copy', 'Ctrl+C'],
31901			paste: ['Paste', 'Paste', 'Ctrl+V'],
31902			selectall: ['Select all', 'SelectAll', 'Ctrl+A'],
31903			bold: ['Bold', 'Bold', 'Ctrl+B'],
31904			italic: ['Italic', 'Italic', 'Ctrl+I'],
31905			underline: ['Underline', 'Underline'],
31906			strikethrough: ['Strikethrough', 'Strikethrough'],
31907			subscript: ['Subscript', 'Subscript'],
31908			superscript: ['Superscript', 'Superscript'],
31909			removeformat: ['Clear formatting', 'RemoveFormat']
31910		}, function(item, name) {
31911			editor.addMenuItem(name, {
31912				text: item[0],
31913				icon: name,
31914				shortcut: item[2],
31915				cmd: item[1]
31916			});
31917		});
31918
31919		editor.on('mousedown', function() {
31920			FloatPanel.hideAll();
31921		});
31922
31923		function toggleFormat(fmt) {
31924			if (fmt.control) {
31925				fmt = fmt.control.value();
31926			}
31927
31928			if (fmt) {
31929				editor.execCommand('mceToggleFormat', false, fmt);
31930			}
31931		}
31932
31933		editor.addButton('styleselect', {
31934			type: 'menubutton',
31935			text: 'Formats',
31936			menu: formatMenu
31937		});
31938
31939		editor.addButton('formatselect', function() {
31940			var items = [], blocks = createFormats(editor.settings.block_formats ||
31941				'Paragraph=p;' +
31942				'Address=address;' +
31943				'Pre=pre;' +
31944				'Header 1=h1;' +
31945				'Header 2=h2;' +
31946				'Header 3=h3;' +
31947				'Header 4=h4;' +
31948				'Header 5=h5;' +
31949				'Header 6=h6'
31950			);
31951
31952			each(blocks, function(block) {
31953				items.push({
31954					text: block[0],
31955					value: block[1],
31956					textStyle: function() {
31957						return getPreviewCss(block[1]);
31958					}
31959				});
31960			});
31961
31962			return {
31963				type: 'listbox',
31964				text: blocks[0][0],
31965				values: items,
31966				fixedWidth: true,
31967				onselect: toggleFormat,
31968				onPostRender: createListBoxChangeHandler(items)
31969			};
31970		});
31971
31972		editor.addButton('fontselect', function() {
31973			var defaultFontsFormats =
31974				'Andale Mono=andale mono,times;' +
31975				'Arial=arial,helvetica,sans-serif;' +
31976				'Arial Black=arial black,avant garde;' +
31977				'Book Antiqua=book antiqua,palatino;' +
31978				'Comic Sans MS=comic sans ms,sans-serif;' +
31979				'Courier New=courier new,courier;' +
31980				'Georgia=georgia,palatino;' +
31981				'Helvetica=helvetica;' +
31982				'Impact=impact,chicago;' +
31983				'Symbol=symbol;' +
31984				'Tahoma=tahoma,arial,helvetica,sans-serif;' +
31985				'Terminal=terminal,monaco;' +
31986				'Times New Roman=times new roman,times;' +
31987				'Trebuchet MS=trebuchet ms,geneva;' +
31988				'Verdana=verdana,geneva;' +
31989				'Webdings=webdings;' +
31990				'Wingdings=wingdings,zapf dingbats';
31991
31992			var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
31993
31994			each(fonts, function(font) {
31995				items.push({
31996					text: {raw: font[0]},
31997					value: font[1],
31998					textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
31999				});
32000			});
32001
32002			return {
32003				type: 'listbox',
32004				text: 'Font Family',
32005				tooltip: 'Font Family',
32006				values: items,
32007				fixedWidth: true,
32008				onPostRender: createListBoxChangeHandler(items, 'fontname'),
32009				onselect: function(e) {
32010					if (e.control.settings.value) {
32011						editor.execCommand('FontName', false, e.control.settings.value);
32012					}
32013				}
32014			};
32015		});
32016
32017		editor.addButton('fontsizeselect', function() {
32018			var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
32019			var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
32020
32021			each(fontsize_formats.split(' '), function(item) {
32022				items.push({text: item, value: item});
32023			});
32024
32025			return {
32026				type: 'listbox',
32027				text: 'Font Sizes',
32028				tooltip: 'Font Sizes',
32029				values: items,
32030				fixedWidth: true,
32031				onPostRender: createListBoxChangeHandler(items, 'fontsize'),
32032				onclick: function(e) {
32033					if (e.control.settings.value) {
32034						editor.execCommand('FontSize', false, e.control.settings.value);
32035					}
32036				}
32037			};
32038		});
32039
32040		editor.addMenuItem('formats', {
32041			text: 'Formats',
32042			menu: formatMenu
32043		});
32044	}
32045});
32046
32047// Included from: js/tinymce/classes/ui/GridLayout.js
32048
32049/**
32050 * GridLayout.js
32051 *
32052 * Copyright, Moxiecode Systems AB
32053 * Released under LGPL License.
32054 *
32055 * License: http://www.tinymce.com/license
32056 * Contributing: http://www.tinymce.com/contributing
32057 */
32058
32059/**
32060 * This layout manager places controls in a grid.
32061 *
32062 * @setting {Number} spacing Spacing between controls.
32063 * @setting {Number} spacingH Horizontal spacing between controls.
32064 * @setting {Number} spacingV Vertical spacing between controls.
32065 * @setting {Number} columns Number of columns to use.
32066 * @setting {String/Array} alignH start|end|center|stretch or array of values for each column.
32067 * @setting {String/Array} alignV start|end|center|stretch or array of values for each column.
32068 * @setting {String} pack start|end
32069 *
32070 * @class tinymce.ui.GridLayout
32071 * @extends tinymce.ui.AbsoluteLayout
32072 */
32073define("tinymce/ui/GridLayout", [
32074	"tinymce/ui/AbsoluteLayout"
32075], function(AbsoluteLayout) {
32076	"use strict";
32077
32078	return AbsoluteLayout.extend({
32079		/**
32080		 * Recalculates the positions of the controls in the specified container.
32081		 *
32082		 * @method recalc
32083		 * @param {tinymce.ui.Container} container Container instance to recalc.
32084		 */
32085		recalc: function(container) {
32086			var settings = container.settings, rows, cols, items, contLayoutRect, width, height, rect,
32087				ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY,
32088				colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, alignX, alignY, availableWidth, availableHeight;
32089
32090			// Get layout settings
32091			settings = container.settings;
32092			items = container.items().filter(':visible');
32093			contLayoutRect = container.layoutRect();
32094			cols = settings.columns || Math.ceil(Math.sqrt(items.length));
32095			rows = Math.ceil(items.length / cols);
32096			spacingH = settings.spacingH || settings.spacing || 0;
32097			spacingV = settings.spacingV || settings.spacing || 0;
32098			alignH = settings.alignH || settings.align;
32099			alignV = settings.alignV || settings.align;
32100			contPaddingBox = container._paddingBox;
32101
32102			if (alignH && typeof(alignH) == "string") {
32103				alignH = [alignH];
32104			}
32105
32106			if (alignV && typeof(alignV) == "string") {
32107				alignV = [alignV];
32108			}
32109
32110			// Zero padd columnWidths
32111			for (x = 0; x < cols; x++) {
32112				colWidths.push(0);
32113			}
32114
32115			// Zero padd rowHeights
32116			for (y = 0; y < rows; y++) {
32117				rowHeights.push(0);
32118			}
32119
32120			// Calculate columnWidths and rowHeights
32121			for (y = 0; y < rows; y++) {
32122				for (x = 0; x < cols; x++) {
32123					ctrl = items[y * cols + x];
32124
32125					// Out of bounds
32126					if (!ctrl) {
32127						break;
32128					}
32129
32130					ctrlLayoutRect = ctrl.layoutRect();
32131					ctrlMinWidth = ctrlLayoutRect.minW;
32132					ctrlMinHeight = ctrlLayoutRect.minH;
32133
32134					colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x];
32135					rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y];
32136				}
32137			}
32138
32139			// Calculate maxX
32140			availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right;
32141			for (maxX = 0, x = 0; x < cols; x++) {
32142				maxX += colWidths[x] + (x > 0 ? spacingH : 0);
32143				availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x];
32144			}
32145
32146			// Calculate maxY
32147			availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom;
32148			for (maxY = 0, y = 0; y < rows; y++) {
32149				maxY += rowHeights[y] + (y > 0 ? spacingV : 0);
32150				availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y];
32151			}
32152
32153			maxX += contPaddingBox.left + contPaddingBox.right;
32154			maxY += contPaddingBox.top + contPaddingBox.bottom;
32155
32156			// Calculate minW/minH
32157			rect = {};
32158			rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW);
32159			rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH);
32160
32161			rect.contentW = rect.minW - contLayoutRect.deltaW;
32162			rect.contentH = rect.minH - contLayoutRect.deltaH;
32163			rect.minW = Math.min(rect.minW, contLayoutRect.maxW);
32164			rect.minH = Math.min(rect.minH, contLayoutRect.maxH);
32165			rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth);
32166			rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight);
32167
32168			// Resize container container if minSize was changed
32169			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
32170				rect.w = rect.minW;
32171				rect.h = rect.minH;
32172
32173				container.layoutRect(rect);
32174				this.recalc(container);
32175
32176				// Forced recalc for example if items are hidden/shown
32177				if (container._lastRect === null) {
32178					var parentCtrl = container.parent();
32179					if (parentCtrl) {
32180						parentCtrl._lastRect = null;
32181						parentCtrl.recalc();
32182					}
32183				}
32184
32185				return;
32186			}
32187
32188			// Update contentW/contentH so absEnd moves correctly
32189			if (contLayoutRect.autoResize) {
32190				rect = container.layoutRect(rect);
32191				rect.contentW = rect.minW - contLayoutRect.deltaW;
32192				rect.contentH = rect.minH - contLayoutRect.deltaH;
32193			}
32194
32195			var flexV;
32196
32197			if (settings.packV == 'start') {
32198				flexV = 0;
32199			} else {
32200				flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0;
32201			}
32202
32203			// Calculate totalFlex
32204			var totalFlex = 0;
32205			var flexWidths = settings.flexWidths;
32206			if (flexWidths) {
32207				for (x = 0; x < flexWidths.length; x++) {
32208					totalFlex += flexWidths[x];
32209				}
32210			} else {
32211				totalFlex = cols;
32212			}
32213
32214			// Calculate new column widths based on flex values
32215			var ratio = availableWidth / totalFlex;
32216			for (x = 0; x < cols; x++) {
32217				colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
32218			}
32219
32220			// Move/resize controls
32221			posY = contPaddingBox.top;
32222			for (y = 0; y < rows; y++) {
32223				posX = contPaddingBox.left;
32224				height = rowHeights[y] + flexV;
32225
32226				for (x = 0; x < cols; x++) {
32227					ctrl = items[y * cols + x];
32228
32229					// No more controls to render then break
32230					if (!ctrl) {
32231						break;
32232					}
32233
32234					// Get control settings and calculate x, y
32235					ctrlSettings = ctrl.settings;
32236					ctrlLayoutRect = ctrl.layoutRect();
32237					width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth);
32238					alignX = alignY = 0;
32239					ctrlLayoutRect.x = posX;
32240					ctrlLayoutRect.y = posY;
32241
32242					// Align control horizontal
32243					align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null);
32244					if (align == "center") {
32245						ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2);
32246					} else if (align == "right") {
32247						ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w;
32248					} else if (align == "stretch") {
32249						ctrlLayoutRect.w = width;
32250					}
32251
32252					// Align control vertical
32253					align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null);
32254					if (align == "center") {
32255						ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2);
32256					} else  if (align == "bottom") {
32257						ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h;
32258					} else if (align == "stretch") {
32259						ctrlLayoutRect.h = height;
32260					}
32261
32262					ctrl.layoutRect(ctrlLayoutRect);
32263
32264					posX += width + spacingH;
32265
32266					if (ctrl.recalc) {
32267						ctrl.recalc();
32268					}
32269				}
32270
32271				posY += height + spacingV;
32272			}
32273		}
32274	});
32275});
32276
32277// Included from: js/tinymce/classes/ui/Iframe.js
32278
32279/**
32280 * Iframe.js
32281 *
32282 * Copyright, Moxiecode Systems AB
32283 * Released under LGPL License.
32284 *
32285 * License: http://www.tinymce.com/license
32286 * Contributing: http://www.tinymce.com/contributing
32287 */
32288
32289/*jshint scripturl:true */
32290
32291/**
32292 * This class creates an iframe.
32293 *
32294 * @setting {String} url Url to open in the iframe.
32295 *
32296 * @-x-less Iframe.less
32297 * @class tinymce.ui.Iframe
32298 * @extends tinymce.ui.Widget
32299 */
32300define("tinymce/ui/Iframe", [
32301	"tinymce/ui/Widget"
32302], function(Widget) {
32303	"use strict";
32304
32305	return Widget.extend({
32306		/**
32307		 * Renders the control as a HTML string.
32308		 *
32309		 * @method renderHtml
32310		 * @return {String} HTML representing the control.
32311		 */
32312		renderHtml: function() {
32313			var self = this;
32314
32315			self.addClass('iframe');
32316			self.canFocus = false;
32317
32318			return (
32319				'<iframe id="' + self._id + '" class="' + self.classes() + '" tabindex="-1" src="' +
32320				(self.settings.url || "javascript:\'\'") + '" frameborder="0"></iframe>'
32321			);
32322		},
32323
32324		/**
32325		 * Setter for the iframe source.
32326		 *
32327		 * @method src
32328		 * @param {String} src Source URL for iframe.
32329		 */
32330		src: function(src) {
32331			this.getEl().src = src;
32332		},
32333
32334		/**
32335		 * Inner HTML for the iframe.
32336		 *
32337		 * @method html
32338		 * @param {String} html HTML string to set as HTML inside the iframe.
32339		 * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
32340		 * @return {tinymce.ui.Iframe} Current iframe control.
32341		 */
32342		html: function(html, callback) {
32343			var self = this, body = this.getEl().contentWindow.document.body;
32344
32345			// Wait for iframe to initialize IE 10 takes time
32346			if (!body) {
32347				setTimeout(function() {
32348					self.html(html);
32349				}, 0);
32350			} else {
32351				body.innerHTML = html;
32352
32353				if (callback) {
32354					callback();
32355				}
32356			}
32357
32358			return this;
32359		}
32360	});
32361});
32362
32363// Included from: js/tinymce/classes/ui/Label.js
32364
32365/**
32366 * Label.js
32367 *
32368 * Copyright, Moxiecode Systems AB
32369 * Released under LGPL License.
32370 *
32371 * License: http://www.tinymce.com/license
32372 * Contributing: http://www.tinymce.com/contributing
32373 */
32374
32375/**
32376 * This class creates a label element. A label is a simple text control
32377 * that can be bound to other controls.
32378 *
32379 * @-x-less Label.less
32380 * @class tinymce.ui.Label
32381 * @extends tinymce.ui.Widget
32382 */
32383define("tinymce/ui/Label", [
32384	"tinymce/ui/Widget",
32385	"tinymce/ui/DomUtils"
32386], function(Widget, DomUtils) {
32387	"use strict";
32388
32389	return Widget.extend({
32390		/**
32391		 * Constructs a instance with the specified settings.
32392		 *
32393		 * @constructor
32394		 * @param {Object} settings Name/value object with settings.
32395		 * @param {Boolean} multiline Multiline label.
32396		 */
32397		init: function(settings) {
32398			var self = this;
32399
32400			self._super(settings);
32401			self.addClass('widget');
32402			self.addClass('label');
32403			self.canFocus = false;
32404
32405			if (settings.multiline) {
32406				self.addClass('autoscroll');
32407			}
32408
32409			if (settings.strong) {
32410				self.addClass('strong');
32411			}
32412		},
32413
32414		/**
32415		 * Initializes the current controls layout rect.
32416		 * This will be executed by the layout managers to determine the
32417		 * default minWidth/minHeight etc.
32418		 *
32419		 * @method initLayoutRect
32420		 * @return {Object} Layout rect instance.
32421		 */
32422		initLayoutRect: function() {
32423			var self = this, layoutRect = self._super();
32424
32425			if (self.settings.multiline) {
32426				var size = DomUtils.getSize(self.getEl());
32427
32428				// Check if the text fits within maxW if not then try word wrapping it
32429				if (size.width > layoutRect.maxW) {
32430					layoutRect.minW = layoutRect.maxW;
32431					self.addClass('multiline');
32432				}
32433
32434				self.getEl().style.width = layoutRect.minW + 'px';
32435				layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height);
32436			}
32437
32438			return layoutRect;
32439		},
32440
32441		/**
32442		 * Repaints the control after a layout operation.
32443		 *
32444		 * @method repaint
32445		 */
32446		repaint: function() {
32447			var self = this;
32448
32449			if (!self.settings.multiline) {
32450				self.getEl().style.lineHeight = self.layoutRect().h + 'px';
32451			}
32452
32453			return self._super();
32454		},
32455
32456		/**
32457		 * Sets/gets the current label text.
32458		 *
32459		 * @method text
32460		 * @param {String} [text] New label text.
32461		 * @return {String|tinymce.ui.Label} Current text or current label instance.
32462		 */
32463		text: function(text) {
32464			var self = this;
32465
32466			if (self._rendered && text) {
32467				this.innerHtml(self.encode(text));
32468			}
32469
32470			return self._super(text);
32471		},
32472
32473		/**
32474		 * Renders the control as a HTML string.
32475		 *
32476		 * @method renderHtml
32477		 * @return {String} HTML representing the control.
32478		 */
32479		renderHtml: function() {
32480			var self = this, forId = self.settings.forId;
32481
32482			return (
32483				'<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId : '') + '">' +
32484					self.encode(self._text) +
32485				'</label>'
32486			);
32487		}
32488	});
32489});
32490
32491// Included from: js/tinymce/classes/ui/Toolbar.js
32492
32493/**
32494 * Toolbar.js
32495 *
32496 * Copyright, Moxiecode Systems AB
32497 * Released under LGPL License.
32498 *
32499 * License: http://www.tinymce.com/license
32500 * Contributing: http://www.tinymce.com/contributing
32501 */
32502
32503/**
32504 * Creates a new toolbar.
32505 *
32506 * @class tinymce.ui.Toolbar
32507 * @extends tinymce.ui.Container
32508 */
32509define("tinymce/ui/Toolbar", [
32510	"tinymce/ui/Container",
32511	"tinymce/ui/KeyboardNavigation"
32512], function(Container, KeyboardNavigation) {
32513	"use strict";
32514
32515	return Container.extend({
32516		Defaults: {
32517			role: 'toolbar',
32518			layout: 'flow'
32519		},
32520
32521		/**
32522		 * Constructs a instance with the specified settings.
32523		 *
32524		 * @constructor
32525		 * @param {Object} settings Name/value object with settings.
32526		 */
32527		init: function(settings) {
32528			var self = this;
32529
32530			self._super(settings);
32531			self.addClass('toolbar');
32532		},
32533
32534		/**
32535		 * Called after the control has been rendered.
32536		 *
32537		 * @method postRender
32538		 */
32539		postRender: function() {
32540			var self = this;
32541
32542			self.items().addClass('toolbar-item');
32543
32544			self.keyNav = new KeyboardNavigation({
32545				root: self,
32546				enableLeftRight: true
32547			});
32548
32549			return self._super();
32550		}
32551	});
32552});
32553
32554// Included from: js/tinymce/classes/ui/MenuBar.js
32555
32556/**
32557 * MenuBar.js
32558 *
32559 * Copyright, Moxiecode Systems AB
32560 * Released under LGPL License.
32561 *
32562 * License: http://www.tinymce.com/license
32563 * Contributing: http://www.tinymce.com/contributing
32564 */
32565
32566/**
32567 * Creates a new menubar.
32568 *
32569 * @-x-less MenuBar.less
32570 * @class tinymce.ui.MenuBar
32571 * @extends tinymce.ui.Container
32572 */
32573define("tinymce/ui/MenuBar", [
32574	"tinymce/ui/Toolbar"
32575], function(Toolbar) {
32576	"use strict";
32577
32578	return Toolbar.extend({
32579		Defaults: {
32580			role: 'menubar',
32581			containerCls: 'menubar',
32582			defaults: {
32583				type: 'menubutton'
32584			}
32585		}
32586	});
32587});
32588
32589// Included from: js/tinymce/classes/ui/MenuButton.js
32590
32591/**
32592 * MenuButton.js
32593 *
32594 * Copyright, Moxiecode Systems AB
32595 * Released under LGPL License.
32596 *
32597 * License: http://www.tinymce.com/license
32598 * Contributing: http://www.tinymce.com/contributing
32599 */
32600
32601/**
32602 * Creates a new menu button.
32603 *
32604 * @-x-less MenuButton.less
32605 * @class tinymce.ui.MenuButton
32606 * @extends tinymce.ui.Button
32607 */
32608define("tinymce/ui/MenuButton", [
32609	"tinymce/ui/Button",
32610	"tinymce/ui/Factory",
32611	"tinymce/ui/MenuBar"
32612], function(Button, Factory, MenuBar) {
32613	"use strict";
32614
32615	// TODO: Maybe add as some global function
32616	function isChildOf(node, parent) {
32617		while (node) {
32618			if (parent === node) {
32619				return true;
32620			}
32621
32622			node = node.parentNode;
32623		}
32624
32625		return false;
32626	}
32627
32628	var MenuButton = Button.extend({
32629		/**
32630		 * Constructs a instance with the specified settings.
32631		 *
32632		 * @constructor
32633		 * @param {Object} settings Name/value object with settings.
32634		 */
32635		init: function(settings) {
32636			var self = this;
32637
32638			self._renderOpen = true;
32639			self._super(settings);
32640
32641			self.addClass('menubtn');
32642
32643			if (settings.fixedWidth) {
32644				self.addClass('fixed-width');
32645			}
32646
32647			self.aria('haspopup', true);
32648			self.hasPopup = true;
32649		},
32650
32651		/**
32652		 * Shows the menu for the button.
32653		 *
32654		 * @method showMenu
32655		 */
32656		showMenu: function() {
32657			var self = this, settings = self.settings, menu;
32658
32659			if (self.menu && self.menu.visible()) {
32660				return self.hideMenu();
32661			}
32662
32663			if (!self.menu) {
32664				menu = settings.menu || [];
32665
32666				// Is menu array then auto constuct menu control
32667				if (menu.length) {
32668					menu = {
32669						type: 'menu',
32670						items: menu
32671					};
32672				} else {
32673					menu.type = menu.type || 'menu';
32674				}
32675
32676				self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
32677				self.fire('createmenu');
32678				self.menu.reflow();
32679				self.menu.on('cancel', function(e) {
32680					if (e.control === self.menu) {
32681						self.focus();
32682					}
32683				});
32684
32685				self.menu.on('show hide', function(e) {
32686					if (e.control == self.menu) {
32687						self.activeMenu(e.type == 'show');
32688					}
32689				}).fire('show');
32690
32691				self.aria('expanded', true);
32692			}
32693
32694			self.menu.show();
32695			self.menu.layoutRect({w: self.layoutRect().w});
32696			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
32697		},
32698
32699		/**
32700		 * Hides the menu for the button.
32701		 *
32702		 * @method hideMenu
32703		 */
32704		hideMenu: function() {
32705			var self = this;
32706
32707			if (self.menu) {
32708				self.menu.items().each(function(item) {
32709					if (item.hideMenu) {
32710						item.hideMenu();
32711					}
32712				});
32713
32714				self.menu.hide();
32715				self.aria('expanded', false);
32716			}
32717		},
32718
32719		/**
32720		 * Sets the active menu state.
32721		 *
32722		 * @private
32723		 */
32724		activeMenu: function(state) {
32725			this.toggleClass('active', state);
32726		},
32727
32728		/**
32729		 * Renders the control as a HTML string.
32730		 *
32731		 * @method renderHtml
32732		 * @return {String} HTML representing the control.
32733		 */
32734		renderHtml: function() {
32735			var self = this, id = self._id, prefix = self.classPrefix;
32736			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
32737
32738			self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
32739
32740			return (
32741				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
32742					'<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
32743						(icon ? '<i class="' + icon + '"></i>' : '') +
32744						'<span>' + (self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') + '</span>' +
32745						' <i class="' + prefix + 'caret"></i>' +
32746					'</button>' +
32747				'</div>'
32748			);
32749		},
32750
32751		/**
32752		 * Gets invoked after the control has been rendered.
32753		 *
32754		 * @method postRender
32755		 */
32756		postRender: function() {
32757			var self = this;
32758
32759			self.on('click', function(e) {
32760				if (e.control === self && isChildOf(e.target, self.getEl())) {
32761					self.showMenu();
32762
32763					if (e.keyboard) {
32764						self.menu.items()[0].focus();
32765					}
32766				}
32767			});
32768
32769			self.on('mouseenter', function(e) {
32770				var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
32771
32772				if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
32773					parent.items().filter('MenuButton').each(function(ctrl) {
32774						if (ctrl.hideMenu && ctrl != overCtrl) {
32775							if (ctrl.menu && ctrl.menu.visible()) {
32776								hasVisibleSiblingMenu = true;
32777							}
32778
32779							ctrl.hideMenu();
32780						}
32781					});
32782
32783					if (hasVisibleSiblingMenu) {
32784						overCtrl.focus(); // Fix for: #5887
32785						overCtrl.showMenu();
32786					}
32787				}
32788			});
32789
32790			return self._super();
32791		},
32792
32793		/**
32794		 * Sets/gets the current button text.
32795		 *
32796		 * @method text
32797		 * @param {String} [text] New button text.
32798		 * @return {String|tinymce.ui.MenuButton} Current text or current MenuButton instance.
32799		 */
32800		text: function(text) {
32801			var self = this, i, children;
32802
32803			if (self._rendered) {
32804				children = self.getEl('open').getElementsByTagName('span');
32805				for (i = 0; i < children.length; i++) {
32806					children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
32807				}
32808			}
32809
32810			return this._super(text);
32811		},
32812
32813		/**
32814		 * Removes the control and it's menus.
32815		 *
32816		 * @method remove
32817		 */
32818		remove: function() {
32819			this._super();
32820
32821			if (this.menu) {
32822				this.menu.remove();
32823			}
32824		}
32825	});
32826
32827	return MenuButton;
32828});
32829
32830// Included from: js/tinymce/classes/ui/ListBox.js
32831
32832/**
32833 * ListBox.js
32834 *
32835 * Copyright, Moxiecode Systems AB
32836 * Released under LGPL License.
32837 *
32838 * License: http://www.tinymce.com/license
32839 * Contributing: http://www.tinymce.com/contributing
32840 */
32841
32842/**
32843 * Creates a new list box control.
32844 *
32845 * @-x-less ListBox.less
32846 * @class tinymce.ui.ListBox
32847 * @extends tinymce.ui.MenuButton
32848 */
32849define("tinymce/ui/ListBox", [
32850	"tinymce/ui/MenuButton"
32851], function(MenuButton) {
32852	"use strict";
32853
32854	return MenuButton.extend({
32855		/**
32856		 * Constructs a instance with the specified settings.
32857		 *
32858		 * @constructor
32859		 * @param {Object} settings Name/value object with settings.
32860		 * @setting {Array} values Array with values to add to list box.
32861		 */
32862		init: function(settings) {
32863			var self = this, values, i, selected, selectedText, lastItemCtrl;
32864
32865			self._values = values = settings.values;
32866			if (values) {
32867				for (i = 0; i < values.length; i++) {
32868					selected = values[i].selected || settings.value === values[i].value;
32869
32870					if (selected) {
32871						selectedText = selectedText || values[i].text;
32872						self._value = values[i].value;
32873					}
32874				}
32875
32876				settings.menu = values;
32877			}
32878
32879			settings.text = settings.text || selectedText || values[0].text;
32880
32881			self._super(settings);
32882			self.addClass('listbox');
32883
32884			self.on('select', function(e) {
32885				var ctrl = e.control;
32886
32887				if (lastItemCtrl) {
32888					e.lastControl = lastItemCtrl;
32889				}
32890
32891				if (settings.multiple) {
32892					ctrl.active(!ctrl.active());
32893				} else {
32894					self.value(e.control.settings.value);
32895				}
32896
32897				lastItemCtrl = ctrl;
32898			});
32899		},
32900
32901		/**
32902		 * Getter/setter function for the control value.
32903		 *
32904		 * @method value
32905		 * @param {String} [value] Value to be set.
32906		 * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
32907		 */
32908		value: function(value) {
32909			var self = this, active, selectedText, menu, i;
32910
32911			function activateByValue(menu, value) {
32912				menu.items().each(function(ctrl) {
32913					active = ctrl.value() === value;
32914
32915					if (active) {
32916						selectedText = selectedText || ctrl.text();
32917					}
32918
32919					ctrl.active(active);
32920
32921					if (ctrl.menu) {
32922						activateByValue(ctrl.menu, value);
32923					}
32924				});
32925			}
32926
32927			if (typeof(value) != "undefined") {
32928				if (self.menu) {
32929					activateByValue(self.menu, value);
32930				} else {
32931					menu = self.settings.menu;
32932					for (i = 0; i < menu.length; i++) {
32933						active = menu[i].value == value;
32934
32935						if (active) {
32936							selectedText = selectedText || menu[i].text;
32937						}
32938
32939						menu[i].active = active;
32940					}
32941				}
32942
32943				self.text(selectedText || this.settings.text);
32944			}
32945
32946			return self._super(value);
32947		}
32948	});
32949});
32950
32951// Included from: js/tinymce/classes/ui/MenuItem.js
32952
32953/**
32954 * MenuItem.js
32955 *
32956 * Copyright, Moxiecode Systems AB
32957 * Released under LGPL License.
32958 *
32959 * License: http://www.tinymce.com/license
32960 * Contributing: http://www.tinymce.com/contributing
32961 */
32962
32963/**
32964 * Creates a new menu item.
32965 *
32966 * @-x-less MenuItem.less
32967 * @class tinymce.ui.MenuItem
32968 * @extends tinymce.ui.Control
32969 */
32970define("tinymce/ui/MenuItem", [
32971	"tinymce/ui/Widget",
32972	"tinymce/ui/Factory",
32973	"tinymce/Env"
32974], function(Widget, Factory, Env) {
32975	"use strict";
32976
32977	return Widget.extend({
32978		Defaults: {
32979			border: 0,
32980			role: 'menuitem'
32981		},
32982
32983		/**
32984		 * Constructs a instance with the specified settings.
32985		 *
32986		 * @constructor
32987		 * @param {Object} settings Name/value object with settings.
32988		 * @setting {Boolean} selectable Selectable menu.
32989		 * @setting {Array} menu Submenu array with items.
32990		 * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
32991		 */
32992		init: function(settings) {
32993			var self = this;
32994
32995			self.hasPopup = true;
32996
32997			self._super(settings);
32998
32999			settings = self.settings;
33000
33001			self.addClass('menu-item');
33002
33003			if (settings.menu) {
33004				self.addClass('menu-item-expand');
33005			}
33006
33007			if (settings.preview) {
33008				self.addClass('menu-item-preview');
33009			}
33010
33011			if (self._text === '-' || self._text === '|') {
33012				self.addClass('menu-item-sep');
33013				self.aria('role', 'separator');
33014				self.canFocus = false;
33015				self._text = '-';
33016			}
33017
33018			if (settings.selectable) {
33019				self.aria('role', 'menuitemcheckbox');
33020				self.aria('checked', true);
33021				self.addClass('menu-item-checkbox');
33022				settings.icon = 'selected';
33023			}
33024
33025			if (!settings.preview && !settings.selectable) {
33026				self.addClass('menu-item-normal');
33027			}
33028
33029			self.on('mousedown', function(e) {
33030				e.preventDefault();
33031			});
33032
33033			self.on('mouseenter click', function(e) {
33034				if (e.control === self) {
33035					if (!settings.menu && e.type === 'click') {
33036						self.parent().hideAll();
33037						self.fire('cancel');
33038						self.fire('select');
33039					} else {
33040						self.showMenu();
33041
33042						if (e.keyboard) {
33043							setTimeout(function() {
33044								self.menu.items()[0].focus();
33045							}, 0);
33046						}
33047					}
33048				}
33049			});
33050
33051			if (settings.menu) {
33052				self.aria('haspopup', true);
33053			}
33054		},
33055
33056		/**
33057		 * Returns true/false if the menuitem has sub menu.
33058		 *
33059		 * @method hasMenus
33060		 * @return {Boolean} True/false state if it has submenu.
33061		 */
33062		hasMenus: function() {
33063			return !!this.settings.menu;
33064		},
33065
33066		/**
33067		 * Shows the menu for the menu item.
33068		 *
33069		 * @method showMenu
33070		 */
33071		showMenu: function() {
33072			var self = this, settings = self.settings, menu, parent = self.parent();
33073
33074			parent.items().each(function(ctrl) {
33075				if (ctrl !== self) {
33076					ctrl.hideMenu();
33077				}
33078			});
33079
33080			if (settings.menu) {
33081				menu = self.menu;
33082
33083				if (!menu) {
33084					menu = settings.menu;
33085
33086					// Is menu array then auto constuct menu control
33087					if (menu.length) {
33088						menu = {
33089							type: 'menu',
33090							items: menu
33091						};
33092					} else {
33093						menu.type = menu.type || 'menu';
33094					}
33095
33096					if (parent.settings.itemDefaults) {
33097						menu.itemDefaults = parent.settings.itemDefaults;
33098					}
33099
33100					menu = self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
33101					menu.reflow();
33102					menu.fire('show');
33103					menu.on('cancel', function() {
33104						self.focus();
33105					});
33106
33107					menu.on('hide', function(e) {
33108						if (e.control === menu) {
33109							self.removeClass('selected');
33110						}
33111					});
33112				} else {
33113					menu.show();
33114				}
33115
33116				menu._parentMenu = parent;
33117
33118				menu.addClass('menu-sub');
33119
33120				var rel = menu.testMoveRel(
33121					self.getEl(),
33122					self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
33123				);
33124
33125				menu.moveRel(self.getEl(), rel);
33126				menu.rel = rel;
33127
33128				rel = 'menu-sub-' + rel;
33129				menu.removeClass(menu._lastRel);
33130				menu.addClass(rel);
33131				menu._lastRel = rel;
33132
33133				self.addClass('selected');
33134				self.aria('expanded', true);
33135			}
33136		},
33137
33138		/**
33139		 * Hides the menu for the menu item.
33140		 *
33141		 * @method hideMenu
33142		 */
33143		hideMenu: function() {
33144			var self = this;
33145
33146			if (self.menu) {
33147				self.menu.items().each(function(item) {
33148					if (item.hideMenu) {
33149						item.hideMenu();
33150					}
33151				});
33152
33153				self.menu.hide();
33154				self.aria('expanded', false);
33155			}
33156
33157			return self;
33158		},
33159
33160		/**
33161		 * Renders the control as a HTML string.
33162		 *
33163		 * @method renderHtml
33164		 * @return {String} HTML representing the control.
33165		 */
33166		renderHtml: function() {
33167			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
33168			var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
33169
33170			if (icon) {
33171				self.parent().addClass('menu-has-icons');
33172			}
33173
33174			if (settings.image) {
33175				icon = 'none';
33176				image = ' style="background-image: url(\'' + settings.image + '\')"';
33177			}
33178
33179			if (shortcut && Env.mac) {
33180				// format shortcut for Mac
33181				shortcut = shortcut.replace(/ctrl\+alt\+/i, '&#x2325;&#x2318;'); // ctrl+cmd
33182				shortcut = shortcut.replace(/ctrl\+/i, '&#x2318;'); // ctrl symbol
33183				shortcut = shortcut.replace(/alt\+/i, '&#x2325;'); // cmd symbol
33184				shortcut = shortcut.replace(/shift\+/i, '&#x21E7;'); // shift symbol
33185			}
33186
33187			icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
33188
33189			return (
33190				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
33191					(text !== '-' ? '<i class="' + icon + '"' + image + '></i>&nbsp;' : '') +
33192					(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
33193					(shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' +
33194					 shortcut + '</div>' : '') +
33195					(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
33196				'</div>'
33197			);
33198		},
33199
33200		/**
33201		 * Gets invoked after the control has been rendered.
33202		 *
33203		 * @method postRender
33204		 */
33205		postRender: function() {
33206			var self = this, settings = self.settings;
33207
33208			var textStyle = settings.textStyle;
33209			if (typeof(textStyle) == "function") {
33210				textStyle = textStyle.call(this);
33211			}
33212
33213			if (textStyle) {
33214				var textElm = self.getEl('text');
33215				if (textElm) {
33216					textElm.setAttribute('style', textStyle);
33217				}
33218			}
33219
33220			return self._super();
33221		},
33222
33223		/**
33224		 * Removes the control and it's menus.
33225		 *
33226		 * @method remove
33227		 */
33228		remove: function() {
33229			this._super();
33230
33231			if (this.menu) {
33232				this.menu.remove();
33233			}
33234		}
33235	});
33236});
33237
33238// Included from: js/tinymce/classes/ui/Menu.js
33239
33240/**
33241 * Menu.js
33242 *
33243 * Copyright, Moxiecode Systems AB
33244 * Released under LGPL License.
33245 *
33246 * License: http://www.tinymce.com/license
33247 * Contributing: http://www.tinymce.com/contributing
33248 */
33249
33250/**
33251 * Creates a new menu.
33252 *
33253 * @-x-less Menu.less
33254 * @class tinymce.ui.Menu
33255 * @extends tinymce.ui.FloatPanel
33256 */
33257define("tinymce/ui/Menu", [
33258	"tinymce/ui/FloatPanel",
33259	"tinymce/ui/KeyboardNavigation",
33260	"tinymce/ui/MenuItem",
33261	"tinymce/util/Tools"
33262], function(FloatPanel, KeyboardNavigation, MenuItem, Tools) {
33263	"use strict";
33264
33265	var Menu = FloatPanel.extend({
33266		Defaults: {
33267			defaultType: 'menuitem',
33268			border: 1,
33269			layout: 'stack',
33270			role: 'menu'
33271		},
33272
33273		/**
33274		 * Constructs a instance with the specified settings.
33275		 *
33276		 * @constructor
33277		 * @param {Object} settings Name/value object with settings.
33278		 */
33279		init: function(settings) {
33280			var self = this;
33281
33282			settings.autohide = true;
33283			settings.constrainToViewport = true;
33284
33285			if (settings.itemDefaults) {
33286				var items = settings.items, i = items.length;
33287
33288				while (i--) {
33289					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
33290				}
33291			}
33292
33293			self._super(settings);
33294			self.addClass('menu');
33295
33296			self.keyNav = new KeyboardNavigation({
33297				root: self,
33298				enableUpDown: true,
33299				enableLeftRight: true,
33300
33301				leftAction: function() {
33302					if (self.parent() instanceof MenuItem) {
33303						self.keyNav.cancel();
33304					}
33305				},
33306
33307				onCancel: function() {
33308					self.fire('cancel', {}, false);
33309					self.hide();
33310				}
33311			});
33312		},
33313
33314		/**
33315		 * Repaints the control after a layout operation.
33316		 *
33317		 * @method repaint
33318		 */
33319		repaint: function() {
33320			this.toggleClass('menu-align', true);
33321
33322			this._super();
33323
33324			this.getEl().style.height = '';
33325			this.getEl('body').style.height = '';
33326
33327			return this;
33328		},
33329
33330		/**
33331		 * Hides/closes the menu.
33332		 *
33333		 * @method cancel
33334		 */
33335		cancel: function() {
33336			var self = this;
33337
33338			self.hideAll();
33339			self.fire('cancel');
33340			self.fire('select');
33341		},
33342
33343		/**
33344		 * Hide menu and all sub menus.
33345		 *
33346		 * @method hideAll
33347		 */
33348		hideAll: function() {
33349			var self = this;
33350
33351			this.find('menuitem').exec('hideMenu');
33352
33353			return self._super();
33354		},
33355
33356		/**
33357		 * Invoked before the menu is rendered.
33358		 *
33359		 * @method preRender
33360		 */
33361		preRender: function() {
33362			var self = this;
33363
33364			self.items().each(function(ctrl) {
33365				var settings = ctrl.settings;
33366
33367				if (settings.icon || settings.selectable) {
33368					self._hasIcons = true;
33369					return false;
33370				}
33371			});
33372
33373			return self._super();
33374		}
33375	});
33376
33377	return Menu;
33378});
33379
33380// Included from: js/tinymce/classes/ui/Radio.js
33381
33382/**
33383 * Radio.js
33384 *
33385 * Copyright, Moxiecode Systems AB
33386 * Released under LGPL License.
33387 *
33388 * License: http://www.tinymce.com/license
33389 * Contributing: http://www.tinymce.com/contributing
33390 */
33391
33392/**
33393 * Creates a new radio button.
33394 *
33395 * @-x-less Radio.less
33396 * @class tinymce.ui.Radio
33397 * @extends tinymce.ui.Checkbox
33398 */
33399define("tinymce/ui/Radio", [
33400	"tinymce/ui/Checkbox"
33401], function(Checkbox) {
33402	"use strict";
33403
33404	return Checkbox.extend({
33405		Defaults: {
33406			classes: "radio",
33407			role: "radio"
33408		}
33409	});
33410});
33411
33412// Included from: js/tinymce/classes/ui/ResizeHandle.js
33413
33414/**
33415 * ResizeHandle.js
33416 *
33417 * Copyright, Moxiecode Systems AB
33418 * Released under LGPL License.
33419 *
33420 * License: http://www.tinymce.com/license
33421 * Contributing: http://www.tinymce.com/contributing
33422 */
33423
33424/**
33425 * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events.
33426 *
33427 * @-x-less ResizeHandle.less
33428 * @class tinymce.ui.ResizeHandle
33429 * @extends tinymce.ui.Widget
33430 */
33431define("tinymce/ui/ResizeHandle", [
33432	"tinymce/ui/Widget",
33433	"tinymce/ui/DragHelper"
33434], function(Widget, DragHelper) {
33435	"use strict";
33436
33437	return Widget.extend({
33438		/**
33439		 * Renders the control as a HTML string.
33440		 *
33441		 * @method renderHtml
33442		 * @return {String} HTML representing the control.
33443		 */
33444		renderHtml: function() {
33445			var self = this, prefix = self.classPrefix;
33446
33447			self.addClass('resizehandle');
33448
33449			if (self.settings.direction == "both") {
33450				self.addClass('resizehandle-both');
33451			}
33452
33453			self.canFocus = false;
33454
33455			return (
33456				'<div id="' + self._id + '" class="' + self.classes() + '">' +
33457					'<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' +
33458				'</div>'
33459			);
33460		},
33461
33462		/**
33463		 * Called after the control has been rendered.
33464		 *
33465		 * @method postRender
33466		 */
33467		postRender: function() {
33468			var self = this;
33469
33470			self._super();
33471
33472			self.resizeDragHelper = new DragHelper(this._id, {
33473				start: function() {
33474					self.fire('ResizeStart');
33475				},
33476
33477				drag: function(e) {
33478					if (self.settings.direction != "both") {
33479						e.deltaX = 0;
33480					}
33481
33482					self.fire('Resize', e);
33483				},
33484
33485				end: function() {
33486					self.fire('ResizeEnd');
33487				}
33488			});
33489		},
33490
33491		remove: function() {
33492			if (this.resizeDragHelper) {
33493				this.resizeDragHelper.destroy();
33494			}
33495
33496			return this._super();
33497		}
33498	});
33499});
33500
33501// Included from: js/tinymce/classes/ui/Spacer.js
33502
33503/**
33504 * Spacer.js
33505 *
33506 * Copyright, Moxiecode Systems AB
33507 * Released under LGPL License.
33508 *
33509 * License: http://www.tinymce.com/license
33510 * Contributing: http://www.tinymce.com/contributing
33511 */
33512
33513/**
33514 * Creates a spacer. This control is used in flex layouts for example.
33515 *
33516 * @-x-less Spacer.less
33517 * @class tinymce.ui.Spacer
33518 * @extends tinymce.ui.Widget
33519 */
33520define("tinymce/ui/Spacer", [
33521	"tinymce/ui/Widget"
33522], function(Widget) {
33523	"use strict";
33524
33525	return Widget.extend({
33526		/**
33527		 * Renders the control as a HTML string.
33528		 *
33529		 * @method renderHtml
33530		 * @return {String} HTML representing the control.
33531		 */
33532		renderHtml: function() {
33533			var self = this;
33534
33535			self.addClass('spacer');
33536			self.canFocus = false;
33537
33538			return '<div id="' + self._id + '" class="' + self.classes() + '"></div>';
33539		}
33540	});
33541});
33542
33543// Included from: js/tinymce/classes/ui/SplitButton.js
33544
33545/**
33546 * SplitButton.js
33547 *
33548 * Copyright, Moxiecode Systems AB
33549 * Released under LGPL License.
33550 *
33551 * License: http://www.tinymce.com/license
33552 * Contributing: http://www.tinymce.com/contributing
33553 */
33554
33555/**
33556 * Creates a split button.
33557 *
33558 * @-x-less SplitButton.less
33559 * @class tinymce.ui.SplitButton
33560 * @extends tinymce.ui.Button
33561 */
33562define("tinymce/ui/SplitButton", [
33563	"tinymce/ui/MenuButton",
33564	"tinymce/ui/DomUtils"
33565], function(MenuButton, DomUtils) {
33566	return MenuButton.extend({
33567		Defaults: {
33568			classes: "widget btn splitbtn",
33569			role: "splitbutton"
33570		},
33571
33572		/**
33573		 * Repaints the control after a layout operation.
33574		 *
33575		 * @method repaint
33576		 */
33577		repaint: function() {
33578			var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm;
33579
33580			self._super();
33581
33582			mainButtonElm = elm.firstChild;
33583			menuButtonElm = elm.lastChild;
33584
33585			DomUtils.css(mainButtonElm, {
33586				width: rect.w - DomUtils.getSize(menuButtonElm).width,
33587				height: rect.h - 2
33588			});
33589
33590			DomUtils.css(menuButtonElm, {
33591				height: rect.h - 2
33592			});
33593
33594			return self;
33595		},
33596
33597		/**
33598		 * Sets the active menu state.
33599		 *
33600		 * @private
33601		 */
33602		activeMenu: function(state) {
33603			var self = this;
33604
33605			DomUtils.toggleClass(self.getEl().lastChild, self.classPrefix + 'active', state);
33606		},
33607
33608		/**
33609		 * Renders the control as a HTML string.
33610		 *
33611		 * @method renderHtml
33612		 * @return {String} HTML representing the control.
33613		 */
33614		renderHtml: function() {
33615			var self = this, id = self._id, prefix = self.classPrefix;
33616			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
33617
33618			return (
33619				'<div id="' + id + '" class="' + self.classes() + '">' +
33620					'<button type="button" hidefocus tabindex="-1">' +
33621						(icon ? '<i class="' + icon + '"></i>' : '') +
33622						(self._text ? (icon ? ' ' : '') + self._text : '') +
33623					'</button>' +
33624					'<button type="button" class="' + prefix + 'open" hidefocus tabindex="-1">' +
33625						//(icon ? '<i class="' + icon + '"></i>' : '') +
33626						(self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') +
33627						' <i class="' + prefix + 'caret"></i>' +
33628					'</button>' +
33629				'</div>'
33630			);
33631		},
33632
33633		/**
33634		 * Called after the control has been rendered.
33635		 *
33636		 * @method postRender
33637		 */
33638		postRender: function() {
33639			var self = this, onClickHandler = self.settings.onclick;
33640
33641			self.on('click', function(e) {
33642				var node = e.target;
33643
33644				if (e.control == this) {
33645					// Find clicks that is on the main button
33646					while (node) {
33647						if (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1) {
33648							e.stopImmediatePropagation();
33649							onClickHandler.call(this, e);
33650							return;
33651						}
33652
33653						node = node.parentNode;
33654					}
33655				}
33656			});
33657
33658			delete self.settings.onclick;
33659
33660			return self._super();
33661		}
33662	});
33663});
33664
33665// Included from: js/tinymce/classes/ui/StackLayout.js
33666
33667/**
33668 * StackLayout.js
33669 *
33670 * Copyright, Moxiecode Systems AB
33671 * Released under LGPL License.
33672 *
33673 * License: http://www.tinymce.com/license
33674 * Contributing: http://www.tinymce.com/contributing
33675 */
33676
33677/**
33678 * This layout uses the browsers layout when the items are blocks.
33679 *
33680 * @-x-less StackLayout.less
33681 * @class tinymce.ui.StackLayout
33682 * @extends tinymce.ui.FlowLayout
33683 */
33684define("tinymce/ui/StackLayout", [
33685	"tinymce/ui/FlowLayout"
33686], function(FlowLayout) {
33687	"use strict";
33688
33689	return FlowLayout.extend({
33690		Defaults: {
33691			containerClass: 'stack-layout',
33692			controlClass: 'stack-layout-item',
33693			endClass : 'break'
33694		}
33695	});
33696});
33697
33698// Included from: js/tinymce/classes/ui/TabPanel.js
33699
33700/**
33701 * TabPanel.js
33702 *
33703 * Copyright, Moxiecode Systems AB
33704 * Released under LGPL License.
33705 *
33706 * License: http://www.tinymce.com/license
33707 * Contributing: http://www.tinymce.com/contributing
33708 */
33709
33710/**
33711 * Creates a tab panel control.
33712 *
33713 * @-x-less TabPanel.less
33714 * @class tinymce.ui.TabPanel
33715 * @extends tinymce.ui.Panel
33716 *
33717 * @setting {Number} activeTab Active tab index.
33718 */
33719define("tinymce/ui/TabPanel", [
33720	"tinymce/ui/Panel",
33721	"tinymce/ui/DomUtils"
33722], function(Panel, DomUtils) {
33723	"use strict";
33724
33725	return Panel.extend({
33726		lastIdx: 0,
33727
33728		Defaults: {
33729			layout: 'absolute',
33730			defaults: {
33731				type: 'panel'
33732			}
33733		},
33734
33735		/**
33736		 * Activates the specified tab by index.
33737		 *
33738		 * @method activateTab
33739		 * @param {Number} idx Index of the tab to activate.
33740		 */
33741		activateTab: function(idx) {
33742			if (this.activeTabId) {
33743				DomUtils.removeClass(this.getEl(this.activeTabId), this.classPrefix + 'active');
33744			}
33745
33746			this.activeTabId = 't' + idx;
33747
33748			DomUtils.addClass(this.getEl('t' + idx), this.classPrefix + 'active');
33749
33750			if (idx != this.lastIdx) {
33751				this.items()[this.lastIdx].hide();
33752				this.lastIdx = idx;
33753			}
33754
33755			this.items()[idx].show().fire('showtab');
33756			this.reflow();
33757		},
33758
33759		/**
33760		 * Renders the control as a HTML string.
33761		 *
33762		 * @method renderHtml
33763		 * @return {String} HTML representing the control.
33764		 */
33765		renderHtml: function() {
33766			var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix;
33767
33768			self.preRender();
33769			layout.preRender(self);
33770
33771			self.items().each(function(ctrl, i) {
33772				tabsHtml += (
33773					'<div id="' + self._id + '-t' + i + '" class="' + prefix + 'tab" unselectable="on">' +
33774						self.encode(ctrl.settings.title) +
33775					'</div>'
33776				);
33777			});
33778
33779			return (
33780				'<div id="' + self._id + '" class="' + self.classes() + '" hideFocus="1" tabIndex="-1">' +
33781					'<div id="' + self._id + '-head" class="' + prefix + 'tabs">' +
33782						tabsHtml +
33783					'</div>' +
33784					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
33785						layout.renderHtml(self) +
33786					'</div>' +
33787				'</div>'
33788			);
33789		},
33790
33791		/**
33792		 * Called after the control has been rendered.
33793		 *
33794		 * @method postRender
33795		 */
33796		postRender: function() {
33797			var self = this;
33798
33799			self._super();
33800
33801			self.settings.activeTab = self.settings.activeTab || 0;
33802			self.activateTab(self.settings.activeTab);
33803
33804			this.on('click', function(e) {
33805				var targetParent = e.target.parentNode;
33806
33807				if (e.target.parentNode.id == self._id + '-head') {
33808					var i = targetParent.childNodes.length;
33809
33810					while (i--) {
33811						if (targetParent.childNodes[i] == e.target) {
33812							self.activateTab(i);
33813						}
33814					}
33815				}
33816			});
33817		},
33818
33819		/**
33820		 * Initializes the current controls layout rect.
33821		 * This will be executed by the layout managers to determine the
33822		 * default minWidth/minHeight etc.
33823		 *
33824		 * @method initLayoutRect
33825		 * @return {Object} Layout rect instance.
33826		 */
33827		initLayoutRect: function() {
33828			var self = this, rect, minW, minH;
33829
33830			minW = DomUtils.getSize(self.getEl('head')).width;
33831			minW = minW < 0 ? 0 : minW;
33832			minH = 0;
33833			self.items().each(function(item, i) {
33834				minW = Math.max(minW, item.layoutRect().minW);
33835				minH = Math.max(minH, item.layoutRect().minH);
33836				if (self.settings.activeTab != i) {
33837					item.hide();
33838				}
33839			});
33840
33841			self.items().each(function(ctrl) {
33842				ctrl.settings.x = 0;
33843				ctrl.settings.y = 0;
33844				ctrl.settings.w = minW;
33845				ctrl.settings.h = minH;
33846
33847				ctrl.layoutRect({
33848					x: 0,
33849					y: 0,
33850					w: minW,
33851					h: minH
33852				});
33853			});
33854
33855			var headH = DomUtils.getSize(self.getEl('head')).height;
33856
33857			self.settings.minWidth = minW;
33858			self.settings.minHeight = minH + headH;
33859
33860			rect = self._super();
33861			rect.deltaH += headH;
33862			rect.innerH = rect.h - rect.deltaH;
33863
33864			return rect;
33865		}
33866	});
33867});
33868
33869// Included from: js/tinymce/classes/ui/TextBox.js
33870
33871/**
33872 * TextBox.js
33873 *
33874 * Copyright, Moxiecode Systems AB
33875 * Released under LGPL License.
33876 *
33877 * License: http://www.tinymce.com/license
33878 * Contributing: http://www.tinymce.com/contributing
33879 */
33880
33881/**
33882 * Creates a new textbox.
33883 *
33884 * @-x-less TextBox.less
33885 * @class tinymce.ui.TextBox
33886 * @extends tinymce.ui.Widget
33887 */
33888define("tinymce/ui/TextBox", [
33889	"tinymce/ui/Widget",
33890	"tinymce/ui/DomUtils"
33891], function(Widget, DomUtils) {
33892	"use strict";
33893
33894	return Widget.extend({
33895		/**
33896		 * Constructs a instance with the specified settings.
33897		 *
33898		 * @constructor
33899		 * @param {Object} settings Name/value object with settings.
33900		 * @setting {Boolean} multiline True if the textbox is a multiline control.
33901		 * @setting {Number} maxLength Max length for the textbox.
33902		 * @setting {Number} size Size of the textbox in characters.
33903		 */
33904		init: function(settings) {
33905			var self = this;
33906
33907			self._super(settings);
33908
33909			self._value = settings.value || '';
33910			self.addClass('textbox');
33911
33912			if (settings.multiline) {
33913				self.addClass('multiline');
33914			} else {
33915				// TODO: Rework this
33916				self.on('keydown', function(e) {
33917					if (e.keyCode == 13) {
33918						self.parents().reverse().each(function(ctrl) {
33919							e.preventDefault();
33920
33921							if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
33922								ctrl.fire('submit', {data: ctrl.toJSON()});
33923								return false;
33924							}
33925						});
33926					}
33927				});
33928			}
33929		},
33930
33931		/**
33932		 * Getter/setter function for the disabled state.
33933		 *
33934		 * @method value
33935		 * @param {Boolean} [state] State to be set.
33936		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
33937		 */
33938		disabled: function(state) {
33939			var self = this;
33940
33941			if (self._rendered && typeof(state) != 'undefined') {
33942				self.getEl().disabled = state;
33943			}
33944
33945			return self._super(state);
33946		},
33947
33948		/**
33949		 * Getter/setter function for the control value.
33950		 *
33951		 * @method value
33952		 * @param {String} [value] Value to be set.
33953		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
33954		 */
33955		value: function(value) {
33956			var self = this;
33957
33958			if (typeof(value) != "undefined") {
33959				self._value = value;
33960
33961				if (self._rendered) {
33962					self.getEl().value = value;
33963				}
33964
33965				return self;
33966			}
33967
33968			if (self._rendered) {
33969				return self.getEl().value;
33970			}
33971
33972			return self._value;
33973		},
33974
33975		/**
33976		 * Repaints the control after a layout operation.
33977		 *
33978		 * @method repaint
33979		 */
33980		repaint: function() {
33981			var self = this, style, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
33982
33983			style = self.getEl().style;
33984			rect = self._layoutRect;
33985			lastRepaintRect = self._lastRepaintRect || {};
33986
33987			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
33988			var doc = document;
33989			if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
33990				style.lineHeight = (rect.h - borderH) + 'px';
33991			}
33992
33993			borderBox = self._borderBox;
33994			borderW = borderBox.left + borderBox.right + 8;
33995			borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0);
33996
33997			if (rect.x !== lastRepaintRect.x) {
33998				style.left = rect.x + 'px';
33999				lastRepaintRect.x = rect.x;
34000			}
34001
34002			if (rect.y !== lastRepaintRect.y) {
34003				style.top = rect.y + 'px';
34004				lastRepaintRect.y = rect.y;
34005			}
34006
34007			if (rect.w !== lastRepaintRect.w) {
34008				style.width = (rect.w - borderW) + 'px';
34009				lastRepaintRect.w = rect.w;
34010			}
34011
34012			if (rect.h !== lastRepaintRect.h) {
34013				style.height = (rect.h - borderH) + 'px';
34014				lastRepaintRect.h = rect.h;
34015			}
34016
34017			self._lastRepaintRect = lastRepaintRect;
34018			self.fire('repaint', {}, false);
34019
34020			return self;
34021		},
34022
34023		/**
34024		 * Renders the control as a HTML string.
34025		 *
34026		 * @method renderHtml
34027		 * @return {String} HTML representing the control.
34028		 */
34029		renderHtml: function() {
34030			var self = this, id = self._id, settings = self.settings, value = self.encode(self._value, false), extraAttrs = '';
34031
34032			if ("spellcheck" in settings) {
34033				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
34034			}
34035
34036			if (settings.maxLength) {
34037				extraAttrs += ' maxlength="' + settings.maxLength + '"';
34038			}
34039
34040			if (settings.size) {
34041				extraAttrs += ' size="' + settings.size + '"';
34042			}
34043
34044			if (settings.subtype) {
34045				extraAttrs += ' type="' + settings.subtype + '"';
34046			}
34047
34048			if (self.disabled()) {
34049				extraAttrs += ' disabled="disabled"';
34050			}
34051
34052			if (settings.multiline) {
34053				return (
34054					'<textarea id="' + id + '" class="' + self.classes() + '" ' +
34055					(settings.rows ? ' rows="' + settings.rows + '"' : '') +
34056					' hidefocus="true"' + extraAttrs + '>' + value +
34057					'</textarea>'
34058				);
34059			}
34060
34061			return '<input id="' + id + '" class="' + self.classes() + '" value="' + value + '" hidefocus="true"' + extraAttrs + '>';
34062		},
34063
34064		/**
34065		 * Called after the control has been rendered.
34066		 *
34067		 * @method postRender
34068		 */
34069		postRender: function() {
34070			var self = this;
34071
34072			DomUtils.on(self.getEl(), 'change', function(e) {
34073				self.fire('change', e);
34074			});
34075
34076			return self._super();
34077		},
34078
34079		remove: function() {
34080			DomUtils.off(this.getEl());
34081			this._super();
34082		}
34083	});
34084});
34085
34086// Included from: js/tinymce/classes/ui/Throbber.js
34087
34088/**
34089 * Throbber.js
34090 *
34091 * Copyright, Moxiecode Systems AB
34092 * Released under LGPL License.
34093 *
34094 * License: http://www.tinymce.com/license
34095 * Contributing: http://www.tinymce.com/contributing
34096 */
34097
34098/**
34099 * This class enables you to display a Throbber for any element.
34100 *
34101 * @-x-less Throbber.less
34102 * @class tinymce.ui.Throbber
34103 */
34104define("tinymce/ui/Throbber", [
34105	"tinymce/ui/DomUtils"
34106], function(DomUtils) {
34107	"use strict";
34108
34109	/**
34110	 * Constructs a new throbber.
34111	 *
34112	 * @constructor
34113	 * @param {Element} elm DOM Html element to display throbber in.
34114	 */
34115	return function(elm) {
34116		var self = this, state;
34117
34118		/**
34119		 * Shows the throbber.
34120		 *
34121		 * @method show
34122		 * @param {Number} [time] Time to wait before showing.
34123		 * @return {tinymce.ui.Throbber} Current throbber instance.
34124		 */
34125		self.show = function(time) {
34126			self.hide();
34127
34128			state = true;
34129
34130			window.setTimeout(function() {
34131				if (state) {
34132					elm.appendChild(DomUtils.createFragment('<div class="mce-throbber"></div>'));
34133				}
34134			}, time || 0);
34135
34136			return self;
34137		};
34138
34139		/**
34140		 * Hides the throbber.
34141		 *
34142		 * @method hide
34143		 * @return {tinymce.ui.Throbber} Current throbber instance.
34144		 */
34145		self.hide = function() {
34146			var child = elm.lastChild;
34147
34148			if (child && child.className.indexOf('throbber') != -1) {
34149				child.parentNode.removeChild(child);
34150			}
34151
34152			state = false;
34153
34154			return self;
34155		};
34156	};
34157});
34158
34159expose(["tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/util/Tools","tinymce/dom/Range","tinymce/html/Entities","tinymce/Env","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","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/Selection","tinymce/dom/RangeUtils","tinymce/Formatter","tinymce/UndoManager","tinymce/EnterKey","tinymce/ForceBlocks","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/DomUtils","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/KeyboardNavigation","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/util/Quirks","tinymce/util/Observable","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/PanelButton","tinymce/ui/ColorButton","tinymce/ui/ComboBox","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"]);
34160})(this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.