PageSourceSearch

https://wreczyca-wielka.pl/js/tiny_mce/tiny_mce.js

js wreczyca-wielka.pl collected 2026-10-02 22:36:37 UTC 506,845 bytes, 19,175 lines download raw bytes

1// FILE IS GENERATED BY COMBINING THE SOURCES IN THE "classes" DIRECTORY SO DON'T MODIFY THIS FILE DIRECTLY
2(function(win) {
3	var whiteSpaceRe = /^\s*|\s*$/g,
4		undef, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
5
6	var tinymce = {
7		majorVersion : '3',
8
9		minorVersion : '5.11',
10
11		releaseDate : '2014-05-08',
12
13		_init : function() {
14			var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
15
16			t.isIE11 = ua.indexOf('Trident/') != -1 && (ua.indexOf('rv:') != -1 || na.appName.indexOf('Netscape') != -1);
17
18			t.isOpera = win.opera && opera.buildNumber;
19
20			t.isWebKit = /WebKit/.test(ua);
21
22			t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName) || t.isIE11;
23
24			t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
25
26			t.isIE7 = t.isIE && /MSIE [7]/.test(ua);
27
28			t.isIE8 = t.isIE && /MSIE [8]/.test(ua);
29
30			t.isIE9 = t.isIE && /MSIE [9]/.test(ua);
31
32			t.isGecko = !t.isWebKit && !t.isIE11 && /Gecko/.test(ua);
33
34			t.isMac = ua.indexOf('Mac') != -1;
35
36			t.isAir = /adobeair/i.test(ua);
37
38			t.isIDevice = /(iPad|iPhone)/.test(ua);
39			
40			t.isIOS5 = t.isIDevice && ua.match(/AppleWebKit\/(\d*)/)[1]>=534;
41
42			// TinyMCE .NET webcontrol might be setting the values for TinyMCE
43			if (win.tinyMCEPreInit) {
44				t.suffix = tinyMCEPreInit.suffix;
45				t.baseURL = tinyMCEPreInit.base;
46				t.query = tinyMCEPreInit.query;
47				return;
48			}
49
50			// Get suffix and base
51			t.suffix = '';
52
53			// If base element found, add that infront of baseURL
54			nl = d.getElementsByTagName('base');
55			for (i=0; i<nl.length; i++) {
56				v = nl[i].href;
57				if (v) {
58					// Host only value like http://site.com or http://site.com:8008
59					if (/^https?:\/\/[^\/]+$/.test(v))
60						v += '/';
61
62					base = v ? v.match(/.*\//)[0] : ''; // Get only directory
63				}
64			}
65
66			function getBase(n) {
67				if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {
68					if (/_(src|dev)\.js/g.test(n.src))
69						t.suffix = '_src';
70
71					if ((p = n.src.indexOf('?')) != -1)
72						t.query = n.src.substring(p + 1);
73
74					t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
75
76					// If path to script is relative and a base href was found add that one infront
77					// the src property will always be an absolute one on non IE browsers and IE 8
78					// so this logic will basically only be executed on older IE versions
79					if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)
80						t.baseURL = base + t.baseURL;
81
82					return t.baseURL;
83				}
84
85				return null;
86			};
87
88			// Check document
89			nl = d.getElementsByTagName('script');
90			for (i=0; i<nl.length; i++) {
91				if (getBase(nl[i]))
92					return;
93			}
94
95			// Check head
96			n = d.getElementsByTagName('head')[0];
97			if (n) {
98				nl = n.getElementsByTagName('script');
99				for (i=0; i<nl.length; i++) {
100					if (getBase(nl[i]))
101						return;
102				}
103			}
104
105			return;
106		},
107
108		is : function(o, t) {
109			if (!t)
110				return o !== undef;
111
112			if (t == 'array' && tinymce.isArray(o))
113				return true;
114
115			return typeof(o) == t;
116		},
117
118		isArray: Array.isArray || function(obj) {
119			return Object.prototype.toString.call(obj) === "[object Array]";
120		},
121
122		makeMap : function(items, delim, map) {
123			var i;
124
125			items = items || [];
126			delim = delim || ',';
127
128			if (typeof(items) == "string")
129				items = items.split(delim);
130
131			map = map || {};
132
133			i = items.length;
134			while (i--)
135				map[items[i]] = {};
136
137			return map;
138		},
139
140		each : function(o, cb, s) {
141			var n, l;
142
143			if (!o)
144				return 0;
145
146			s = s || o;
147
148			if (o.length !== undef) {
149				// Indexed arrays, needed for Safari
150				for (n=0, l = o.length; n < l; n++) {
151					if (cb.call(s, o[n], n, o) === false)
152						return 0;
153				}
154			} else {
155				// Hashtables
156				for (n in o) {
157					if (o.hasOwnProperty(n)) {
158						if (cb.call(s, o[n], n, o) === false)
159							return 0;
160					}
161				}
162			}
163
164			return 1;
165		},
166
167
168		map : function(a, f) {
169			var o = [];
170
171			tinymce.each(a, function(v) {
172				o.push(f(v));
173			});
174
175			return o;
176		},
177
178		grep : function(a, f) {
179			var o = [];
180
181			tinymce.each(a, function(v) {
182				if (!f || f(v))
183					o.push(v);
184			});
185
186			return o;
187		},
188
189		inArray : function(a, v) {
190			var i, l;
191
192			if (a) {
193				for (i = 0, l = a.length; i < l; i++) {
194					if (a[i] === v)
195						return i;
196				}
197			}
198
199			return -1;
200		},
201
202		extend : function(obj, ext) {
203			var i, l, name, args = arguments, value;
204
205			for (i = 1, l = args.length; i < l; i++) {
206				ext = args[i];
207				for (name in ext) {
208					if (ext.hasOwnProperty(name)) {
209						value = ext[name];
210
211						if (value !== undef) {
212							obj[name] = value;
213						}
214					}
215				}
216			}
217
218			return obj;
219		},
220
221
222		trim : function(s) {
223			return (s ? '' + s : '').replace(whiteSpaceRe, '');
224		},
225
226		create : function(s, p, root) {
227			var t = this, sp, ns, cn, scn, c, de = 0;
228
229			// Parse : <prefix> <class>:<super class>
230			s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
231			cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
232
233			// Create namespace for new class
234			ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);
235
236			// Class already exists
237			if (ns[cn])
238				return;
239
240			// Make pure static class
241			if (s[2] == 'static') {
242				ns[cn] = p;
243
244				if (this.onCreate)
245					this.onCreate(s[2], s[3], ns[cn]);
246
247				return;
248			}
249
250			// Create default constructor
251			if (!p[cn]) {
252				p[cn] = function() {};
253				de = 1;
254			}
255
256			// Add constructor and methods
257			ns[cn] = p[cn];
258			t.extend(ns[cn].prototype, p);
259
260			// Extend
261			if (s[5]) {
262				sp = t.resolve(s[5]).prototype;
263				scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
264
265				// Extend constructor
266				c = ns[cn];
267				if (de) {
268					// Add passthrough constructor
269					ns[cn] = function() {
270						return sp[scn].apply(this, arguments);
271					};
272				} else {
273					// Add inherit constructor
274					ns[cn] = function() {
275						this.parent = sp[scn];
276						return c.apply(this, arguments);
277					};
278				}
279				ns[cn].prototype[cn] = ns[cn];
280
281				// Add super methods
282				t.each(sp, function(f, n) {
283					ns[cn].prototype[n] = sp[n];
284				});
285
286				// Add overridden methods
287				t.each(p, function(f, n) {
288					// Extend methods if needed
289					if (sp[n]) {
290						ns[cn].prototype[n] = function() {
291							this.parent = sp[n];
292							return f.apply(this, arguments);
293						};
294					} else {
295						if (n != cn)
296							ns[cn].prototype[n] = f;
297					}
298				});
299			}
300
301			// Add static methods
302			t.each(p['static'], function(f, n) {
303				ns[cn][n] = f;
304			});
305
306			if (this.onCreate)
307				this.onCreate(s[2], s[3], ns[cn].prototype);
308		},
309
310		walk : function(o, f, n, s) {
311			s = s || this;
312
313			if (o) {
314				if (n)
315					o = o[n];
316
317				tinymce.each(o, function(o, i) {
318					if (f.call(s, o, i, n) === false)
319						return false;
320
321					tinymce.walk(o, f, n, s);
322				});
323			}
324		},
325
326		createNS : function(n, o) {
327			var i, v;
328
329			o = o || win;
330
331			n = n.split('.');
332			for (i=0; i<n.length; i++) {
333				v = n[i];
334
335				if (!o[v])
336					o[v] = {};
337
338				o = o[v];
339			}
340
341			return o;
342		},
343
344		resolve : function(n, o) {
345			var i, l;
346
347			o = o || win;
348
349			n = n.split('.');
350			for (i = 0, l = n.length; i < l; i++) {
351				o = o[n[i]];
352
353				if (!o)
354					break;
355			}
356
357			return o;
358		},
359
360		addUnload : function(f, s) {
361			var t = this, unload;
362
363			unload = function() {
364				var li = t.unloads, o, n;
365
366				if (li) {
367					// Call unload handlers
368					for (n in li) {
369						o = li[n];
370
371						if (o && o.func)
372							o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
373					}
374
375					// Detach unload function
376					if (win.detachEvent) {
377						win.detachEvent('onbeforeunload', fakeUnload);
378						win.detachEvent('onunload', unload);
379					} else if (win.removeEventListener)
380						win.removeEventListener('unload', unload, false);
381
382					// Destroy references
383					t.unloads = o = li = w = unload = 0;
384
385					// Run garbarge collector on IE
386					if (win.CollectGarbage)
387						CollectGarbage();
388				}
389			};
390
391			function fakeUnload() {
392				var d = document;
393
394				function stop() {
395					// Prevent memory leak
396					d.detachEvent('onstop', stop);
397
398					// Call unload handler
399					if (unload)
400						unload();
401
402					d = 0;
403				};
404
405				// Is there things still loading, then do some magic
406				if (d.readyState == 'interactive') {
407					// Fire unload when the currently loading page is stopped
408					if (d)
409						d.attachEvent('onstop', stop);
410
411					// Remove onstop listener after a while to prevent the unload function
412					// to execute if the user presses cancel in an onbeforeunload
413					// confirm dialog and then presses the browser stop button
414					win.setTimeout(function() {
415						if (d)
416							d.detachEvent('onstop', stop);
417					}, 0);
418				}
419			};
420
421			f = {func : f, scope : s || this};
422
423			if (!t.unloads) {
424				// Attach unload handler
425				if (win.attachEvent) {
426					win.attachEvent('onunload', unload);
427					win.attachEvent('onbeforeunload', fakeUnload);
428				} else if (win.addEventListener)
429					win.addEventListener('unload', unload, false);
430
431				// Setup initial unload handler array
432				t.unloads = [f];
433			} else
434				t.unloads.push(f);
435
436			return f;
437		},
438
439		removeUnload : function(f) {
440			var u = this.unloads, r = null;
441
442			tinymce.each(u, function(o, i) {
443				if (o && o.func == f) {
444					u.splice(i, 1);
445					r = f;
446					return false;
447				}
448			});
449
450			return r;
451		},
452
453		explode : function(s, d) {
454			if (!s || tinymce.is(s, 'array')) {
455				return s;
456			}
457
458			return tinymce.map(s.split(d || ','), tinymce.trim);
459		},
460
461		_addVer : function(u) {
462			var v;
463
464			if (!this.query)
465				return u;
466
467			v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
468
469			if (u.indexOf('#') == -1)
470				return u + v;
471
472			return u.replace('#', v + '#');
473		},
474
475		// Fix function for IE 9 where regexps isn't working correctly
476		// Todo: remove me once MS fixes the bug
477		_replace : function(find, replace, str) {
478			// On IE9 we have to fake $x replacement
479			if (isRegExpBroken) {
480				return str.replace(find, function() {
481					var val = replace, args = arguments, i;
482
483					for (i = 0; i < args.length - 2; i++) {
484						if (args[i] === undef) {
485							val = val.replace(new RegExp('\\$' + i, 'g'), '');
486						} else {
487							val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
488						}
489					}
490
491					return val;
492				});
493			}
494
495			return str.replace(find, replace);
496		}
497
498		};
499
500	// Initialize the API
501	tinymce._init();
502
503	// Expose tinymce namespace to the global namespace (window)
504	win.tinymce = win.tinyMCE = tinymce;
505
506	// Describe the different namespaces
507
508	})(window);
509
510
511
512tinymce.create('tinymce.util.Dispatcher', {
513	scope : null,
514	listeners : null,
515	inDispatch: false,
516
517	Dispatcher : function(scope) {
518		this.scope = scope || this;
519		this.listeners = [];
520	},
521
522	add : function(callback, scope) {
523		this.listeners.push({cb : callback, scope : scope || this.scope});
524
525		return callback;
526	},
527
528	addToTop : function(callback, scope) {
529		var self = this, listener = {cb : callback, scope : scope || self.scope};
530
531		// Create new listeners if addToTop is executed in a dispatch loop
532		if (self.inDispatch) {
533			self.listeners = [listener].concat(self.listeners);
534		} else {
535			self.listeners.unshift(listener);
536		}
537
538		return callback;
539	},
540
541	remove : function(callback) {
542		var listeners = this.listeners, output = null;
543
544		tinymce.each(listeners, function(listener, i) {
545			if (callback == listener.cb) {
546				output = listener;
547				listeners.splice(i, 1);
548				return false;
549			}
550		});
551
552		return output;
553	},
554
555	dispatch : function() {
556		var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener;
557
558		self.inDispatch = true;
559		
560		// Needs to be a real loop since the listener count might change while looping
561		// And this is also more efficient
562		for (i = 0; i < listeners.length; i++) {
563			listener = listeners[i];
564			returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]);
565
566			if (returnValue === false)
567				break;
568		}
569
570		self.inDispatch = false;
571
572		return returnValue;
573	}
574
575	});
576
577(function() {
578	var each = tinymce.each;
579
580	tinymce.create('tinymce.util.URI', {
581		URI : function(u, s) {
582			var t = this, o, a, b, base_url;
583
584			// Trim whitespace
585			u = tinymce.trim(u);
586
587			// Default settings
588			s = t.settings = s || {};
589
590			// Strange app protocol that isn't http/https or local anchor
591			// For example: mailto,skype,tel etc.
592			if (/^([\w\-]+):([^\/]{2})/i.test(u) || /^\s*#/.test(u)) {
593				t.source = u;
594				return;
595			}
596
597			// Absolute path with no host, fake host and protocol
598			if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
599				u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
600
601			// Relative path http:// or protocol relative //path
602			if (!/^[\w\-]*:?\/\//.test(u)) {
603				base_url = s.base_uri ? s.base_uri.path : new tinymce.util.URI(location.href).directory;
604				u = ((s.base_uri && s.base_uri.protocol) || 'http') + '://mce_host' + t.toAbsPath(base_url, u);
605			}
606
607			// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
608			u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
609			u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
610			each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
611				var s = u[i];
612
613				// Zope 3 workaround, they use @@something
614				if (s)
615					s = s.replace(/\(mce_at\)/g, '@@');
616
617				t[v] = s;
618			});
619
620			b = s.base_uri;
621			if (b) {
622				if (!t.protocol)
623					t.protocol = b.protocol;
624
625				if (!t.userInfo)
626					t.userInfo = b.userInfo;
627
628				if (!t.port && t.host === 'mce_host')
629					t.port = b.port;
630
631				if (!t.host || t.host === 'mce_host')
632					t.host = b.host;
633
634				t.source = '';
635			}
636
637			//t.path = t.path || '/';
638		},
639
640		setPath : function(p) {
641			var t = this;
642
643			p = /^(.*?)\/?(\w+)?$/.exec(p);
644
645			// Update path parts
646			t.path = p[0];
647			t.directory = p[1];
648			t.file = p[2];
649
650			// Rebuild source
651			t.source = '';
652			t.getURI();
653		},
654
655		toRelative : function(u) {
656			var t = this, o;
657
658			if (u === "./")
659				return u;
660
661			u = new tinymce.util.URI(u, {base_uri : t});
662
663			// Not on same domain/port or protocol
664			if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
665				return u.getURI();
666
667			var tu = t.getURI(), uu = u.getURI();
668			
669			// Allow usage of the base_uri when relative_urls = true
670			if(tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu))
671				return tu;
672
673			o = t.toRelPath(t.path, u.path);
674
675			// Add query
676			if (u.query)
677				o += '?' + u.query;
678
679			// Add anchor
680			if (u.anchor)
681				o += '#' + u.anchor;
682
683			return o;
684		},
685	
686		toAbsolute : function(u, nh) {
687			u = new tinymce.util.URI(u, {base_uri : this});
688
689			return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
690		},
691
692		toRelPath : function(base, path) {
693			var items, bp = 0, out = '', i, l;
694
695			// Split the paths
696			base = base.substring(0, base.lastIndexOf('/'));
697			base = base.split('/');
698			items = path.split('/');
699
700			if (base.length >= items.length) {
701				for (i = 0, l = base.length; i < l; i++) {
702					if (i >= items.length || base[i] != items[i]) {
703						bp = i + 1;
704						break;
705					}
706				}
707			}
708
709			if (base.length < items.length) {
710				for (i = 0, l = items.length; i < l; i++) {
711					if (i >= base.length || base[i] != items[i]) {
712						bp = i + 1;
713						break;
714					}
715				}
716			}
717
718			if (bp === 1)
719				return path;
720
721			for (i = 0, l = base.length - (bp - 1); i < l; i++)
722				out += "../";
723
724			for (i = bp - 1, l = items.length; i < l; i++) {
725				if (i != bp - 1)
726					out += "/" + items[i];
727				else
728					out += items[i];
729			}
730
731			return out;
732		},
733
734		toAbsPath : function(base, path) {
735			var i, nb = 0, o = [], tr, outPath;
736
737			// Split paths
738			tr = /\/$/.test(path) ? '/' : '';
739			base = base.split('/');
740			path = path.split('/');
741
742			// Remove empty chunks
743			each(base, function(k) {
744				if (k)
745					o.push(k);
746			});
747
748			base = o;
749
750			// Merge relURLParts chunks
751			for (i = path.length - 1, o = []; i >= 0; i--) {
752				// Ignore empty or .
753				if (path[i].length === 0 || path[i] === ".")
754					continue;
755
756				// Is parent
757				if (path[i] === '..') {
758					nb++;
759					continue;
760				}
761
762				// Move up
763				if (nb > 0) {
764					nb--;
765					continue;
766				}
767
768				o.push(path[i]);
769			}
770
771			i = base.length - nb;
772
773			// If /a/b/c or /
774			if (i <= 0)
775				outPath = o.reverse().join('/');
776			else
777				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
778
779			// Add front / if it's needed
780			if (outPath.indexOf('/') !== 0)
781				outPath = '/' + outPath;
782
783			// Add traling / if it's needed
784			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)
785				outPath += tr;
786
787			return outPath;
788		},
789
790		getURI : function(nh) {
791			var s, t = this;
792
793			// Rebuild source
794			if (!t.source || nh) {
795				s = '';
796
797				if (!nh) {
798					if (t.protocol)
799						s += t.protocol + '://';
800
801					if (t.userInfo)
802						s += t.userInfo + '@';
803
804					if (t.host)
805						s += t.host;
806
807					if (t.port)
808						s += ':' + t.port;
809				}
810
811				if (t.path)
812					s += t.path;
813
814				if (t.query)
815					s += '?' + t.query;
816
817				if (t.anchor)
818					s += '#' + t.anchor;
819
820				t.source = s;
821			}
822
823			return t.source;
824		}
825	});
826})();
827
828(function() {
829	var each = tinymce.each;
830
831	tinymce.create('static tinymce.util.Cookie', {
832		getHash : function(n) {
833			var v = this.get(n), h;
834
835			if (v) {
836				each(v.split('&'), function(v) {
837					v = v.split('=');
838					h = h || {};
839					h[unescape(v[0])] = unescape(v[1]);
840				});
841			}
842
843			return h;
844		},
845
846		setHash : function(n, v, e, p, d, s) {
847			var o = '';
848
849			each(v, function(v, k) {
850				o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
851			});
852
853			this.set(n, o, e, p, d, s);
854		},
855
856		get : function(n) {
857			var c = document.cookie, e, p = n + "=", b;
858
859			// Strict mode
860			if (!c)
861				return;
862
863			b = c.indexOf("; " + p);
864
865			if (b == -1) {
866				b = c.indexOf(p);
867
868				if (b !== 0)
869					return null;
870			} else
871				b += 2;
872
873			e = c.indexOf(";", b);
874
875			if (e == -1)
876				e = c.length;
877
878			return unescape(c.substring(b + p.length, e));
879		},
880
881		set : function(n, v, e, p, d, s) {
882			document.cookie = n + "=" + escape(v) +
883				((e) ? "; expires=" + e.toGMTString() : "") +
884				((p) ? "; path=" + escape(p) : "") +
885				((d) ? "; domain=" + d : "") +
886				((s) ? "; secure" : "");
887		},
888
889		remove : function(name, path, domain) {
890			var date = new Date();
891
892			date.setTime(date.getTime() - 1000);
893
894			this.set(name, '', date, path, domain);
895		}
896	});
897})();
898
899(function() {
900	function serialize(o, quote) {
901		var i, v, t, name;
902
903		quote = quote || '"';
904
905		if (o == null)
906			return 'null';
907
908		t = typeof o;
909
910		if (t == 'string') {
911			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
912
913			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
914				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
915				if (quote === '"' && a === "'")
916					return a;
917
918				i = v.indexOf(b);
919
920				if (i + 1)
921					return '\\' + v.charAt(i + 1);
922
923				a = b.charCodeAt().toString(16);
924
925				return '\\u' + '0000'.substring(a.length) + a;
926			}) + quote;
927		}
928
929		if (t == 'object') {
930			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
931					for (i=0, v = '['; i<o.length; i++)
932						v += (i > 0 ? ',' : '') + serialize(o[i], quote);
933
934					return v + ']';
935				}
936
937				v = '{';
938
939				for (name in o) {
940					if (o.hasOwnProperty(name)) {
941						v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + quote +':' + serialize(o[name], quote) : '';
942					}
943				}
944
945				return v + '}';
946		}
947
948		return '' + o;
949	};
950
951	tinymce.util.JSON = {
952		serialize: serialize,
953
954		parse: function(s) {
955			try {
956				return eval('(' + s + ')');
957			} catch (ex) {
958				// Ignore
959			}
960		}
961
962		};
963})();
964
965tinymce.create('static tinymce.util.XHR', {
966	send : function(o) {
967		var x, t, w = window, c = 0;
968
969		function ready() {
970			if (!o.async || x.readyState == 4 || c++ > 10000) {
971				if (o.success && c < 10000 && x.status == 200)
972					o.success.call(o.success_scope, '' + x.responseText, x, o);
973				else if (o.error)
974					o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
975
976				x = null;
977			} else
978				w.setTimeout(ready, 10);
979		};
980
981		// Default settings
982		o.scope = o.scope || this;
983		o.success_scope = o.success_scope || o.scope;
984		o.error_scope = o.error_scope || o.scope;
985		o.async = o.async === false ? false : true;
986		o.data = o.data || '';
987
988		function get(s) {
989			x = 0;
990
991			try {
992				x = new ActiveXObject(s);
993			} catch (ex) {
994			}
995
996			return x;
997		};
998
999		x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
1000
1001		if (x) {
1002			if (x.overrideMimeType)
1003				x.overrideMimeType(o.content_type);
1004
1005			x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
1006
1007			if (o.content_type)
1008				x.setRequestHeader('Content-Type', o.content_type);
1009
1010			x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
1011
1012			x.send(o.data);
1013
1014			// Syncronous request
1015			if (!o.async)
1016				return ready();
1017
1018			// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
1019			t = w.setTimeout(ready, 10);
1020		}
1021	}
1022});
1023
1024(function() {
1025	var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
1026
1027	tinymce.create('tinymce.util.JSONRequest', {
1028		JSONRequest : function(s) {
1029			this.settings = extend({
1030			}, s);
1031			this.count = 0;
1032		},
1033
1034		send : function(o) {
1035			var ecb = o.error, scb = o.success;
1036
1037			o = extend(this.settings, o);
1038
1039			o.success = function(c, x) {
1040				c = JSON.parse(c);
1041
1042				if (typeof(c) == 'undefined') {
1043					c = {
1044						error : 'JSON Parse error.'
1045					};
1046				}
1047
1048				if (c.error)
1049					ecb.call(o.error_scope || o.scope, c.error, x);
1050				else
1051					scb.call(o.success_scope || o.scope, c.result);
1052			};
1053
1054			o.error = function(ty, x) {
1055				if (ecb)
1056					ecb.call(o.error_scope || o.scope, ty, x);
1057			};
1058
1059			o.data = JSON.serialize({
1060				id : o.id || 'c' + (this.count++),
1061				method : o.method,
1062				params : o.params
1063			});
1064
1065			// JSON content type for Ruby on rails. Bug: #1883287
1066			o.content_type = 'application/json';
1067
1068			XHR.send(o);
1069		},
1070
1071		'static' : {
1072			sendRPC : function(o) {
1073				return new tinymce.util.JSONRequest().send(o);
1074			}
1075		}
1076	});
1077}());
1078(function(tinymce){
1079	tinymce.VK = {
1080		BACKSPACE: 8,
1081		DELETE: 46,
1082		DOWN: 40,
1083		ENTER: 13,
1084		LEFT: 37,
1085		RIGHT: 39,
1086		SPACEBAR: 32,
1087		TAB: 9,
1088		UP: 38,
1089
1090		modifierPressed: function (e) {
1091			return e.shiftKey || e.ctrlKey || e.altKey;
1092		},
1093
1094		metaKeyPressed: function(e) {
1095			// Check if ctrl or meta key is pressed also check if alt is false for Polish users
1096			return tinymce.isMac ? e.metaKey : e.ctrlKey && !e.altKey;
1097		}
1098	};
1099})(tinymce);
1100
1101tinymce.util.Quirks = function(editor) {
1102	var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
1103		settings = editor.settings, parser = editor.parser, serializer = editor.serializer, each = tinymce.each;
1104
1105	function setEditorCommandState(cmd, state) {
1106		try {
1107			editor.getDoc().execCommand(cmd, false, state);
1108		} catch (ex) {
1109			// Ignore
1110		}
1111	}
1112
1113	function getDocumentMode() {
1114		var documentMode = editor.getDoc().documentMode;
1115
1116		return documentMode ? documentMode : 6;
1117	};
1118
1119	function isDefaultPrevented(e) {
1120		return e.isDefaultPrevented();
1121	};
1122
1123	function cleanupStylesWhenDeleting() {
1124		function removeMergedFormatSpans(isDelete) {
1125			var rng, blockElm, wrapperElm, bookmark, container, offset, elm;
1126
1127			function isAtStartOrEndOfElm() {
1128				if (container.nodeType == 3) {
1129					if (isDelete && offset == container.length) {
1130						return true;
1131					}
1132
1133					if (!isDelete && offset === 0) {
1134						return true;
1135					}
1136				}
1137			}
1138
1139			rng = selection.getRng();
1140			var tmpRng = [rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset];
1141
1142			if (!rng.collapsed) {
1143				isDelete = true;
1144			}
1145
1146			container = rng[(isDelete ? 'start' : 'end') + 'Container'];
1147			offset = rng[(isDelete ? 'start' : 'end') + 'Offset'];
1148
1149			if (container.nodeType == 3) {
1150				blockElm = dom.getParent(rng.startContainer, dom.isBlock);
1151
1152				// On delete clone the root span of the next block element
1153				if (isDelete) {
1154					blockElm = dom.getNext(blockElm, dom.isBlock);
1155				}
1156
1157				if (blockElm && (isAtStartOrEndOfElm() || !rng.collapsed)) {
1158					// Wrap children of block in a EM and let WebKit stick is
1159					// runtime styles junk into that EM
1160					wrapperElm = dom.create('em', {'id': '__mceDel'});
1161
1162					each(tinymce.grep(blockElm.childNodes), function(node) {
1163						wrapperElm.appendChild(node);
1164					});
1165
1166					blockElm.appendChild(wrapperElm);
1167				}
1168			}
1169
1170			// Do the backspace/delete action
1171			rng = dom.createRng();
1172			rng.setStart(tmpRng[0], tmpRng[1]);
1173			rng.setEnd(tmpRng[2], tmpRng[3]);
1174			selection.setRng(rng);
1175			editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
1176
1177			// Remove temp wrapper element
1178			if (wrapperElm) {
1179				bookmark = selection.getBookmark();
1180
1181				while (elm = dom.get('__mceDel')) {
1182					dom.remove(elm, true);
1183				}
1184
1185				selection.moveToBookmark(bookmark);
1186			}
1187		}
1188
1189		editor.onKeyDown.add(function(editor, e) {
1190			var isDelete;
1191
1192			isDelete = e.keyCode == DELETE;
1193			if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
1194				e.preventDefault();
1195				removeMergedFormatSpans(isDelete);
1196			}
1197		});
1198
1199		editor.addCommand('Delete', function() {removeMergedFormatSpans();});
1200	};
1201	
1202	function emptyEditorWhenDeleting() {
1203		function serializeRng(rng) {
1204			var body = dom.create("body");
1205			var contents = rng.cloneContents();
1206			body.appendChild(contents);
1207			return selection.serializer.serialize(body, {format: 'html'});
1208		}
1209
1210		function allContentsSelected(rng) {
1211			var selection = serializeRng(rng);
1212
1213			var allRng = dom.createRng();
1214			allRng.selectNode(editor.getBody());
1215
1216			var allSelection = serializeRng(allRng);
1217			return selection === allSelection;
1218		}
1219
1220		editor.onKeyDown.add(function(editor, e) {
1221			var keyCode = e.keyCode, isCollapsed;
1222
1223			// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
1224			if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
1225				isCollapsed = editor.selection.isCollapsed();
1226
1227				// Selection is collapsed but the editor isn't empty
1228				if (isCollapsed && !dom.isEmpty(editor.getBody())) {
1229					return;
1230				}
1231
1232				// IE deletes all contents correctly when everything is selected
1233				if (tinymce.isIE && !isCollapsed) {
1234					return;
1235				}
1236
1237				// Selection isn't collapsed but not all the contents is selected
1238				if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
1239					return;
1240				}
1241
1242				// Manually empty the editor
1243				editor.setContent('');
1244				editor.selection.setCursorLocation(editor.getBody(), 0);
1245				editor.nodeChanged();
1246			}
1247		});
1248	};
1249
1250	function selectAll() {
1251		editor.onKeyDown.add(function(editor, e) {
1252			if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
1253				e.preventDefault();
1254				editor.execCommand('SelectAll');
1255			}
1256		});
1257	};
1258
1259	function inputMethodFocus() {
1260		if (!editor.settings.content_editable) {
1261			// Case 1 IME doesn't initialize if you focus the document
1262			dom.bind(editor.getDoc(), 'focusin', function(e) {
1263				selection.setRng(selection.getRng());
1264			});
1265
1266			// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
1267			dom.bind(editor.getDoc(), 'mousedown', function(e) {
1268				if (e.target == editor.getDoc().documentElement) {
1269					editor.getWin().focus();
1270					selection.setRng(selection.getRng());
1271				}
1272			});
1273		}
1274	};
1275
1276	function removeHrOnBackspace() {
1277		editor.onKeyDown.add(function(editor, e) {
1278			if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
1279				if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
1280					var node = selection.getNode();
1281					var previousSibling = node.previousSibling;
1282
1283					if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
1284						dom.remove(previousSibling);
1285						tinymce.dom.Event.cancel(e);
1286					}
1287				}
1288			}
1289		})
1290	}
1291
1292	function focusBody() {
1293		// Fix for a focus bug in FF 3.x where the body element
1294		// wouldn't get proper focus if the user clicked on the HTML element
1295		if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
1296			editor.onMouseDown.add(function(editor, e) {
1297				if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
1298					var body = editor.getBody();
1299
1300					// Blur the body it's focused but not correctly focused
1301					body.blur();
1302
1303					// Refocus the body after a little while
1304					setTimeout(function() {
1305						body.focus();
1306					}, 0);
1307				}
1308			});
1309		}
1310	};
1311
1312function selectControlElements() {
1313		editor.onClick.add(function(editor, e) {
1314			e = e.target;
1315
1316			// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
1317			// WebKit can't even do simple things like selecting an image
1318			// Needs tobe the setBaseAndExtend or it will fail to select floated images
1319			if (/^(IMG|HR)$/.test(e.nodeName)) {
1320				try {
1321					selection.getSel().setBaseAndExtent(e, 0, e, 1);
1322				}
1323				catch (x) {
1324					// On Chrome 58 and up, the above throws an exception. Try this instead.
1325					selection.select(e);
1326				}
1327 			}
1328 
1329 			if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) {
1330				selection.select(e);
1331			}
1332
1333			editor.nodeChanged();
1334		});
1335	};
1336
1337	function removeStylesWhenDeletingAccrossBlockElements() {
1338		function getAttributeApplyFunction() {
1339			var template = dom.getAttribs(selection.getStart().cloneNode(false));
1340
1341			return function() {
1342				var target = selection.getStart();
1343
1344				if (target !== editor.getBody()) {
1345					dom.setAttrib(target, "style", null);
1346
1347					each(template, function(attr) {
1348						target.setAttributeNode(attr.cloneNode(true));
1349					});
1350				}
1351			};
1352		}
1353
1354		function isSelectionAcrossElements() {
1355			return !selection.isCollapsed() && dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
1356		}
1357
1358		function blockEvent(editor, e) {
1359			e.preventDefault();
1360			return false;
1361		}
1362
1363		editor.onKeyPress.add(function(editor, e) {
1364			var applyAttributes;
1365
1366			if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
1367				applyAttributes = getAttributeApplyFunction();
1368				editor.getDoc().execCommand('delete', false, null);
1369				applyAttributes();
1370				e.preventDefault();
1371				return false;
1372			}
1373		});
1374
1375		dom.bind(editor.getDoc(), 'cut', function(e) {
1376			var applyAttributes;
1377
1378			if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
1379				applyAttributes = getAttributeApplyFunction();
1380				editor.onKeyUp.addToTop(blockEvent);
1381
1382				setTimeout(function() {
1383					applyAttributes();
1384					editor.onKeyUp.remove(blockEvent);
1385				}, 0);
1386			}
1387		});
1388	}
1389
1390	function selectionChangeNodeChanged() {
1391		var lastRng, selectionTimer;
1392
1393		dom.bind(editor.getDoc(), 'selectionchange', function() {
1394			if (selectionTimer) {
1395				clearTimeout(selectionTimer);
1396				selectionTimer = 0;
1397			}
1398
1399			selectionTimer = window.setTimeout(function() {
1400				var rng = selection.getRng();
1401
1402				// Compare the ranges to see if it was a real change or not
1403				if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {
1404					editor.nodeChanged();
1405					lastRng = rng;
1406				}
1407			}, 50);
1408		});
1409	}
1410
1411	function ensureBodyHasRoleApplication() {
1412		document.body.setAttribute("role", "application");
1413	}
1414
1415	function disableBackspaceIntoATable() {
1416		editor.onKeyDown.add(function(editor, e) {
1417			if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
1418				if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
1419					var previousSibling = selection.getNode().previousSibling;
1420					if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
1421						return tinymce.dom.Event.cancel(e);
1422					}
1423				}
1424			}
1425		})
1426	}
1427
1428	function addNewLinesBeforeBrInPre() {
1429		// IE8+ rendering mode does the right thing with BR in PRE
1430		if (getDocumentMode() > 7) {
1431			return;
1432		}
1433
1434		 // Enable display: none in area and add a specific class that hides all BR elements in PRE to
1435		 // avoid the caret from getting stuck at the BR elements while pressing the right arrow key
1436		setEditorCommandState('RespectVisibilityInDesign', true);
1437		editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
1438		dom.addClass(editor.getBody(), 'mceHideBrInPre');
1439
1440		// Adds a \n before all BR elements in PRE to get them visual
1441		parser.addNodeFilter('pre', function(nodes, name) {
1442			var i = nodes.length, brNodes, j, brElm, sibling;
1443
1444			while (i--) {
1445				brNodes = nodes[i].getAll('br');
1446				j = brNodes.length;
1447				while (j--) {
1448					brElm = brNodes[j];
1449
1450					// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
1451					sibling = brElm.prev;
1452					if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
1453						sibling.value += '\n';
1454					} else {
1455						brElm.parent.insert(new tinymce.html.Node('#text', 3), brElm, true).value = '\n';
1456					}
1457				}
1458			}
1459		});
1460
1461		// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
1462		serializer.addNodeFilter('pre', function(nodes, name) {
1463			var i = nodes.length, brNodes, j, brElm, sibling;
1464
1465			while (i--) {
1466				brNodes = nodes[i].getAll('br');
1467				j = brNodes.length;
1468				while (j--) {
1469					brElm = brNodes[j];
1470					sibling = brElm.prev;
1471					if (sibling && sibling.type == 3) {
1472						sibling.value = sibling.value.replace(/\r?\n$/, '');
1473					}
1474				}
1475			}
1476		});
1477	}
1478
1479	function removePreSerializedStylesWhenSelectingControls() {
1480		dom.bind(editor.getBody(), 'mouseup', function(e) {
1481			var value, node = selection.getNode();
1482
1483			// Moved styles to attributes on IMG eements
1484			if (node.nodeName == 'IMG') {
1485				// Convert style width to width attribute
1486				if (value = dom.getStyle(node, 'width')) {
1487					dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
1488					dom.setStyle(node, 'width', '');
1489				}
1490
1491				// Convert style height to height attribute
1492				if (value = dom.getStyle(node, 'height')) {
1493					dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
1494					dom.setStyle(node, 'height', '');
1495				}
1496			}
1497		});
1498	}
1499
1500	function keepInlineElementOnDeleteBackspace() {
1501		editor.onKeyDown.add(function(editor, e) {
1502			var isDelete, rng, container, offset, brElm, sibling, collapsed;
1503
1504			isDelete = e.keyCode == DELETE;
1505			if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
1506				rng = selection.getRng();
1507				container = rng.startContainer;
1508				offset = rng.startOffset;
1509				collapsed = rng.collapsed;
1510
1511				// Override delete if the start container is a text node and is at the beginning of text or
1512				// just before/after the last character to be deleted in collapsed mode
1513				if (container.nodeType == 3 && container.nodeValue.length > 0 && ((offset === 0 && !collapsed) || (collapsed && offset === (isDelete ? 0 : 1)))) {
1514					// Edge case when deleting <p><b><img> |x</b></p>
1515					sibling = container.previousSibling;
1516					if (sibling && sibling.nodeName == "IMG") {
1517						return;
1518					}
1519
1520					nonEmptyElements = editor.schema.getNonEmptyElements();
1521
1522					// Prevent default logic since it's broken
1523					e.preventDefault();
1524
1525					// Insert a BR before the text node this will prevent the containing element from being deleted/converted
1526					brElm = dom.create('br', {id: '__tmp'});
1527					container.parentNode.insertBefore(brElm, container);
1528
1529					// Do the browser delete
1530					editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
1531
1532					// Check if the previous sibling is empty after deleting for example: <p><b></b>|</p>
1533					container = selection.getRng().startContainer;
1534					sibling = container.previousSibling;
1535					if (sibling && sibling.nodeType == 1 && !dom.isBlock(sibling) && dom.isEmpty(sibling) && !nonEmptyElements[sibling.nodeName.toLowerCase()]) {
1536						dom.remove(sibling);
1537					}
1538
1539					// Remove the temp element we inserted
1540					dom.remove('__tmp');
1541				}
1542			}
1543		});
1544	}
1545
1546	function removeBlockQuoteOnBackSpace() {
1547		// Add block quote deletion handler
1548		editor.onKeyDown.add(function(editor, e) {
1549			var rng, container, offset, root, parent;
1550
1551			if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
1552				return;
1553			}
1554
1555			rng = selection.getRng();
1556			container = rng.startContainer;
1557			offset = rng.startOffset;
1558			root = dom.getRoot();
1559			parent = container;
1560
1561			if (!rng.collapsed || offset !== 0) {
1562				return;
1563			}
1564
1565			while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
1566				parent = parent.parentNode;
1567			}
1568
1569			// Is the cursor at the beginning of a blockquote?
1570			if (parent.tagName === 'BLOCKQUOTE') {
1571				// Remove the blockquote
1572				editor.formatter.toggle('blockquote', null, parent);
1573
1574				// Move the caret to the beginning of container
1575				rng = dom.createRng();
1576				rng.setStart(container, 0);
1577				rng.setEnd(container, 0);
1578				selection.setRng(rng);
1579			}
1580		});
1581	};
1582
1583	function setGeckoEditingOptions() {
1584		function setOpts() {
1585			editor._refreshContentEditable();
1586
1587			setEditorCommandState("StyleWithCSS", false);
1588			setEditorCommandState("enableInlineTableEditing", false);
1589
1590			if (!settings.object_resizing) {
1591				setEditorCommandState("enableObjectResizing", false);
1592			}
1593		};
1594
1595		if (!settings.readonly) {
1596			editor.onBeforeExecCommand.add(setOpts);
1597			editor.onMouseDown.add(setOpts);
1598		}
1599	};
1600
1601	function addBrAfterLastLinks() {
1602		function fixLinks(editor, o) {
1603			each(dom.select('a'), function(node) {
1604				var parentNode = node.parentNode, root = dom.getRoot();
1605
1606				if (parentNode.lastChild === node) {
1607					while (parentNode && !dom.isBlock(parentNode)) {
1608						if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
1609							return;
1610						}
1611
1612						parentNode = parentNode.parentNode;
1613					}
1614
1615					dom.add(parentNode, 'br', {'data-mce-bogus' : 1});
1616				}
1617			});
1618		};
1619
1620		editor.onExecCommand.add(function(editor, cmd) {
1621			if (cmd === 'CreateLink') {
1622				fixLinks(editor);
1623			}
1624		});
1625
1626		editor.onSetContent.add(selection.onSetContent.add(fixLinks));
1627	};
1628
1629	function setDefaultBlockType() {
1630		if (settings.forced_root_block) {
1631			editor.onInit.add(function() {
1632				setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
1633			});
1634		}
1635	}
1636
1637	function removeGhostSelection() {
1638		function repaint(sender, args) {
1639			if (!sender || !args.initial) {
1640				editor.execCommand('mceRepaint');
1641			}
1642		};
1643
1644		editor.onUndo.add(repaint);
1645		editor.onRedo.add(repaint);
1646		editor.onSetContent.add(repaint);
1647	};
1648
1649	function deleteControlItemOnBackSpace() {
1650		editor.onKeyDown.add(function(editor, e) {
1651			var rng;
1652
1653			if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
1654				rng = editor.getDoc().selection.createRange();
1655				if (rng && rng.item) {
1656					e.preventDefault();
1657					editor.undoManager.beforeChange();
1658					dom.remove(rng.item(0));
1659					editor.undoManager.add();
1660				}
1661			}
1662		});
1663	};
1664
1665	function renderEmptyBlocksFix() {
1666		var emptyBlocksCSS;
1667
1668		// IE10+
1669		if (getDocumentMode() >= 10) {
1670			emptyBlocksCSS = '';
1671			each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
1672				emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
1673			});
1674
1675			editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
1676		}
1677	};
1678
1679	function fakeImageResize() {
1680		var selectedElmX, selectedElmY, selectedElm, selectedElmGhost, selectedHandle, startX, startY, startW, startH, ratio,
1681			resizeHandles, width, height, rootDocument = document, editableDoc = editor.getDoc();
1682
1683		if (!settings.object_resizing || settings.webkit_fake_resize === false) {
1684			return;
1685		}
1686
1687		// Try disabling object resizing if WebKit implements resizing in the future
1688		setEditorCommandState("enableObjectResizing", false);
1689
1690		// Details about each resize handle how to scale etc
1691		resizeHandles = {
1692			// Name: x multiplier, y multiplier, delta size x, delta size y
1693			n: [.5, 0, 0, -1],
1694			e: [1, .5, 1, 0],
1695			s: [.5, 1, 0, 1],
1696			w: [0, .5, -1, 0],
1697			nw: [0, 0, -1, -1],
1698			ne: [1, 0, 1, -1],
1699			se: [1, 1, 1, 1],
1700			sw : [0, 1, -1, 1]
1701		};
1702
1703		function resizeElement(e) {
1704			var deltaX, deltaY;
1705
1706			// Calc new width/height
1707			deltaX = e.screenX - startX;
1708			deltaY = e.screenY - startY;
1709
1710			// Calc new size
1711			width = deltaX * selectedHandle[2] + startW;
1712			height = deltaY * selectedHandle[3] + startH;
1713
1714			// Never scale down lower than 5 pixels
1715			width = width < 5 ? 5 : width;
1716			height = height < 5 ? 5 : height;
1717
1718			// Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image
1719			if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {
1720				width = Math.round(height / ratio);
1721				height = Math.round(width * ratio);
1722			}
1723
1724			// Update ghost size
1725			dom.setStyles(selectedElmGhost, {
1726				width: width,
1727				height: height
1728			});
1729
1730			// Update ghost X position if needed
1731			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
1732				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
1733			}
1734
1735			// Update ghost Y position if needed
1736			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
1737				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
1738			}
1739		}
1740
1741		function endResize() {
1742			function setSizeProp(name, value) {
1743				if (value) {
1744					// Resize by using style or attribute
1745					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
1746						dom.setStyle(selectedElm, name, value);
1747					} else {
1748						dom.setAttrib(selectedElm, name, value);
1749					}
1750				}
1751			}
1752
1753			// Set width/height properties
1754			setSizeProp('width', width);
1755			setSizeProp('height', height);
1756
1757			dom.unbind(editableDoc, 'mousemove', resizeElement);
1758			dom.unbind(editableDoc, 'mouseup', endResize);
1759
1760			if (rootDocument != editableDoc) {
1761				dom.unbind(rootDocument, 'mousemove', resizeElement);
1762				dom.unbind(rootDocument, 'mouseup', endResize);
1763			}
1764
1765			// Remove ghost and update resize handle positions
1766			dom.remove(selectedElmGhost);
1767			showResizeRect(selectedElm);
1768		}
1769
1770		function showResizeRect(targetElm) {
1771			var position, targetWidth, targetHeight;
1772
1773			hideResizeRect();
1774
1775			// Get position and size of target
1776			position = dom.getPos(targetElm);
1777			selectedElmX = position.x;
1778			selectedElmY = position.y;
1779			targetWidth = targetElm.offsetWidth;
1780			targetHeight = targetElm.offsetHeight;
1781
1782			// Reset width/height if user selects a new image/table
1783			if (selectedElm != targetElm) {
1784				selectedElm = targetElm;
1785				width = height = 0;
1786			}
1787
1788			each(resizeHandles, function(handle, name) {
1789				var handleElm;
1790
1791				// Get existing or render resize handle
1792				handleElm = dom.get('mceResizeHandle' + name);
1793				if (!handleElm) {
1794					handleElm = dom.add(editableDoc.documentElement, 'div', {
1795						id: 'mceResizeHandle' + name,
1796						'class': 'mceResizeHandle',
1797						style: 'cursor:' + name + '-resize; margin:0; padding:0'
1798					});
1799
1800					dom.bind(handleElm, 'mousedown', function(e) {
1801						e.preventDefault();
1802
1803						endResize();
1804
1805						startX = e.screenX;
1806						startY = e.screenY;
1807						startW = selectedElm.clientWidth;
1808						startH = selectedElm.clientHeight;
1809						ratio = startH / startW;
1810						selectedHandle = handle;
1811
1812						selectedElmGhost = selectedElm.cloneNode(true);
1813						dom.addClass(selectedElmGhost, 'mceClonedResizable');
1814						dom.setStyles(selectedElmGhost, {
1815							left: selectedElmX,
1816							top: selectedElmY,
1817							margin: 0
1818						});
1819
1820						editableDoc.documentElement.appendChild(selectedElmGhost);
1821
1822						dom.bind(editableDoc, 'mousemove', resizeElement);
1823						dom.bind(editableDoc, 'mouseup', endResize);
1824
1825						if (rootDocument != editableDoc) {
1826							dom.bind(rootDocument, 'mousemove', resizeElement);
1827							dom.bind(rootDocument, 'mouseup', endResize);
1828						}
1829					});
1830				} else {
1831					dom.show(handleElm);
1832				}
1833
1834				// Position element
1835				dom.setStyles(handleElm, {
1836					left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
1837					top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
1838				});
1839			});
1840
1841			// Only add resize rectangle on WebKit and only on images
1842			if (!tinymce.isOpera && selectedElm.nodeName == "IMG") {
1843				selectedElm.setAttribute('data-mce-selected', '1');
1844			}
1845		}
1846
1847		function hideResizeRect() {
1848			if (selectedElm) {
1849				selectedElm.removeAttribute('data-mce-selected');
1850			}
1851
1852			for (var name in resizeHandles) {
1853				dom.hide('mceResizeHandle' + name);
1854			}
1855		}
1856
1857		// Add CSS for resize handles, cloned element and selected
1858		editor.contentStyles.push(
1859			'.mceResizeHandle {' +
1860				'position: absolute;' +
1861				'border: 1px solid black;' +
1862				'background: #FFF;' +
1863				'width: 5px;' +
1864				'height: 5px;' +
1865				'z-index: 10000' +
1866			'}' +
1867			'.mceResizeHandle:hover {' +
1868				'background: #000' +
1869			'}' +
1870			'img[data-mce-selected] {' +
1871				'outline: 1px solid black' +
1872			'}' +
1873			'img.mceClonedResizable, table.mceClonedResizable {' +
1874				'position: absolute;' +
1875				'outline: 1px dashed black;' +
1876				'opacity: .5;' +
1877				'z-index: 10000' +
1878			'}'
1879		);
1880
1881		function updateResizeRect() {
1882			var controlElm = dom.getParent(selection.getNode(), 'table,img');
1883
1884			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
1885			each(dom.select('img[data-mce-selected]'), function(img) {
1886				img.removeAttribute('data-mce-selected');
1887			});
1888
1889			if (controlElm) {
1890				showResizeRect(controlElm);
1891			} else {
1892				hideResizeRect();
1893			}
1894		}
1895
1896		// Show/hide resize rect when image is selected
1897		editor.onNodeChange.add(updateResizeRect);
1898
1899		// Fixes WebKit quirk where it returns IMG on getNode if caret is after last image in container
1900		dom.bind(editableDoc, 'selectionchange', updateResizeRect);
1901
1902		// Remove the internal attribute when serializing the DOM
1903		editor.serializer.addAttributeFilter('data-mce-selected', function(nodes, name) {
1904			var i = nodes.length;
1905
1906			while (i--) {
1907				nodes[i].attr(name, null);
1908			}
1909		});
1910	}
1911
1912	function keepNoScriptContents() {
1913		if (getDocumentMode() < 9) {
1914			parser.addNodeFilter('noscript', function(nodes) {
1915				var i = nodes.length, node, textNode;
1916
1917				while (i--) {
1918					node = nodes[i];
1919					textNode = node.firstChild;
1920
1921					if (textNode) {
1922						node.attr('data-mce-innertext', textNode.value);
1923					}
1924				}
1925			});
1926
1927			serializer.addNodeFilter('noscript', function(nodes) {
1928				var i = nodes.length, node, textNode, value;
1929
1930				while (i--) {
1931					node = nodes[i];
1932					textNode = nodes[i].firstChild;
1933
1934					if (textNode) {
1935						textNode.value = tinymce.html.Entities.decode(textNode.value);
1936					} else {
1937						// Old IE can't retain noscript value so an attribute is used to store it
1938						value = node.attributes.map['data-mce-innertext'];
1939						if (value) {
1940							node.attr('data-mce-innertext', null);
1941							textNode = new tinymce.html.Node('#text', 3);
1942							textNode.value = value;
1943							textNode.raw = true;
1944							node.append(textNode);
1945						}
1946					}
1947				}
1948			});
1949		}
1950	}
1951
1952	function bodyHeight() {
1953		editor.contentStyles.push('body {min-height: 100px}');
1954		editor.onClick.add(function(ed, e) {
1955			if (e.target.nodeName == 'HTML') {
1956				editor.execCommand('SelectAll');
1957				editor.selection.collapse(true);
1958				editor.nodeChanged();
1959			}
1960		});
1961	}
1962
1963	function fixControlSelection() {
1964		editor.onInit.add(function() {
1965			var selectedRng;
1966
1967			editor.getBody().addEventListener('mscontrolselect', function(e) {
1968				setTimeout(function() {
1969					if (editor.selection.getNode() != e.target) {
1970						selectedRng = editor.selection.getRng();
1971						selection.fakeRng = editor.dom.createRng();
1972						selection.fakeRng.setStartBefore(e.target);
1973						selection.fakeRng.setEndAfter(e.target);
1974					}
1975				}, 0);
1976			}, false);
1977
1978			editor.getDoc().addEventListener('selectionchange', function(e) {
1979				if (selectedRng && !tinymce.dom.RangeUtils.compareRanges(editor.selection.getRng(), selectedRng)) {
1980					selection.fakeRng = selectedRng = null;
1981				}
1982			}, false);
1983		});
1984	}
1985
1986	// All browsers
1987	disableBackspaceIntoATable();
1988	removeBlockQuoteOnBackSpace();
1989	emptyEditorWhenDeleting();
1990
1991	// WebKit
1992	if (tinymce.isWebKit) {
1993		keepInlineElementOnDeleteBackspace();
1994		cleanupStylesWhenDeleting();
1995		inputMethodFocus();
1996		selectControlElements();
1997		setDefaultBlockType();
1998
1999		// iOS
2000		if (tinymce.isIDevice) {
2001			selectionChangeNodeChanged();
2002		} else {
2003			fakeImageResize();
2004			selectAll();
2005		}
2006	}
2007
2008	// IE
2009	if (tinymce.isIE && !tinymce.isIE11) {
2010		removeHrOnBackspace();
2011		ensureBodyHasRoleApplication();
2012		addNewLinesBeforeBrInPre();
2013		removePreSerializedStylesWhenSelectingControls();
2014		deleteControlItemOnBackSpace();
2015		renderEmptyBlocksFix();
2016		keepNoScriptContents();
2017	}
2018
2019	// IE 11+
2020	if (tinymce.isIE11) {
2021		bodyHeight();
2022		fixControlSelection();
2023	}
2024
2025	// Gecko
2026	if (tinymce.isGecko && !tinymce.isIE11) {
2027		removeHrOnBackspace();
2028		focusBody();
2029		removeStylesWhenDeletingAccrossBlockElements();
2030		setGeckoEditingOptions();
2031		addBrAfterLastLinks();
2032		removeGhostSelection();
2033	}
2034
2035	// Opera
2036	if (tinymce.isOpera) {
2037		fakeImageResize();
2038	}
2039};
2040(function(tinymce) {
2041	var namedEntities, baseEntities, reverseEntities,
2042		attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
2043		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
2044		rawCharsRegExp = /[<>&\"\']/g,
2045		entityRegExp = /&(#x|#)?([\w]+);/g,
2046		asciiMap = {
2047				128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",
2048				135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",
2049				142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",
2050				150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",
2051				156 : "\u0153", 158 : "\u017E", 159 : "\u0178"
2052		};
2053
2054	// Raw entities
2055	baseEntities = {
2056		'\"' : '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code
2057		"'" : '&#39;',
2058		'<' : '&lt;',
2059		'>' : '&gt;',
2060		'&' : '&amp;'
2061	};
2062
2063	// Reverse lookup table for raw entities
2064	reverseEntities = {
2065		'&lt;' : '<',
2066		'&gt;' : '>',
2067		'&amp;' : '&',
2068		'&quot;' : '"',
2069		'&apos;' : "'"
2070	};
2071
2072	// Decodes text by using the browser
2073	function nativeDecode(text) {
2074		var elm;
2075
2076		elm = document.createElement("div");
2077		elm.innerHTML = text;
2078
2079		return elm.textContent || elm.innerText || text;
2080	};
2081
2082	// Build a two way lookup table for the entities
2083	function buildEntitiesLookup(items, radix) {
2084		var i, chr, entity, lookup = {};
2085
2086		if (items) {
2087			items = items.split(',');
2088			radix = radix || 10;
2089
2090			// Build entities lookup table
2091			for (i = 0; i < items.length; i += 2) {
2092				chr = String.fromCharCode(parseInt(items[i], radix));
2093
2094				// Only add non base entities
2095				if (!baseEntities[chr]) {
2096					entity = '&' + items[i + 1] + ';';
2097					lookup[chr] = entity;
2098					lookup[entity] = chr;
2099				}
2100			}
2101
2102			return lookup;
2103		}
2104	};
2105
2106	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
2107	namedEntities = buildEntitiesLookup(
2108		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
2109		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
2110		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
2111		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
2112		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
2113		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
2114		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
2115		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
2116		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
2117		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
2118		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
2119		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
2120		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
2121		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
2122		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
2123		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
2124		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
2125		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
2126		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
2127		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8
2127k4,' +
2128		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
2129		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
2130		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
2131		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
2132		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
2133
2134	tinymce.html = tinymce.html || {};
2135
2136	tinymce.html.Entities = {
2137		encodeRaw : function(text, attr) {
2138			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2139				return baseEntities[chr] || chr;
2140			});
2141		},
2142
2143		encodeAllRaw : function(text) {
2144			return ('' + text).replace(rawCharsRegExp, function(chr) {
2145				return baseEntities[chr] || chr;
2146			});
2147		},
2148
2149		encodeNumeric : function(text, attr) {
2150			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2151				// Multi byte sequence convert it to a single entity
2152				if (chr.length > 1)
2153					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
2154
2155				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
2156			});
2157		},
2158
2159		encodeNamed : function(text, attr, entities) {
2160			entities = entities || namedEntities;
2161
2162			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2163				return baseEntities[chr] || entities[chr] || chr;
2164			});
2165		},
2166
2167		getEncodeFunc : function(name, entities) {
2168			var Entities = tinymce.html.Entities;
2169
2170			entities = buildEntitiesLookup(entities) || namedEntities;
2171
2172			function encodeNamedAndNumeric(text, attr) {
2173				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
2174					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
2175				});
2176			};
2177
2178			function encodeCustomNamed(text, attr) {
2179				return Entities.encodeNamed(text, attr, entities);
2180			};
2181
2182			// Replace + with , to be compatible with previous TinyMCE versions
2183			name = tinymce.makeMap(name.replace(/\+/g, ','));
2184
2185			// Named and numeric encoder
2186			if (name.named && name.numeric)
2187				return encodeNamedAndNumeric;
2188
2189			// Named encoder
2190			if (name.named) {
2191				// Custom names
2192				if (entities)
2193					return encodeCustomNamed;
2194
2195				return Entities.encodeNamed;
2196			}
2197
2198			// Numeric
2199			if (name.numeric)
2200				return Entities.encodeNumeric;
2201
2202			// Raw encoder
2203			return Entities.encodeRaw;
2204		},
2205
2206		decode : function(text) {
2207			return text.replace(entityRegExp, function(all, numeric, value) {
2208				if (numeric) {
2209					value = parseInt(value, numeric.length === 2 ? 16 : 10);
2210
2211					// Support upper UTF
2212					if (value > 0xFFFF) {
2213						value -= 0x10000;
2214
2215						return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
2216					} else
2217						return asciiMap[value] || String.fromCharCode(value);
2218				}
2219
2220				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
2221			});
2222		}
2223	};
2224})(tinymce);
2225
2226tinymce.html.Styles = function(settings, schema) {
2227	var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
2228		urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
2229		styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
2230		trimRightRegExp = /\s+$/,
2231		urlColorRegExp = /rgb/,
2232		undef, i, encodingLookup = {}, encodingItems;
2233
2234	settings = settings || {};
2235
2236	encodingItems = '\\" \\\' \\; \\: ; : \uFEFF'.split(' ');
2237	for (i = 0; i < encodingItems.length; i++) {
2238		encodingLookup[encodingItems[i]] = '\uFEFF' + i;
2239		encodingLookup['\uFEFF' + i] = encodingItems[i];
2240	}
2241
2242	function toHex(match, r, g, b) {
2243		function hex(val) {
2244			val = parseInt(val).toString(16);
2245
2246			return val.length > 1 ? val : '0' + val; // 0 -> 00
2247		};
2248
2249		return '#' + hex(r) + hex(g) + hex(b);
2250	};
2251
2252	return {
2253		toHex : function(color) {
2254			return color.replace(rgbRegExp, toHex);
2255		},
2256
2257		parse : function(css) {
2258			var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;
2259
2260			function compress(prefix, suffix) {
2261				var top, right, bottom, left;
2262
2263				// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
2264				// So lets asume it shouldn't be there
2265				if (styles['border-image'] === 'none') {
2266					delete styles['border-image'];
2267				}
2268
2269				// Get values and check it it needs compressing
2270				top = styles[prefix + '-top' + suffix];
2271				if (!top)
2272					return;
2273
2274				right = styles[prefix + '-right' + suffix];
2275				if (top != right)
2276					return;
2277
2278				bottom = styles[prefix + '-bottom' + suffix];
2279				if (right != bottom)
2280					return;
2281
2282				left = styles[prefix + '-left' + suffix];
2283				if (bottom != left)
2284					return;
2285
2286				// Compress
2287				styles[prefix + suffix] = left;
2288				delete styles[prefix + '-top' + suffix];
2289				delete styles[prefix + '-right' + suffix];
2290				delete styles[prefix + '-bottom' + suffix];
2291				delete styles[prefix + '-left' + suffix];
2292			};
2293
2294			function canCompress(key) {
2295				var value = styles[key], i;
2296
2297				if (!value || value.indexOf(' ') < 0)
2298					return;
2299
2300				value = value.split(' ');
2301				i = value.length;
2302				while (i--) {
2303					if (value[i] !== value[0])
2304						return false;
2305				}
2306
2307				styles[key] = value[0];
2308
2309				return true;
2310			};
2311
2312			function compress2(target, a, b, c) {
2313				if (!canCompress(a))
2314					return;
2315
2316				if (!canCompress(b))
2317					return;
2318
2319				if (!canCompress(c))
2320					return;
2321
2322				// Compress
2323				styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
2324				delete styles[a];
2325				delete styles[b];
2326				delete styles[c];
2327			};
2328
2329			// Encodes the specified string by replacing all \" \' ; : with _<num>
2330			function encode(str) {
2331				isEncoded = true;
2332
2333				return encodingLookup[str];
2334			};
2335
2336			// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
2337			// It will also decode the \" \' if keep_slashes is set to fale or omitted
2338			function decode(str, keep_slashes) {
2339				if (isEncoded) {
2340					str = str.replace(/\uFEFF[0-9]/g, function(str) {
2341						return encodingLookup[str];
2342					});
2343				}
2344
2345				if (!keep_slashes)
2346					str = str.replace(/\\([\'\";:])/g, "$1");
2347
2348				return str;
2349			};
2350
2351			function processUrl(match, url, url2, url3, str, str2) {
2352				str = str || str2;
2353
2354				if (str) {
2355					str = decode(str);
2356
2357					// Force strings into single quote format
2358					return "'" + str.replace(/\'/g, "\\'") + "'";
2359				}
2360
2361				url = decode(url || url2 || url3);
2362
2363				// Convert the URL to relative/absolute depending on config
2364				if (urlConverter)
2365					url = urlConverter.call(urlConverterScope, url, 'style');
2366
2367				// Output new URL format
2368				return "url('" + url.replace(/\'/g, "\\'") + "')";
2369			};
2370
2371			if (css) {
2372				// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
2373				css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
2374					return str.replace(/[;:]/g, encode);
2375				});
2376
2377				// Parse styles
2378				while (matches = styleRegExp.exec(css)) {
2379					name = matches[1].replace(trimRightRegExp, '').toLowerCase();
2380					value = matches[2].replace(trimRightRegExp, '');
2381
2382					if (name && value.length > 0) {
2383						// Opera will produce 700 instead of bold in their style values
2384						if (name === 'font-weight' && value === '700')
2385							value = 'bold';
2386						else if (name === 'color' || name === 'background-color') // Lowercase colors like RED
2387							value = value.toLowerCase();		
2388
2389						// Convert RGB colors to HEX
2390						value = value.replace(rgbRegExp, toHex);
2391
2392						// Convert URLs and force them into url('value') format
2393						value = value.replace(urlOrStrRegExp, processUrl);
2394						styles[name] = isEncoded ? decode(value, true) : value;
2395					}
2396
2397					styleRegExp.lastIndex = matches.index + matches[0].length;
2398				}
2399
2400				// Compress the styles to reduce it's size for example IE will expand styles
2401				compress("border", "");
2402				compress("border", "-width");
2403				compress("border", "-color");
2404				compress("border", "-style");
2405				compress("padding", "");
2406				compress("margin", "");
2407				compress2('border', 'border-width', 'border-style', 'border-color');
2408
2409				// Remove pointless border, IE produces these
2410				if (styles.border === 'medium none')
2411					delete styles.border;
2412			}
2413
2414			return styles;
2415		},
2416
2417		serialize : function(styles, element_name) {
2418			var css = '', name, value;
2419
2420			function serializeStyles(name) {
2421				var styleList, i, l, value;
2422
2423				styleList = schema.styles[name];
2424				if (styleList) {
2425					for (i = 0, l = styleList.length; i < l; i++) {
2426						name = styleList[i];
2427						value = styles[name];
2428
2429						if (value !== undef && value.length > 0)
2430							css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
2431					}
2432				}
2433			};
2434
2435			// Serialize styles according to schema
2436			if (element_name && schema && schema.styles) {
2437				// Serialize global styles and element specific styles
2438				serializeStyles('*');
2439				serializeStyles(element_name);
2440			} else {
2441				// Output the styles in the order they are inside the object
2442				for (name in styles) {
2443					value = styles[name];
2444
2445					if (value !== undef && value.length > 0)
2446						css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
2447				}
2448			}
2449
2450			return css;
2451		}
2452	};
2453};
2454
2455(function(tinymce) {
2456	var mapCache = {}, makeMap = tinymce.makeMap, each = tinymce.each;
2457
2458	function split(str, delim) {
2459		return str.split(delim || ',');
2460	};
2461
2462	function unpack(lookup, data) {
2463		var key, elements = {};
2464
2465		function replace(value) {
2466			return value.replace(/[A-Z]+/g, function(key) {
2467				return replace(lookup[key]);
2468			});
2469		};
2470
2471		// Unpack lookup
2472		for (key in lookup) {
2473			if (lookup.hasOwnProperty(key))
2474				lookup[key] = replace(lookup[key]);
2475		}
2476
2477		// Unpack and parse data into object map
2478		replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {
2479			attributes = split(attributes, '|');
2480
2481			elements[name] = {
2482				attributes : makeMap(attributes),
2483				attributesOrder : attributes,
2484				children : makeMap(children, '|', {'#comment' : {}})
2485			}
2486		});
2487
2488		return elements;
2489	};
2490
2491	function getHTML5() {
2492		var html5 = mapCache.html5;
2493
2494		if (!html5) {
2495			html5 = mapCache.html5 = unpack({
2496					A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
2497					B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|' +
2498						'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr',
2499					C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|' +
2500						'figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|' +
2501						'p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
2502				}, 'html[A|manifest][body|head]' +
2503					'head[A][base|command|link|meta|noscript|script|style|title]' +
2504					'title[A][#]' +
2505					'base[A|href|target][]' +
2506					'link[A|href|rel|media|type|sizes][]' +
2507					'meta[A|http-equiv|name|content|charset][]' +
2508					'style[A|type|media|scoped][#]' +
2509					'script[A|charset|type|src|defer|async][#]' +
2510					'noscript[A][C]' +
2511					'body[A][C]' +
2512					'section[A][C]' +
2513					'nav[A][C]' +
2514					'article[A][C]' +
2515					'aside[A][C]' +
2516					'h1[A][B]' +
2517					'h2[A][B]' +
2518					'h3[A][B]' +
2519					'h4[A][B]' +
2520					'h5[A][B]' +
2521					'h6[A][B]' +
2522					'hgroup[A][h1|h2|h3|h4|h5|h6]' +
2523					'header[A][C]' +
2524					'footer[A][C]' +
2525					'address[A][C]' +
2526					'p[A][B]' +
2527					'br[A][]' +
2528					'pre[A][B]' +
2529					'dialog[A][dd|dt]' +
2530					'blockquote[A|cite][C]' +
2531					'ol[A|start|reversed][li]' +
2532					'ul[A][li]' +
2533					'li[A|value][C]' +
2534					'dl[A][dd|dt]' +
2535					'dt[A][B]' +
2536					'dd[A][C]' +
2537					'a[A|href|target|ping|rel|media|type][B]' +
2538					'em[A][B]' +
2539					'strong[A][B]' +
2540					'small[A][B]' +
2541					'cite[A][B]' +
2542					'q[A|cite][B]' +
2543					'dfn[A][B]' +
2544					'abbr[A][B]' +
2545					'code[A][B]' +
2546					'var[A][B]' +
2547					'samp[A][B]' +
2548					'kbd[A][B]' +
2549					'sub[A][B]' +
2550					'sup[A][B]' +
2551					'i[A][B]' +
2552					'b[A][B]' +
2553					'mark[A][B]' +
2554					'progress[A|value|max][B]' +
2555					'meter[A|value|min|max|low|high|optimum][B]' +
2556					'time[A|datetime][B]' +
2557					'ruby[A][B|rt|rp]' +
2558					'rt[A][B]' +
2559					'rp[A][B]' +
2560					'bdo[A][B]' +
2561					'span[A][B]' +
2562					'ins[A|cite|datetime][B]' +
2563					'del[A|cite|datetime][B]' +
2564					'figure[A][C|legend|figcaption]' +
2565					'figcaption[A][C]' +
2566					'img[A|alt|src|height|width|usemap|ismap][]' +
2567					'iframe[A|name|src|height|width|sandbox|seamless|allowfullscreen][]' +
2568					'embed[A|src|height|width|type][]' +
2569					'object[A|data|type|height|width|usemap|name|form|classid][param]' +
2570					'param[A|name|value][]' +
2571					'details[A|open][C|legend]' +
2572					'command[A|type|label|icon|disabled|checked|radiogroup][]' +
2573					'menu[A|type|label][C|li]' +
2574					'legend[A][C|B]' +
2575					'div[A][C]' +
2576					'source[A|src|type|media][]' +
2577					'audio[A|src|autobuffer|autoplay|loop|controls][source]' +
2578					'video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]' +
2579					'hr[A][]' +
2580					'form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]' +
2581					'fieldset[A|disabled|form|name][C|legend]' +
2582					'label[A|form|for][B]' +
2583					'input[A|type|accept|alt|autocomplete|autofocus|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|' +
2584						'multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]' +
2585					'button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]' +
2586					'select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]' +
2587					'datalist[A][B|option]' +
2588					'optgroup[A|disabled|label][option]' +
2589					'option[A|disabled|selected|label|value][]' +
2590					'textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]' +
2591					'keygen[A|autofocus|challenge|disabled|form|keytype|name][]' +
2592					'output[A|for|form|name][B]' +
2593					'canvas[A|width|height][]' +
2594					'map[A|name][B|C]' +
2595					'area[A|shape|coords|href|alt|target|media|rel|ping|type][]' +
2596					'mathml[A][]' +
2597					'svg[A][]' +
2598					'table[A|border][caption|colgroup|thead|tfoot|tbody|tr]' +
2599					'caption[A][C]' +
2600					'colgroup[A|span][col]' +
2601					'col[A|span][]' +
2602					'thead[A][tr]' +
2603					'tfoot[A][tr]' +
2604					'tbody[A][tr]' +
2605					'tr[A][th|td]' +
2606					'th[A|headers|rowspan|colspan|scope][B]' +
2607					'td[A|headers|rowspan|colspan][C]' +
2608					'wbr[A][]'
2609			);
2610		}
2611
2612		return html5;
2613	};
2614
2615	function getHTML4() {
2616		var html4 = mapCache.html4;
2617
2618		if (!html4) {
2619			// This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size
2620			html4 = mapCache.html4 = unpack({
2621				Z : 'H|K|N|O|P',
2622				Y : 'X|form|R|Q',
2623				ZG : 'E|span|width|align|char|charoff|valign',
2624				X : 'p|T|div|U|W|isindex|fieldset|table',
2625				ZF : 'E|align|char|charoff|valign',
2626				W : 'pre|hr|blockquote|address|center|noframes',
2627				ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',
2628				ZD : '[E][S]',
2629				U : 'ul|ol|dl|menu|dir',
2630				ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',
2631				T : 'h1|h2|h3|h4|h5|h6',
2632				ZB : 'X|S|Q',
2633				S : 'R|P',
2634				ZA : 'a|G|J|M|O|P',
2635				R : 'a|H|K|N|O',
2636				Q : 'noscript|P',
2637				P : 'ins|del|script',
2638				O : 'input|select|textarea|label|button',
2639				N : 'M|L',
2640				M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',
2641				L : 'sub|sup',
2642				K : 'J|I',
2643				J : 'tt|i|b|u|s|strike',
2644				I : 'big|small|font|basefont',
2645				H : 'G|F',
2646				G : 'br|span|bdo',
2647				F : 'object|applet|img|map|iframe',
2648				E : 'A|B|C',
2649				D : 'accesskey|tabindex|onfocus|onblur',
2650				C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
2651				B : 'lang|xml:lang|dir',
2652				A : 'id|class|style|title'
2653			}, 'script[id|charset|type|language|src|defer|xml:space][]' + 
2654				'style[B|id|type|media|title|xml:space][]' + 
2655				'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + 
2656				'param[id|name|value|valuetype|type][]' + 
2657				'p[E|align][#|S]' + 
2658				'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + 
2659				'br[A|clear][]' + 
2660				'span[E][#|S]' + 
2661				'bdo[A|C|B][#|S]' + 
2662				'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + 
2663				'h1[E|align][#|S]' + 
2664				'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + 
2665				'map[B|C|A|name][X|form|Q|area]' + 
2666				'h2[E|align][#|S]' + 
2667				'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height|allowfullscreen][#|Y]' + 
2668				'h3[E|align][#|S]' + 
2669				'tt[E][#|S]' + 
2670				'i[E][#|S]' + 
2671				'b[E][#|S]' + 
2672				'u[E][#|S]' + 
2673				's[E][#|S]' + 
2674				'strike[E][#|S]' + 
2675				'big[E][#|S]' + 
2676				'small[E][#|S]' + 
2677				'font[A|B|size|color|face][#|S]' + 
2678				'basefont[id|size|color|face][]' + 
2679				'em[E][#|S]' + 
2680				'strong[E][#|S]' + 
2681				'dfn[E][#|S]' + 
2682				'code[E][#|S]' + 
2683				'q[E|cite][#|S]' + 
2684				'samp[E][#|S]' + 
2685				'kbd[E][#|S]' + 
2686				'var[E][#|S]' + 
2687				'cite[E][#|S]' + 
2688				'abbr[E][#|S]' + 
2689				'acronym[E][#|S]' + 
2690				'sub[E][#|S]' + 
2691				'sup[E][#|S]' + 
2692				'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + 
2693				'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + 
2694				'optgroup[E|disabled|label][option]' + 
2695				'option[E|selected|disabled|label|value][]' + 
2696				'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + 
2697				'label[E|for|accesskey|onfocus|onblur][#|S]' + 
2698				'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + 
2699				'h4[E|align][#|S]' + 
2700				'ins[E|cite|datetime][#|Y]' + 
2701				'h5[E|align][#|S]' + 
2702				'del[E|cite|datetime][#|Y]' + 
2703				'h6[E|align][#|S]' + 
2704				'div[E|align][#|Y]' + 
2705				'ul[E|type|compact][li]' + 
2706				'li[E|type|value][#|Y]' + 
2707				'ol[E|type|compact|start][li]' + 
2708				'dl[E|compact][dt|dd]' + 
2709				'dt[E][#|S]' + 
2710				'dd[E][#|Y]' + 
2711				'menu[E|compact][li]' + 
2712				'dir[E|compact][li]' + 
2713				'pre[E|width|xml:space][#|ZA]' + 
2714				'hr[E|align|noshade|size|width][]' + 
2715				'blockquote[E|cite][#|Y]' + 
2716				'address[E][#|S|p]' + 
2717				'center[E][#|Y]' + 
2718				'noframes[E][#|Y]' + 
2719				'isindex[A|B|prompt][]' + 
2720				'fieldset[E][#|legend|Y]' + 
2721				'legend[E|accesskey|align][#|S]' + 
2722				'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + 
2723				'caption[E|align][#|S]' + 
2724				'col[ZG][]' + 
2725				'colgroup[ZG][col]' + 
2726				'thead[ZF][tr]' + 
2727				'tr[ZF|bgcolor][th|td]' + 
2728				'th[E|ZE][#|Y]' + 
2729				'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + 
2730				'noscript[E][#|Y]' + 
2731				'td[E|ZE][#|Y]' + 
2732				'tfoot[ZF][tr]' + 
2733				'tbody[ZF][tr]' + 
2734				'area[E|D|shape|coords|href|nohref|alt|target][]' + 
2735				'base[id|href|target][]' + 
2736				'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'
2737			);
2738		}
2739
2740		return html4;
2741	};
2742
2743	tinymce.html.Schema = function(settings) {
2744		var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
2745		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, blockElementsMap, nonEmptyElementsMap, customElementsMap = {};
2746
2747		// Creates an lookup table map object for the specified option or the default value
2748		function createLookupTable(option, default_value, extend) {
2749			var value = settings[option];
2750
2751			if (!value) {
2752				// Get cached default map or make it if needed
2753				value = mapCache[option];
2754
2755				if (!value) {
2756					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
2757					value = tinymce.extend(value, extend);
2758
2759					mapCache[option] = value;
2760				}
2761			} else {
2762				// Create custom map
2763				value = makeMap(value, ',', makeMap(value.toUpperCase(), ' '));
2764			}
2765
2766			return value;
2767		};
2768
2769		settings = settings || {};
2770		schemaItems = settings.schema == "html5" ? getHTML5() : getHTML4();
2771
2772        // 11:41 17.03.2025 szybki fix, zeby TinyMCE nie wycinal znacznikow <time> (no i innych tez)
2773		// Allow all elements and attributes if verify_html is set to false
2774		//if (settings.verify_html === false)
2775		{
2776			settings.valid_elements = '*[*]';
2777            
2778            settings.verify_html = false;
2779            settings.cleanup = false;
2780            settings.invalid_elements = "";
2781        }
2782
2783		// Build styles list
2784		if (settings.valid_styles) {
2785			validStyles = {};
2786
2787			// Convert styles into a rule list
2788			each(settings.valid_styles, function(value, key) {
2789				validStyles[key] = tinymce.explode(value);
2790			});
2791		}
2792
2793		// Setup map objects
2794		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea');
2795		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
2796		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source wbr');
2797		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls');
2798		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script div', shortEndedElementsMap);
2799		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' + 
2800						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
2801		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' + 
2802						'th tr td li ol ul caption dl dt dd noscript menu isindex samp option datalist select optgroup', textBlockElementsMap);
2803
2804		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
2805		function patternToRegExp(str) {
2806			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
2807		};
2808
2809		// Parses the specified valid_elements string and adds to the current rules
2810		// This function is a bit hard to read since it's heavily optimized for speed
2811		function addValidElements(valid_elements) {
2812			var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
2813				prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,
2814				elementRuleRegExp = /^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
2815				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
2816				hasPatternsRegExp = /[*?+]/;
2817
2818			if (valid_elements) {
2819				// Split valid elements into an array with rules
2820				valid_elements = split(valid_elements);
2821
2822				if (elements['@']) {
2823					globalAttributes = elements['@'].attributes;
2824					globalAttributesOrder = elements['@'].attributesOrder;
2825				}
2826
2827				// Loop all rules
2828				for (ei = 0, el = valid_elements.length; ei < el; ei++) {
2829					// Parse element rule
2830					matches = elementRuleRegExp.exec(valid_elements[ei]);
2831					if (matches) {
2832						// Setup local names for matches
2833						prefix = matches[1];
2834						elementName = matches[2];
2835						outputName = matches[3];
2836						attrData = matches[4];
2837
2838						// Create new attributes and attributesOrder
2839						attributes = {};
2840						attributesOrder = [];
2841
2842						// Create the new element
2843						element = {
2844							attributes : attributes,
2845							attributesOrder : attributesOrder
2846						};
2847
2848						// Padd empty elements prefix
2849						if (prefix === '#')
2850							element.paddEmpty = true;
2851
2852						// Remove empty elements prefix
2853						if (prefix === '-')
2854							element.removeEmpty = true;
2855
2856						// Copy attributes from global rule into current rule
2857						if (globalAttributes) {
2858							for (key in globalAttributes)
2859								attributes[key] = globalAttributes[key];
2860
2861							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
2862						}
2863
2864						// Attributes defined
2865						if (attrData) {
2866							attrData = split(attrData, '|');
2867							for (ai = 0, al = attrData.length; ai < al; ai++) {
2868								matches = attrRuleRegExp.exec(attrData[ai]);
2869								if (matches) {
2870									attr = {};
2871									attrType = matches[1];
2872									attrName = matches[2].replace(/::/g, ':');
2873									prefix = matches[3];
2874									value = matches[4];
2875
2876									// Required
2877									if (attrType === '!') {
2878										element.attributesRequired = element.attributesRequired || [];
2879										element.attributesRequired.push(attrName);
2880										attr.required = true;
2881									}
2882
2883									// Denied from global
2884									if (attrType === '-') {
2885										delete attributes[attrName];
2886										attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);
2887										continue;
2888									}
2889
2890									// Default value
2891									if (prefix) {
2892										// Default value
2893										if (prefix === '=') {
2894											element.attributesDefault = element.attributesDefault || [];
2895											element.attributesDefault.push({name: attrName, value: value});
2896											attr.defaultValue = value;
2897										}
2898
2899										// Forced value
2900										if (prefix === ':') {
2901											element.attributesForced = element.attributesForced || [];
2902											element.attributesForced.push({name: attrName, value: value});
2903											attr.forcedValue = value;
2904										}
2905
2906										// Required values
2907										if (prefix === '<')
2908											attr.validValues = makeMap(value, '?');
2909									}
2910
2911									// Check for attribute patterns
2912									if (hasPatternsRegExp.test(attrName)) {
2913										element.attributePatterns = element.attributePatterns || [];
2914										attr.pattern = patternToRegExp(attrName);
2915										element.attributePatterns.push(attr);
2916									} else {
2917										// Add attribute to order list if it doesn't already exist
2918										if (!attributes[attrName])
2919											attributesOrder.push(attrName);
2920
2921										attributes[attrName] = attr;
2922									}
2923								}
2924							}
2925						}
2926
2927						// Global rule, store away these for later usage
2928						if (!globalAttributes && elementName == '@') {
2929							globalAttributes = attributes;
2930							globalAttributesOrder = attributesOrder;
2931						}
2932
2933						// Handle substitute elements such as b/strong
2934						if (outputName) {
2935							element.outputName = elementName;
2936							elements[outputName] = element;
2937						}
2938
2939						// Add pattern or exact element
2940						if (hasPatternsRegExp.test(elementName)) {
2941							element.pattern = patternToRegExp(elementName);
2942							patternElements.push(element);
2943						} else
2944							elements[elementName] = element;
2945					}
2946				}
2947			}
2948		};
2949
2950		function setValidElements(valid_elements) {
2951			elements = {};
2952			patternElements = [];
2953
2954			addValidElements(valid_elements);
2955
2956			each(schemaItems, function(element, name) {
2957				children[name] = element.children;
2958			});
2959		};
2960
2961		// Adds custom non HTML elements to the schema
2962		function addCustomElements(custom_elements) {
2963			var customElementRegExp = /^(~)?(.+)$/;
2964
2965			if (custom_elements) {
2966				each(split(custom_elements), function(rule) {
2967					var matches = customElementRegExp.exec(rule),
2968						inline = matches[1] === '~',
2969						cloneName = inline ? 'span' : 'div',
2970						name = matches[2];
2971
2972					children[name] = children[cloneName];
2973					customElementsMap[name] = cloneName;
2974
2975					// If it's not marked as inline then add it to valid block elements
2976					if (!inline) {
2977						blockElementsMap[name.toUpperCase()] = {};
2978						blockElementsMap[name] = {};
2979					}
2980
2981					// Add elements clone if needed
2982					if (!elements[name]) {
2983						elements[name] = elements[cloneName];
2984					}
2985
2986					// Add custom elements at span/div positions
2987					each(children, function(element, child) {
2988						if (element[cloneName])
2989							element[name] = element[cloneName];
2990					});
2991				});
2992			}
2993		};
2994
2995		// Adds valid children to the schema object
2996		function addValidChildren(valid_children) {
2997			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
2998
2999			if (valid_children) {
3000				each(split(valid_children), function(rule) {
3001					var matches = childRuleRegExp.exec(rule), parent, prefix;
3002
3003					if (matches) {
3004						prefix = matches[1];
3005
3006						// Add/remove items from default
3007						if (prefix)
3008							parent = children[matches[2]];
3009						else
3010							parent = children[matches[2]] = {'#comment' : {}};
3011
3012						parent = children[matches[2]];
3013
3014						each(split(matches[3], '|'), function(child) {
3015							if (prefix === '-')
3016								delete parent[child];
3017							else
3018								parent[child] = {};
3019						});
3020					}
3021				});
3022			}
3023		};
3024
3025		function getElementRule(name) {
3026			var element = elements[name], i;
3027
3028			// Exact match found
3029			if (element)
3030				return element;
3031
3032			// No exact match then try the patterns
3033			i = patternElements.length;
3034			while (i--) {
3035				element = patternElements[i];
3036
3037				if (element.pattern.test(name))
3038					return element;
3039			}
3040		};
3041
3042		if (!settings.valid_elements) {
3043			// No valid elements defined then clone the elements from the schema spec
3044			each(schemaItems, function(element, name) {
3045				elements[name] = {
3046					attributes : element.attributes,
3047					attributesOrder : element.attributesOrder
3048				};
3049
3050				children[name] = element.children;
3051			});
3052
3053			// Switch these on HTML4
3054			if (settings.schema != "html5") {
3055				each(split('strong/b,em/i'), function(item) {
3056					item = split(item, '/');
3057					elements[item[1]].outputName = item[0];
3058				});
3059			}
3060
3061			// Add default alt attribute for images
3062			elements.img.attributesDefault = [{name: 'alt', value: ''}];
3063
3064			// Remove these if they are empty by default
3065			each(split('ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr,strong,em,b,i,div'), function(name) {
3066				if (elements[name]) {
3067					elements[name].removeEmpty = true;
3068				}
3069			});
3070
3071			// Padd these by default
3072			each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,address,caption'), function(name) {
3073				elements[name].paddEmpty = true;
3074			});
3075		} else
3076			setValidElements(settings.valid_elements);
3077
3078		addCustomElements(settings.custom_elements);
3079		addValidChildren(settings.valid_children);
3080		addValidElements(settings.extended_valid_elements);
3081
3082		// Todo: Remove this when we fix list handling to be valid
3083		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
3084
3085		// Delete invalid elements
3086		if (settings.invalid_elements) {
3087			tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {
3088				if (elements[item])
3089					delete elements[item];
3090			});
3091		}
3092
3093		// If the user didn't allow span only allow internal spans
3094		if (!getElementRule('span'))
3095			addValidElements('span[!data-mce-type|*]');
3096
3097		self.children = children;
3098
3099		self.styles = validStyles;
3100
3101		self.getBoolAttrs = function() {
3102			return boolAttrMap;
3103		};
3104
3105		self.getBlockElements = function() {
3106			return blockElementsMap;
3107		};
3108
3109		self.getTextBlockElements = function() {
3110			return textBlockElementsMap;
3111		};
3112
3113		self.getShortEndedElements = function() {
3114			return shortEndedElementsMap;
3115		};
3116
3117		self.getSelfClosingElements = function() {
3118			return selfClosingElementsMap;
3119		};
3120
3121		self.getNonEmptyElements = function() {
3122			return nonEmptyElementsMap;
3123		};
3124
3125		self.getWhiteSpaceElements = function() {
3126			return whiteSpaceElementsMap;
3127		};
3128
3129		self.isValidChild = function(name, child) {
3130			var parent = children[name];
3131
3132			return !!(parent && parent[child]);
3133		};
3134
3135		self.isValid = function(name, attr) {
3136			var attrPatterns, i, rule = getElementRule(name);
3137
3138			// Check if it's a valid element
3139			if (rule) {
3140				if (attr) {
3141					// Check if attribute name exists
3142					if (rule.attributes[attr]) {
3143						return true;
3144					}
3145
3146					// Check if attribute matches a regexp pattern
3147					attrPatterns = rule.attributePatterns;
3148					if (attrPatterns) {
3149						i = attrPatterns.length;
3150						while (i--) {
3151							if (attrPatterns[i].pattern.test(name)) {
3152								return true;
3153							}
3154						}
3155					}
3156				} else {
3157					return true;
3158				}
3159			}
3160
3161			// No match
3162			return false;
3163		};
3164		
3165		self.getElementRule = getElementRule;
3166
3167		self.getCustomElements = function() {
3168			return customElementsMap;
3169		};
3170
3171		self.addValidElements = addValidElements;
3172
3173		self.setValidElements = setValidElements;
3174
3175		self.addCustomElements = addCustomElements;
3176
3177		self.addValidChildren = addValidChildren;
3178
3179		self.elements = elements;
3180	};
3181})(tinymce);
3182
3183(function(tinymce) {
3184	tinymce.html.SaxParser = function(settings, schema) {
3185		var self = this, noop = function() {};
3186
3187		settings = settings || {};
3188		self.schema = schema = schema || new tinymce.html.Schema();
3189
3190		if (settings.fix_self_closing !== false)
3191			settings.fix_self_closing = true;
3192
3193		// Add handler functions from settings and setup default handlers
3194		tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {
3195			if (name)
3196				self[name] = settings[name] || noop;
3197		});
3198
3199		self.parse = function(html) {
3200			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name, isInternalElement, removeInternalElements,
3201				shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue, invalidPrefixRegExp,
3202				validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,
3203				tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing, isIE;
3204
3205			function processEndTag(name) {
3206				var pos, i;
3207
3208				// Find position of parent of the same type
3209				pos = stack.length;
3210				while (pos--) {
3211					if (stack[pos].name === name)
3212						break;						
3213				}
3214
3215				// Found parent
3216				if (pos >= 0) {
3217					// Close all the open elements
3218					for (i = stack.length - 1; i >= pos; i--) {
3219						name = stack[i];
3220
3221						if (name.valid)
3222							self.end(name.name);
3223					}
3224
3225					// Remove the open elements from the stack
3226					stack.length = pos;
3227				}
3228			};
3229
3230			function parseAttribute(match, name, value, val2, val3) {
3231				var attrRule, i;
3232
3233				name = name.toLowerCase();
3234				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
3235
3236				// Validate name and value
3237				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
3238					attrRule = validAttributesMap[name];
3239
3240					// Find rule by pattern matching
3241					if (!attrRule && validAttributePatterns) {
3242						i = validAttributePatterns.length;
3243						while (i--) {
3244							attrRule = validAttributePatterns[i];
3245							if (attrRule.pattern.test(name))
3246								break;
3247						}
3248
3249						// No rule matched
3250						if (i === -1)
3251							attrRule = null;
3252					}
3253
3254					// No attribute rule found
3255					if (!attrRule)
3256						return;
3257
3258					// Validate value
3259					if (attrRule.validValues && !(value in attrRule.validValues))
3260						return;
3261				}
3262
3263				// Add attribute to list and map
3264				attrList.map[name] = value;
3265				attrList.push({
3266					name: name,
3267					value: value
3268				});
3269			};
3270
3271			// Precompile RegExps and map objects
3272			tokenRegExp = new RegExp('<(?:' +
3273				'(?:!--([\\w\\W]*?)-->)|' + // Comment
3274				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
3275				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
3276				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
3277				'(?:\\/([^>]+)>)|' + // End element
3278				'(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
3279			')', 'g');
3280
3281			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
3282			specialElements = {
3283				'script' : /<\/script[^>]*>/gi,
3284				'style' : /<\/style[^>]*>/gi,
3285				'noscript' : /<\/noscript[^>]*>/gi
3286			};
3287
3288			// Setup lookup tables for empty elements and boolean attributes
3289			shortEndedElements = schema.getShortEndedElements();
3290			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
3291			fillAttrsMap = schema.getBoolAttrs();
3292			validate = settings.validate;
3293			removeInternalElements = settings.remove_internals;
3294			fixSelfClosing = settings.fix_self_closing;
3295			isIE = tinymce.isIE;
3296			invalidPrefixRegExp = /^:/;
3297
3298			while (matches = tokenRegExp.exec(html)) {
3299				// Text
3300				if (index < matches.index)
3301					self.text(decode(html.substr(index, matches.index - index)));
3302
3303				if (value = matches[6]) { // End element
3304					value = value.toLowerCase();
3305
3306					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
3307					if (isIE && invalidPrefixRegExp.test(value))
3308						value = value.substr(1);
3309
3310					processEndTag(value);
3311				} else if (value = matches[7]) { // Start element
3312					value = value.toLowerCase();
3313
3314					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
3315					if (isIE && invalidPrefixRegExp.test(value))
3316						value = value.substr(1);
3317
3318					isShortEnded = value in shortEndedElements;
3319
3320					// Is self closing tag for example an <li> after an open <li>
3321					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)
3322						processEndTag(value);
3323
3324					// Validate element
3325					if (!validate || (elementRule = schema.getElementRule(value))) {
3326						isValidElement = true;
3327
3328						// Grab attributes map and patters when validation is enabled
3329						if (validate) {
3330							validAttributesMap = elementRule.attributes;
3331							validAttributePatterns = elementRule.attributePatterns;
3332						}
3333
3334						// Parse attributes
3335						if (attribsValue = matches[8]) {
3336							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
3337
3338							// If the element has internal attributes then remove it if we are told to do so
3339							if (isInternalElement && removeInternalElements)
3340								isValidElement = false;
3341
3342							attrList = [];
3343							attrList.map = {};
3344
3345							attribsValue.replace(attrRegExp, parseAttribute);
3346						} else {
3347							attrList = [];
3348							attrList.map = {};
3349						}
3350
3351						// Process attributes if validation is enabled
3352						if (validate && !isInternalElement) {
3353							attributesRequired = elementRule.attributesRequired;
3354							attributesDefault = elementRule.attributesDefault;
3355							attributesForced = elementRule.attributesForced;
3356
3357							// Handle forced attributes
3358							if (attributesForced) {
3359								i = attributesForced.length;
3360								while (i--) {
3361									attr = attributesForced[i];
3362									name = attr.name;
3363									attrValue = attr.value;
3364
3365									if (attrValue === '{$uid}')
3366										attrValue = 'mce_' + idCount++;
3367
3368									attrList.map[name] = attrValue;
3369									attrList.push({name: name, value: attrValue});
3370								}
3371							}
3372
3373							// Handle default attributes
3374							if (attributesDefault) {
3375								i = attributesDefault.length;
3376								while (i--) {
3377									attr = attributesDefault[i];
3378									name = attr.name;
3379
3380									if (!(name in attrList.map)) {
3381										attrValue = attr.value;
3382
3383										if (attrValue === '{$uid}')
3384											attrValue = 'mce_' + idCount++;
3385
3386										attrList.map[name] = attrValue;
3387										attrList.push({name: name, value: attrValue});
3388									}
3389								}
3390							}
3391
3392							// Handle required attributes
3393							if (attributesRequired) {
3394								i = attributesRequired.length;
3395								while (i--) {
3396									if (attributesRequired[i] in attrList.map)
3397										break;
3398								}
3399
3400								// None of the required attributes where found
3401								if (i === -1)
3402									isValidElement = false;
3403							}
3404
3405							// Invalidate element if it's marked as bogus
3406							if (attrList.map['data-mce-bogus'])
3407								isValidElement = false;
3408						}
3409
3410						if (isValidElement)
3411							self.start(value, attrList, isShortEnded);
3412					} else
3413						isValidElement = false;
3414
3415					// Treat script, noscript and style a bit different since they may include code that looks like elements
3416					if (endRegExp = specialElements[value]) {
3417						endRegExp.lastIndex = index = matches.index + matches[0].length;
3418
3419						if (matches = endRegExp.exec(html)) {
3420							if (isValidElement)
3421								text = html.substr(index, matches.index - index);
3422
3423							index = matches.index + matches[0].length;
3424						} else {
3425							text = html.substr(index);
3426							index = html.length;
3427						}
3428
3429						if (isValidElement && text.length > 0)
3430							self.text(text, true);
3431
3432						if (isValidElement)
3433							self.end(value);
3434
3435						tokenRegExp.lastIndex = index;
3436						continue;
3437					}
3438
3439					// Push value on to stack
3440					if (!isShortEnded) {
3441						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)
3442							stack.push({name: value, valid: isValidElement});
3443						else if (isValidElement)
3444							self.end(value);
3445					}
3446				} else if (value = matches[1]) { // Comment
3447					self.comment(value);
3448				} else if (value = matches[2]) { // CDATA
3449					self.cdata(value);
3450				} else if (value = matches[3]) { // DOCTYPE
3451					self.doctype(value);
3452				} else if (value = matches[4]) { // PI
3453					self.pi(value, matches[5]);
3454				}
3455
3456				index = matches.index + matches[0].length;
3457			}
3458
3459			// Text
3460			if (index < html.length)
3461				self.text(decode(html.substr(index)));
3462
3463			// Close any open elements
3464			for (i = stack.length - 1; i >= 0; i--) {
3465				value = stack[i];
3466
3467				if (value.valid)
3468					self.end(value.name);
3469			}
3470		};
3471	}
3472})(tinymce);
3473
3474(function(tinymce) {
3475	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
3476		'#text' : 3,
3477		'#comment' : 8,
3478		'#cdata' : 4,
3479		'#pi' : 7,
3480		'#doctype' : 10,
3481		'#document-fragment' : 11
3482	};
3483
3484	// Walks the tree left/right
3485	function walk(node, root_node, prev) {
3486		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
3487
3488		// Walk into nodes if it has a start
3489		if (node[startName])
3490			return node[startName];
3491
3492		// Return the sibling if it has one
3493		if (node !== root_node) {
3494			sibling = node[siblingName];
3495
3496			if (sibling)
3497				return sibling;
3498
3499			// Walk up the parents to look for siblings
3500			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
3501				sibling = parent[siblingName];
3502
3503				if (sibling)
3504					return sibling;
3505			}
3506		}
3507	};
3508
3509	function Node(name, type) {
3510		this.name = name;
3511		this.type = type;
3512
3513		if (type === 1) {
3514			this.attributes = [];
3515			this.attributes.map = {};
3516		}
3517	}
3518
3519	tinymce.extend(Node.prototype, {
3520		replace : function(node) {
3521			var self = this;
3522
3523			if (node.parent)
3524				node.remove();
3525
3526			self.insert(node, self);
3527			self.remove();
3528
3529			return self;
3530		},
3531
3532		attr : function(name, value) {
3533			var self = this, attrs, i, undef;
3534
3535			if (typeof name !== "string") {
3536				for (i in name)
3537					self.attr(i, name[i]);
3538
3539				return self;
3540			}
3541
3542			if (attrs = self.attributes) {
3543				if (value !== undef) {
3544					// Remove attribute
3545					if (value === null) {
3546						if (name in attrs.map) {
3547							delete attrs.map[name];
3548
3549							i = attrs.length;
3550							while (i--) {
3551								if (attrs[i].name === name) {
3552									attrs = attrs.splice(i, 1);
3553									return self;
3554								}
3555							}
3556						}
3557
3558						return self;
3559					}
3560
3561					// Set attribute
3562					if (name in attrs.map) {
3563						// Set attribute
3564						i = attrs.length;
3565						while (i--) {
3566							if (attrs[i].name === name) {
3567								attrs[i].value = value;
3568								break;
3569							}
3570						}
3571					} else
3572						attrs.push({name: name, value: value});
3573
3574					attrs.map[name] = value;
3575
3576					return self;
3577				} else {
3578					return attrs.map[name];
3579				}
3580			}
3581		},
3582
3583		clone : function() {
3584			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
3585
3586			// Clone element attributes
3587			if (selfAttrs = self.attributes) {
3588				cloneAttrs = [];
3589				cloneAttrs.map = {};
3590
3591				for (i = 0, l = selfAttrs.length; i < l; i++) {
3592					selfAttr = selfAttrs[i];
3593
3594					// Clone everything except id
3595					if (selfAttr.name !== 'id') {
3596						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
3597						cloneAttrs.map[selfAttr.name] = selfAttr.value;
3598					}
3599				}
3600
3601				clone.attributes = cloneAttrs;
3602			}
3603
3604			clone.value = self.value;
3605			clone.shortEnded = self.shortEnded;
3606
3607			return clone;
3608		},
3609
3610		wrap : function(wrapper) {
3611			var self = this;
3612
3613			self.parent.insert(wrapper, self);
3614			wrapper.append(self);
3615
3616			return self;
3617		},
3618
3619		unwrap : function() {
3620			var self = this, node, next;
3621
3622			for (node = self.firstChild; node; ) {
3623				next = node.next;
3624				self.insert(node, self, true);
3625				node = next;
3626			}
3627
3628			self.remove();
3629		},
3630
3631		remove : function() {
3632			var self = this, parent = self.parent, next = self.next, prev = self.prev;
3633
3634			if (parent) {
3635				if (parent.firstChild === self) {
3636					parent.firstChild = next;
3637
3638					if (next)
3639						next.prev = null;
3640				} else {
3641					prev.next = next;
3642				}
3643
3644				if (parent.lastChild === self) {
3645					parent.lastChild = prev;
3646
3647					if (prev)
3648						prev.next = null;
3649				} else {
3650					next.prev = prev;
3651				}
3652
3653				self.parent = self.next = self.prev = null;
3654			}
3655
3656			return self;
3657		},
3658
3659		append : function(node) {
3660			var self = this, last;
3661
3662			if (node.parent)
3663				node.remove();
3664
3665			last = self.lastChild;
3666			if (last) {
3667				last.next = node;
3668				node.prev = last;
3669				self.lastChild = node;
3670			} else
3671				self.lastChild = self.firstChild = node;
3672
3673			node.parent = self;
3674
3675			return node;
3676		},
3677
3678		insert : function(node, ref_node, before) {
3679			var parent;
3680
3681			if (node.parent)
3682				node.remove();
3683
3684			parent = ref_node.parent || this;
3685
3686			if (before) {
3687				if (ref_node === parent.firstChild)
3688					parent.firstChild = node;
3689				else
3690					ref_node.prev.next = node;
3691
3692				node.prev = ref_node.prev;
3693				node.next = ref_node;
3694				ref_node.prev = node;
3695			} else {
3696				if (ref_node === parent.lastChild)
3697					parent.lastChild = node;
3698				else
3699					ref_node.next.prev = node;
3700
3701				node.next = ref_node.next;
3702				node.prev = ref_node;
3703				ref_node.next = node;
3704			}
3705
3706			node.parent = parent;
3707
3708			return node;
3709		},
3710
3711		getAll : function(name) {
3712			var self = this, node, collection = [];
3713
3714			for (node = self.firstChild; node; node = walk(node, self)) {
3715				if (node.name === name)
3716					collection.push(node);
3717			}
3718
3719			return collection;
3720		},
3721
3722		empty : function() {
3723			var self = this, nodes, i, node;
3724
3725			// Remove all children
3726			if (self.firstChild) {
3727				nodes = [];
3728
3729				// Collect the children
3730				for (node = self.firstChild; node; node = walk(node, self))
3731					nodes.push(node);
3732
3733				// Remove the children
3734				i = nodes.length;
3735				while (i--) {
3736					node = nodes[i];
3737					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
3738				}
3739			}
3740
3741			self.firstChild = self.lastChild = null;
3742
3743			return self;
3744		},
3745
3746		isEmpty : function(elements) {
3747			var self = this, node = self.firstChild, i, name;
3748
3749			if (node) {
3750				do {
3751					if (node.type === 1) {
3752						// Ignore bogus elements
3753						if (node.attributes.map['data-mce-bogus'])
3754							continue;
3755
3756						// Keep empty elements like <img />
3757						if (elements[node.name])
3758							return false;
3759
3760						// Keep elements with data attributes or name attribute like <a name="1"></a>
3761						i = node.attributes.length;
3762						while (i--) {
3763							name = node.attributes[i].name;
3764							if (name === "name" || name.indexOf('data-mce-') === 0)
3765								return false;
3766						}
3767					}
3768
3769					// Keep comments
3770					if (node.type === 8)
3771						return false;
3772					
3773					// Keep non whitespace text nodes
3774					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))
3775						return false;
3776				} while (node = walk(node, self));
3777			}
3778
3779			return true;
3780		},
3781
3782		walk : function(prev) {
3783			return walk(this, null, prev);
3784		}
3785	});
3786
3787	tinymce.extend(Node, {
3788		create : function(name, attrs) {
3789			var node, attrName;
3790
3791			// Create node
3792			node = new Node(name, typeLookup[name] || 1);
3793
3794			// Add attributes if needed
3795			if (attrs) {
3796				for (attrName in attrs)
3797					node.attr(attrName, attrs[attrName]);
3798			}
3799
3800			return node;
3801		}
3802	});
3803
3804	tinymce.html.Node = Node;
3805})(tinymce);
3806
3807(function(tinymce) {
3808	var Node = tinymce.html.Node;
3809
3810	tinymce.html.DomParser = function(settings, schema) {
3811		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
3812
3813		settings = settings || {};
3814		settings.validate = "validate" in settings ? settings.validate : true;
3815		settings.root_name = settings.root_name || 'body';
3816		self.schema = schema = schema || new tinymce.html.Schema();
3817
3818		function fixInvalidChildren(nodes) {
3819			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i,
3820				childClone, nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
3821
3822			nonSplitableElements = tinymce.makeMap('tr,td,th,tbody,thead,tfoot,table');
3823			nonEmptyElements = schema.getNonEmptyElements();
3824			textBlockElements = schema.getTextBlockElements();
3825
3826			for (ni = 0; ni < nodes.length; ni++) {
3827				node = nodes[ni];
3828
3829				// Already removed or fixed
3830				if (!node.parent || node.fixed)
3831					continue;
3832
3833				// If the invalid element is a text block and the text block is within a parent LI element
3834				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
3835				if (textBlockElements[node.name] && node.parent.name == 'li') {
3836					// Move sibling text blocks after LI element
3837					sibling = node.next;
3838					while (sibling) {
3839						if (textBlockElements[sibling.name]) {
3840							sibling.name = 'li';
3841							sibling.fixed = true;
3842							node.parent.insert(sibling, node.parent);
3843						} else {
3844							break;
3845						}
3846
3847						sibling = sibling.next;
3848					}
3849
3850					// Unwrap current text block
3851					node.unwrap(node);
3852					continue;
3853				}
3854
3855				// Get list of all parent nodes until we find a valid parent to stick the child into
3856				parents = [node];
3857				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplitableElements[parent.name]; parent = parent.parent)
3858					parents.push(parent);
3859
3860				// Found a suitable parent
3861				if (parent && parents.length > 1) {
3862					// Reverse the array since it makes looping easier
3863					parents.reverse();
3864
3865					// Clone the related parent and insert that after the moved node
3866					newParent = currentNode = self.filterNode(parents[0].clone());
3867
3868					// Start cloning and moving children on the left side of the target node
3869					for (i = 0; i < parents.length - 1; i++) {
3870						if (schema.isValidChild(currentNode.name, parents[i].name)) {
3871							tempNode = self.filterNode(parents[i].clone());
3872							currentNode.append(tempNode);
3873						} else
3874							tempNode = currentNode;
3875
3876						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {
3877							nextNode = childNode.next;
3878							tempNode.append(childNode);
3879							childNode = nextNode;
3880						}
3881
3882						currentNode = tempNode;
3883					}
3884
3885					if (!newParent.isEmpty(nonEmptyElements)) {
3886						parent.insert(newParent, parents[0], true);
3887						parent.insert(node, newParent);
3888					} else {
3889						parent.insert(node, parents[0], true);
3890					}
3891
3892					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
3893					parent = parents[0];
3894					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
3895						parent.empty().remove();
3896					}                                       
3897                                        
3898				} else if (node.parent) {
3899					// If it's an LI try to find a UL/OL for it or wrap it
3900					if (node.name === 'li') {
3901						sibling = node.prev;
3902						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
3903							sibling.append(node);
3904							continue;
3905						}
3906
3907						sibling = node.next;
3908						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
3909							sibling.insert(node, sibling.firstChild, true);
3910							continue;
3911						}
3912
3913						node.wrap(self.filterNode(new Node('ul', 1)));
3914						continue;
3915					}
3916
3917					// Try wrapping the element in a DIV
3918					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
3919						node.wrap(self.filterNode(new Node('div', 1)));
3920					} else {
3921						// We failed wrapping it, then remove or unwrap it
3922						if (node.name === 'style' || node.name === 'script')
3923							node.empty().remove();
3924						else
3925							node.unwrap();
3926					}
3927				}
3928			}
3929		};
3930
3931		self.filterNode = function(node) {
3932			var i, name, list;
3933
3934			// Run element filters
3935			if (name in nodeFilters) {
3936				list = matchedNodes[name];
3937
3938				if (list)
3939					list.push(node);
3940				else
3941					matchedNodes[name] = [node];
3942			}
3943
3944			// Run attribute filters
3945			i = attributeFilters.length;
3946			while (i--) {
3947				name = attributeFilters[i].name;
3948
3949				if (name in node.attributes.map) {
3950					list = matchedAttributes[name];
3951
3952					if (list)
3953						list.push(node);
3954					else
3955						matchedAttributes[name] = [node];
3956				}
3957			}
3958
3959			return node;
3960		};
3961
3962		self.addNodeFilter = function(name, callback) {
3963			tinymce.each(tinymce.explode(name), function(name) {
3964				var list = nodeFilters[name];
3965
3966				if (!list)
3967					nodeFilters[name] = list = [];
3968
3969				list.push(callback);
3970			});
3971		};
3972
3973		self.addAttributeFilter = function(name, callback) {
3974			tinymce.each(tinymce.explode(name), function(name) {
3975				var i;
3976
3977				for (i = 0; i < attributeFilters.length; i++) {
3978					if (attributeFilters[i].name === name) {
3979						attributeFilters[i].callbacks.push(callback);
3980						return;
3981					}
3982				}
3983
3984				attributeFilters.push({name: name, callbacks: [callback]});
3985			});
3986		};
3987
3988		self.parse = function(html, args) {
3989			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,
3990				blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement,
3991				endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;
3992
3993			args = args || {};
3994			matchedNodes = {};
3995			matchedAttributes = {};
3996			blockElements = tinymce.extend(tinymce.makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
3997			nonEmptyElements = schema.getNonEmptyElements();
3998			children = schema.children;
3999			validate = settings.validate;
4000			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
4001
4002			whiteSpaceElements = schema.getWhiteSpaceElements();
4003			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
4004			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
4005			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
4006			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
4007
4008			function addRootBlocks() {
4009				var node = rootNode.firstChild, next, rootBlockNode;
4010
4011				while (node) {
4012					next = node.next;
4013
4014					if (node.type == 3 || (node.type == 1 && node.name !== 'p' && !blockElements[node.name] && !node.attr('data-mce-type'))) {
4015						if (!rootBlockNode) {
4016							// Create a new root block element
4017							rootBlockNode = createNode(rootBlockName, 1);
4018							rootNode.insert(rootBlockNode, node);
4019							rootBlockNode.append(node);
4020						} else
4021							rootBlockNode.append(node);
4022					} else {
4023						rootBlockNode = null;
4024					}
4025
4026					node = next;
4027				};
4028			};
4029
4030			function createNode(name, type) {
4031				var node = new Node(name, type), list;
4032
4033				if (name in nodeFilters) {
4034					list = matchedNodes[name];
4035
4036					if (list)
4037						list.push(node);
4038					else
4039						matchedNodes[name] = [node];
4040				}
4041
4042				return node;
4043			};
4044
4045			function removeWhitespaceBefore(node) {
4046				var textNode, textVal, sibling;
4047
4048				for (textNode = node.prev; textNode && textNode.type === 3; ) {
4049					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
4050
4051					if (textVal.length > 0) {
4052						textNode.value = textVal;
4053						textNode = textNode.prev;
4054					} else {
4055						sibling = textNode.prev;
4056						textNode.remove();
4057						textNode = sibling;
4058					}
4059				}
4060			};
4061
4062			function cloneAndExcludeBlocks(input) {
4063				var name, output = {};
4064
4065				for (name in input) {
4066					if (name !== 'li' && name != 'p') {
4067						output[name] = input[name];
4068					}
4069				}
4070
4071				return output;
4072			};
4073
4074			parser = new tinymce.html.SaxParser({
4075				validate : validate,
4076
4077				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
4078				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
4079
4080				cdata: function(text) {
4081					node.append(createNode('#cdata', 4)).value = text;
4082				},
4083
4084				text: function(text, raw) {
4085					var textNode;
4086
4087					// Trim all redundant whitespace on non white space elements
4088					if (!isInWhiteSpacePreservedElement) {
4089						text = text.replace(allWhiteSpaceRegExp, ' ');
4090
4091						if (node.lastChild && blockElements[node.lastChild.name])
4092							text = text.replace(startWhiteSpaceRegExp, '');
4093					}
4094
4095					// Do we need to create the node
4096					if (text.length !== 0) {
4097						textNode = createNode('#text', 3);
4098						textNode.raw = !!raw;
4099						node.append(textNode).value = text;
4100					}
4101				},
4102
4103				comment: function(text) {
4104					node.append(createNode('#comment', 8)).value = text;
4105				},
4106
4107				pi: function(name, text) {
4108					node.append(createNode(name, 7)).value = text;
4109					removeWhitespaceBefore(node);
4110				},
4111
4112				doctype: function(text) {
4113					var newNode;
4114		
4115					newNode = node.append(createNode('#doctype', 10));
4116					newNode.value = text;
4117					removeWhitespaceBefore(node);
4118				},
4119
4120				start: function(name, attrs, empty) {
4121					var newNode, attrFiltersLen, elementRule, textNode, attrName, text, sibling, parent;
4122
4123					elementRule = validate ? schema.getElementRule(name) : {};
4124					if (elementRule) {
4125						newNode = createNode(elementRule.outputName || name, 1);
4126						newNode.attributes = attrs;
4127						newNode.shortEnded = empty;
4128
4129						node.append(newNode);
4130
4131						// Check if node is valid child of the parent node is the child is
4132						// unknown we don't collect it since it's probably a custom element
4133						parent = children[node.name];
4134						if (parent && children[newNode.name] && !parent[newNode.name])
4135							invalidChildren.push(newNode);
4136
4137						attrFiltersLen = attributeFilters.length;
4138						while (attrFiltersLen--) {
4139							attrName = attributeFilters[attrFiltersLen].name;
4140
4141							if (attrName in attrs.map) {
4142								list = matchedAttributes[attrName];
4143
4144								if (list)
4145									list.push(newNode);
4146								else
4147									matchedAttributes[attrName] = [newNode];
4148							}
4149						}
4150
4151						// Trim whitespace before block
4152						if (blockElements[name])
4153							removeWhitespaceBefore(newNode);
4154
4155						// Change current node if the element wasn't empty i.e not <br /> or <img />
4156						if (!empty)
4157							node = newNode;
4158
4159						// Check if we are inside a whitespace preserved element
4160						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
4161							isInWhiteSpacePreservedElement = true;
4162						}
4163					}
4164				},
4165
4166				end: function(name) {
4167					var textNode, elementRule, text, sibling, tempNode;
4168
4169					elementRule = validate ? schema.getElementRule(name) : {};
4170					if (elementRule) {
4171						if (blockElements[name]) {
4172							if (!isInWhiteSpacePreservedElement) {
4173								// Trim whitespace of the first node in a block
4174								textNode = node.firstChild;
4175								if (textNode && textNode.type === 3) {
4176									text = textNode.value.replace(startWhiteSpaceRegExp, '');
4177
4178									// Any characters left after trim or should we remove it
4179									if (text.length > 0) {
4180										textNode.value = text;
4181										textNode = textNode.next;
4182									} else {
4183										sibling = textNode.next;
4184										textNode.remove();
4185										textNode = sibling;
4186									}
4187
4188									// Remove any pure whitespace siblings
4189									while (textNode && textNode.type === 3) {
4190										text = textNode.value;
4191										sibling = textNode.next;
4192
4193										if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
4194											textNode.remove();
4195											textNode = sibling;
4196										}
4197
4198										textNode = sibling;
4199									}
4200								}
4201
4202								// Trim whitespace of the last node in a block
4203								textNode = node.lastChild;
4204								if (textNode && textNode.type === 3) {
4205									text = textNode.value.replace(endWhiteSpaceRegExp, '');
4206
4207									// Any characters left after trim or should we remove it
4208									if (text.length > 0) {
4209										textNode.value = text;
4210										textNode = textNode.prev;
4211									} else {
4212										sibling = textNode.prev;
4213										textNode.remove();
4214										textNode = sibling;
4215									}
4216
4217									// Remove any pure whitespace siblings
4218									while (textNode && textNode.type === 3) {
4219										text = textNode.value;
4220										sibling = textNode.prev;
4221
4222										if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
4223											textNode.remove();
4224											textNode = sibling;
4225										}
4226
4227										textNode = sibling;
4228									}
4229								}
4230							}
4231
4232							// Trim start white space
4233							// Removed due to: #5424
4234							/*textNode = node.prev;
4235							if (textNode && textNode.type === 3) {
4236								text = textNode.value.replace(startWhiteSpaceRegExp, '');
4237
4238								if (text.length > 0)
4239									textNode.value = text;
4240								else
4241									textNode.remove();
4242							}*/
4243						}
4244
4245						// Check if we exited a whitespace preserved element
4246						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
4247							isInWhiteSpacePreservedElement = false;
4248						}
4249
4250						// Handle empty nodes
4251						if (elementRule.removeEmpty || elementRule.paddEmpty) {
4252							if (node.isEmpty(nonEmptyElements)) {
4253								if (elementRule.paddEmpty)
4254									node.empty().append(new Node('#text', '3')).value = '\u00a0';
4255								else {
4256									// Leave nodes that have a name like <a name="name">
4257									if (!node.attributes.map.name && !node.attributes.map.id) {
4258										tempNode = node.parent;
4259										node.empty().remove();
4260										node = tempNode;
4261										return;
4262									}
4263								}
4264							}
4265						}
4266
4267						node = node.parent;
4268					}
4269				}
4270			}, schema);
4271
4272			rootNode = node = new Node(args.context || settings.root_name, 11);
4273
4274			parser.parse(html);
4275
4276			// Fix invalid children or report invalid children in a contextual parsing
4277			if (validate && invalidChildren.length) {
4278				if (!args.context)
4279					fixInvalidChildren(invalidChildren);
4280				else
4281					args.invalid = true;
4282			}
4283
4284			// Wrap nodes in the root into block elements if the root is body
4285			if (rootBlockName && rootNode.name == 'body')
4286				addRootBlocks();
4287
4288			// Run filters only when the contents is valid
4289			if (!args.invalid) {
4290				// Run node filters
4291				for (name in matchedNodes) {
4292					list = nodeFilters[name];
4293					nodes = matchedNodes[name];
4294
4295					// Remove already removed children
4296					fi = nodes.length;
4297					while (fi--) {
4298						if (!nodes[fi].parent)
4299							nodes.splice(fi, 1);
4300					}
4301
4302					for (i = 0, l = list.length; i < l; i++)
4303						list[i](nodes, name, args);
4304				}
4305
4306				// Run attribute filters
4307				for (i = 0, l = attributeFilters.length; i < l; i++) {
4308					list = attributeFilters[i];
4309
4310					if (list.name in matchedAttributes) {
4311						nodes = matchedAttributes[list.name];
4312
4313						// Remove already removed children
4314						fi = nodes.length;
4315						while (fi--) {
4316							if (!nodes[fi].parent)
4317								nodes.splice(fi, 1);
4318						}
4319
4320						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++)
4321							list.callbacks[fi](nodes, list.name, args);
4322					}
4323				}
4324			}
4325
4326			return rootNode;
4327		};
4328
4329		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
4330		// make it possible to place the caret inside empty blocks. This logic tries to remove
4331		// these elements and keep br elements that where intended to be there intact
4332		if (settings.remove_trailing_brs) {
4333			self.addNodeFilter('br', function(nodes, name) {
4334				var i, l = nodes.length, node, blockElements = tinymce.extend({}, schema.getBlockElements()),
4335					nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
4336
4337				// Remove brs from body element as well
4338				blockElements.body = 1;
4339
4340				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
4341				for (i = 0; i < l; i++) {
4342					node = nodes[i];
4343					parent = node.parent;
4344
4345					if (blockElements[node.parent.name] && node === parent.lastChild) {
4346						// Loop all nodes to the left of the current node and check for other BR elements
4347						// excluding bookmarks since they are invisible
4348						prev = node.prev;
4349						while (prev) {
4350							prevName = prev.name;
4351
4352							// Ignore bookmarks
4353							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
4354								// Found a non BR element
4355								if (prevName !== "br")
4356									break;
4357	
4358								// Found another br it's a <br><br> structure then don't remove anything
4359								if (prevName === 'br') {
4360									node = null;
4361									break;
4362								}
4363							}
4364
4365							prev = prev.prev;
4366						}
4367
4368						if (node) {
4369							node.remove();
4370
4371							// Is the parent to be considered empty after we removed the BR
4372							if (parent.isEmpty(nonEmptyElements)) {
4373								elementRule = schema.getElementRule(parent.name);
4374
4375								// Remove or padd the element depending on schema rule
4376								if (elementRule) {
4377									if (elementRule.removeEmpty)
4378										parent.remove();
4379									else if (elementRule.paddEmpty)
4380										parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
4381								}
4382							}
4383						}
4384					} else {
4385						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p> so they become <p><b><i>&nbsp;</i></b></p> 
4386						lastParent = node;
4387						while (parent.firstChild === lastParent && parent.lastChild === lastParent) {
4388							lastParent = parent;
4389
4390							if (blockElements[parent.name]) {
4391								break;
4392							}
4393
4394							parent = parent.parent;
4395						}
4396
4397						if (lastParent === parent) {
4398							textNode = new tinymce.html.Node('#text', 3);
4399							textNode.value = '\u00a0';
4400							node.replace(textNode);
4401						}
4402					}
4403				}
4404			});
4405		}
4406
4407		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
4408		if (!settings.allow_html_in_named_anchor) {
4409			self.addAttributeFilter('id,name', function(nodes, name) {
4410				var i = nodes.length, sibling, prevSibling, parent, node;
4411
4412				while (i--) {
4413					node = nodes[i];
4414					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
4415						parent = node.parent;
4416
4417						// Move children after current node
4418						sibling = node.lastChild;
4419						do {
4420							prevSibling = sibling.prev;
4421							parent.insert(sibling, node);
4422							sibling = prevSibling;
4423						} while (sibling);
4424					}
4425				}
4426			});
4427		}
4428	}
4429})(tinymce);
4430
4431tinymce.html.Writer = function(settings) {
4432	var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
4433
4434	settings = settings || {};
4435	indent = settings.indent;
4436	indentBefore = tinymce.makeMap(settings.indent_before || '');
4437	indentAfter = tinymce.makeMap(settings.indent_after || '');
4438	encode = tinymce.html.Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
4439	htmlOutput = settings.element_format == "html";
4440
4441	return {
4442		start: function(name, attrs, empty) {
4443			var i, l, attr, value;
4444
4445			if (indent && indentBefore[name] && html.length > 0) {
4446				value = html[html.length - 1];
4447
4448				if (value.length > 0 && value !== '\n')
4449					html.push('\n');
4450			}
4451
4452			html.push('<', name);
4453
4454			if (attrs) {
4455				for (i = 0, l = attrs.length; i < l; i++) {
4456					attr = attrs[i];
4457					html.push(' ', attr.name, '="', encode(attr.value, true), '"');
4458				}
4459			}
4460
4461			if (!empty || htmlOutput)
4462				html[html.length] = '>';
4463			else
4464				html[html.length] = ' />';
4465
4466			if (empty && indent && indentAfter[name] && html.length > 0) {
4467				value = html[html.length - 1];
4468
4469				if (value.length > 0 && value !== '\n')
4470					html.push('\n');
4471			}
4472		},
4473
4474		end: function(name) {
4475			var value;
4476
4477			/*if (indent && indentBefore[name] && html.length > 0) {
4478				value = html[html.length - 1];
4479
4480				if (value.length > 0 && value !== '\n')
4481					html.push('\n');
4482			}*/
4483
4484			html.push('</', name, '>');
4485
4486			if (indent && indentAfter[name] && html.length > 0) {
4487				value = html[html.length - 1];
4488
4489				if (value.length > 0 && value !== '\n')
4490					html.push('\n');
4491			}
4492		},
4493
4494		text: function(text, raw) {
4495			if (text.length > 0)
4496				html[html.length] = raw ? text : encode(text);
4497		},
4498
4499		cdata: function(text) {
4500			html.push('<![CDATA[', text, ']]>');
4501		},
4502
4503		comment: function(text) {
4504			html.push('<!--', text, '-->');
4505		},
4506
4507		pi: function(name, text) {
4508			if (text)
4509				html.push('<?', name, ' ', text, '?>');
4510			else
4511				html.push('<?', name, '?>');
4512
4513			if (indent)
4514				html.push('\n');
4515		},
4516
4517		doctype: function(text) {
4518			html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
4519		},
4520
4521		reset: function() {
4522			html.length = 0;
4523		},
4524
4525		getContent: function() {
4526			return html.join('').replace(/\n$/, '');
4527		}
4528	};
4529};
4530
4531(function(tinymce) {
4532	tinymce.html.Serializer = function(settings, schema) {
4533		var self = this, writer = new tinymce.html.Writer(settings);
4534
4535		settings = settings || {};
4536		settings.validate = "validate" in settings ? settings.validate : true;
4537
4538		self.schema = schema = schema || new tinymce.html.Schema();
4539		self.writer = writer;
4540
4541		self.serialize = function(node) {
4542			var handlers, validate;
4543
4544			validate = settings.validate;
4545
4546			handlers = {
4547				// #text
4548				3: function(node, raw) {
4549					writer.text(node.value, node.raw);
4550				},
4551
4552				// #comment
4553				8: function(node) {
4554					writer.comment(node.value);
4555				},
4556
4557				// Processing instruction
4558				7: function(node) {
4559					writer.pi(node.name, node.value);
4560				},
4561
4562				// Doctype
4563				10: function(node) {
4564					writer.doctype(node.value);
4565				},
4566
4567				// CDATA
4568				4: function(node) {
4569					writer.cdata(node.value);
4570				},
4571
4572				// Document fragment
4573				11: function(node) {
4574					if ((node = node.firstChild)) {
4575						do {
4576							walk(node);
4577						} while (node = node.next);
4578					}
4579				}
4580			};
4581
4582			writer.reset();
4583
4584			function walk(node) {
4585				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
4586
4587				if (!handler) {
4588					name = node.name;
4589					isEmpty = node.shortEnded;
4590					attrs = node.attributes;
4591
4592					// Sort attributes
4593					if (validate && attrs && attrs.length > 1) {
4594						sortedAttrs = [];
4595						sortedAttrs.map = {};
4596
4597						elementRule = schema.getElementRule(node.name);
4598						for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
4599							attrName = elementRule.attributesOrder[i];
4600
4601							if (attrName in attrs.map) {
4602								attrValue = attrs.map[attrName];
4603								sortedAttrs.map[attrName] = attrValue;
4604								sortedAttrs.push({name: attrName, value: attrValue});
4605							}
4606						}
4607
4608						for (i = 0, l = attrs.length; i < l; i++) {
4609							attrName = attrs[i].name;
4610
4611							if (!(attrName in sortedAttrs.map)) {
4612								attrValue = attrs.map[attrName];
4613								sortedAttrs.map[attrName] = attrValue;
4614								sortedAttrs.push({name: attrName, value: attrValue});
4615							}
4616						}
4617
4618						attrs = sortedAttrs;
4619					}
4620
4621					writer.start(node.name, attrs, isEmpty);
4622
4623					if (!isEmpty) {
4624						if ((node = node.firstChild)) {
4625							do {
4626								walk(node);
4627							} while (node = node.next);
4628						}
4629
4630						writer.end(name);
4631					}
4632				} else
4633					handler(node);
4634			}
4635
4636			// Serialize element and treat all non elements as fragments
4637			if (node.type == 1 && !settings.inner)
4638				walk(node);
4639			else
4640				handlers[11](node);
4641
4642			return writer.getContent();
4643		};
4644	}
4645})(tinymce);
4646
4647// JSLint defined globals
4648/*global tinymce:false, window:false */
4649
4650tinymce.dom = {};
4651
4652(function(namespace, expando) {
4653	var w3cEventModel = !!document.addEventListener;
4654
4655	function addEvent(target, name, callback, capture) {
4656		if (target.addEventListener) {
4657			target.addEventListener(name, callback, capture || false);
4658		} else if (target.attachEvent) {
4659			target.attachEvent('on' + name, callback);
4660		}
4661	}
4662
4663	function removeEvent(target, name, callback, capture) {
4664		if (target.removeEventListener) {
4665			target.removeEventListener(name, callback, capture || false);
4666		} else if (target.detachEvent) {
4667			target.detachEvent('on' + name, callback);
4668		}
4669	}
4670
4671	function fix(original_event, data) {
4672		var name, event = data || {};
4673
4674		// Dummy function that gets replaced on the delegation state functions
4675		function returnFalse() {
4676			return false;
4677		}
4678
4679		// Dummy function that gets replaced on the delegation state functions
4680		function returnTrue() {
4681			return true;
4682		}
4683
4684		// Copy all properties from the original event
4685		for (name in original_event) {
4686			// layerX/layerY is deprecated in Chrome and produces a warning
4687			if (name !== "layerX" && name !== "layerY") {
4688				event[name] = original_event[name];
4689			}
4690		}
4691
4692		// Normalize target IE uses srcElement
4693		if (!event.target) {
4694			event.target = event.srcElement || document;
4695		}
4696
4697		// Add preventDefault method
4698		event.preventDefault = function() {
4699			event.isDefaultPrevented = returnTrue;
4700
4701			// Execute preventDefault on the original event object
4702			if (original_event) {
4703				if (original_event.preventDefault) {
4704					original_event.preventDefault();
4705				} else {
4706					original_event.returnValue = false; // IE
4707				}
4708			}
4709		};
4710
4711		// Add stopPropagation
4712		event.stopPropagation = function() {
4713			event.isPropagationStopped = returnTrue;
4714
4715			// Execute stopPropagation on the original event object
4716			if (original_event) {
4717				if (original_event.stopPropagation) {
4718					original_event.stopPropagation();
4719				} else {
4720					original_event.cancelBubble = true; // IE
4721				}
4722			 }
4723		};
4724
4725		// Add stopImmediatePropagation
4726		event.stopImmediatePropagation = function() {
4727			event.isImmediatePropagationStopped = returnTrue;
4728			event.stopPropagation();
4729		};
4730
4731		// Add event delegation states
4732		if (!event.isDefaultPrevented) {
4733			event.isDefaultPrevented = returnFalse;
4734			event.isPropagationStopped = returnFalse;
4735			event.isImmediatePropagationStopped = returnFalse;
4736		}
4737
4738		return event;
4739	}
4740
4741	function bindOnReady(win, callback, event_utils) {
4742		var doc = win.document, event = {type: 'ready'};
4743
4744		// Gets called when the DOM is ready
4745		function readyHandler() {
4746			if (!event_utils.domLoaded) {
4747				event_utils.domLoaded = true;
4748				callback(event);
4749			}
4750		}
4751
4752		// Page already loaded then fire it directly
4753		if (doc.readyState == "complete") {
4754			readyHandler();
4755			return;
4756		}
4757
4758		// Use W3C method
4759		if (w3cEventModel) {
4760			addEvent(win, 'DOMContentLoaded', readyHandler);
4761		} else {
4762			// Use IE method
4763			addEvent(doc, "readystatechange", function() {
4764				if (doc.readyState === "complete") {
4765					removeEvent(doc, "readystatechange", arguments.callee);
4766					readyHandler();
4767				}
4768			});
4769
4770			// Wait until we can scroll, when we can the DOM is initialized
4771			if (doc.documentElement.doScroll && win === win.top) {
4772				(function() {
4773					try {
4774						// If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
4775						// http://javascript.nwbox.com/IEContentLoaded/
4776						doc.documentElement.doScroll("left");
4777					} catch (ex) {
4778						setTimeout(arguments.callee, 0);
4779						return;
4780					}
4781
4782					readyHandler();
4783				})();
4784			}
4785		}
4786
4787		// Fallback if any of the above methods should fail for some odd reason
4788		addEvent(win, 'load', readyHandler);
4789	}
4790
4791	function EventUtils(proxy) {
4792		var self = this, events = {}, count, isFocusBlurBound, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
4793
4794		hasMouseEnterLeave = "onmouseenter" in document.documentElement;
4795		hasFocusIn = "onfocusin" in document.documentElement;
4796		mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
4797		count = 1;
4798
4799		// State if the DOMContentLoaded was executed or not
4800		self.domLoaded = false;
4801		self.events = events;
4802
4803		function executeHandlers(evt, id) {
4804			var callbackList, i, l, callback;
4805
4806			callbackList = events[id][evt.type];
4807			if (callbackList) {
4808				for (i = 0, l = callbackList.length; i < l; i++) {
4809					callback = callbackList[i];
4810					
4811					// Check if callback exists might be removed if a unbind is called inside the callback
4812					if (callback && callback.func.call(callback.scope, evt) === false) {
4813						evt.preventDefault();
4814					}
4815
4816					// Should we stop propagation to immediate listeners
4817					if (evt.isImmediatePropagationStopped()) {
4818						return;
4819					}
4820				}
4821			}
4822		}
4823
4824		self.bind = function(target, names, callback, scope) {
4825			var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
4826
4827			// Native event handler function patches the event and executes the callbacks for the expando
4828			function defaultNativeHandler(evt) {
4829				executeHandlers(fix(evt || win.event), id);
4830			}
4831
4832			// Don't bind to text nodes or comments
4833			if (!target || target.nodeType === 3 || target.nodeType === 8) {
4834				return;
4835			}
4836
4837			// Create or get events id for the target
4838			if (!target[expando]) {
4839				id = count++;
4840				target[expando] = id;
4841				events[id] = {};
4842			} else {
4843				id = target[expando];
4844
4845				if (!events[id]) {
4846					events[id] = {};
4847				}
4848			}
4849
4850			// Setup the specified scope or use the target as a default
4851			scope = scope || target;
4852
4853			// Split names and bind each event, enables you to bind multiple events with one call
4854			names = names.split(' ');
4855			i = names.length;
4856			while (i--) {
4857				name = names[i];
4858				nativeHandler = defaultNativeHandler;
4859				fakeName = capture = false;
4860
4861				// Use ready instead of DOMContentLoaded
4862				if (name === "DOMContentLoaded") {
4863					name = "ready";
4864				}
4865
4866				// DOM is already ready
4867				if ((self.domLoaded || target.readyState == 'complete') && name === "ready") {
4868					self.domLoaded = true;
4869					callback.call(scope, fix({type: name}));
4870					continue;
4871				}
4872
4873				// Handle mouseenter/mouseleaver
4874				if (!hasMouseEnterLeave) {
4875					fakeName = mouseEnterLeave[name];
4876
4877					if (fakeName) {
4878						nativeHandler = function(evt) {
4879							var current, related;
4880
4881							current = evt.currentTarget;
4882							related = evt.relatedTarget;
4883
4884							// Check if related is inside the current target if it's not then the event should be ignored since it's a mouseover/mouseout inside the element
4885							if (related && current.contains) {
4886								// Use contains for performance
4887								related = current.contains(related);
4888							} else {
4889								while (related && related !== current) {
4890									related = related.parentNode;
4891								}
4892							}
4893
4894							// Fire fake event
4895							if (!related) {
4896								evt = fix(evt || win.event);
4897								evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
4898								evt.target = current;
4899								executeHandlers(evt, id);
4900							}
4901						};
4902					}
4903				}
4904
4905				// Fake bubbeling of focusin/focusout
4906				if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
4907					capture = true;
4908					fakeName = name === "focusin" ? "focus" : "blur";
4909					nativeHandler = function(evt) {
4910						evt = fix(evt || win.event);
4911						evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
4912						executeHandlers(evt, id);
4913					};
4914				}
4915
4916				// Setup callback list and bind native event
4917				callbackList = events[id][name];
4918				if (!callbackList) {
4919					events[id][name] = callbackList = [{func: callback, scope: scope}];
4920					callbackList.fakeName = fakeName;
4921					callbackList.capture = capture;
4922
4923					// Add the nativeHandler to the callback list so that we can later unbind it
4924					callbackList.nativeHandler = nativeHandler;
4925					if (!w3cEventModel) {
4926						callbackList.proxyHandler = proxy(id);
4927					}
4928
4929					// Check if the target has native events support
4930					if (name === "ready") {
4931						bindOnReady(target, nativeHandler, self);
4932					} else {
4933						addEvent(target, fakeName || name, w3cEventModel ? nativeHandler : callbackList.proxyHandler, capture);
4934					}
4935				} else {
4936					// If it already has an native handler then just push the callback
4937					callbackList.push({func: callback, scope: scope});
4938				}
4939			}
4940
4941			target = callbackList = 0; // Clean memory for IE
4942
4943			return callback;
4944		};
4945
4946		self.unbind = function(target, names, callback) {
4947			var id, callbackList, i, ci, name, eventMap;
4948
4949			// Don't bind to text nodes or comments
4950			if (!target || target.nodeType === 3 || target.nodeType === 8) {
4951				return self;
4952			}
4953
4954			// Unbind event or events if the target has the expando
4955			id = target[expando];
4956			if (id) {
4957				eventMap = events[id];
4958
4959				// Specific callback
4960				if (names) {
4961					names = names.split(' ');
4962					i = names.length;
4963					while (i--) {
4964						name = names[i];
4965						callbackList = eventMap[name];
4966
4967						// Unbind the event if it exists in the map
4968						if (callbackList) {
4969							// Remove specified callback
4970							if (callback) {
4971								ci = callbackList.length;
4972								while (ci--) {
4973									if (callbackList[ci].func === callback) {
4974										callbackList.splice(ci, 1);
4975									}
4976								}
4977							}
4978
4979							// Remove all callbacks if there isn't a specified callback or there is no callbacks left
4980							if (!callback || callbackList.length === 0) {
4981								delete eventMap[name];
4982								removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);
4983							}
4984						}
4985					}
4986				} else {
4987					// All events for a specific element
4988					for (name in eventMap) {
4989						callbackList = eventMap[name];
4990						removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);
4991					}
4992
4993					eventMap = {};
4994				}
4995
4996				// Check if object is empty, if it isn't then we won't remove the expando map
4997				for (name in eventMap) {
4998					return self;
4999				}
5000
5001				// Delete event object
5002				delete events[id];
5003
5004				// Remove expando from target
5005				try {
5006					// IE will fail here since it can't delete properties from window
5007					delete target[expando];
5008				} catch (ex) {
5009					// IE will set it to null
5010					target[expando] = null;
5011				}
5012			}
5013
5014			return self;
5015		};
5016
5017		self.fire = function(target, name, args) {
5018			var id, event;
5019
5020			// Don't bind to text nodes or comments
5021			if (!target || target.nodeType === 3 || target.nodeType === 8) {
5022				return self;
5023			}
5024
5025			// Build event object by patching the args
5026			event = fix(null, args);
5027			event.type = name;
5028
5029			do {
5030				// Found an expando that means there is listeners to execute
5031				id = target[expando];
5032				if (id) {
5033					executeHandlers(event, id);
5034				}
5035
5036				// Walk up the DOM
5037				target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
5038			} while (target && !event.isPropagationStopped());
5039
5040			return self;
5041		};
5042
5043		self.clean = function(target) {
5044			var i, children, unbind = self.unbind;
5045	
5046			// Don't bind to text nodes or comments
5047			if (!target || target.nodeType === 3 || target.nodeType === 8) {
5048				return self;
5049			}
5050
5051			// Unbind any element on the specificed target
5052			if (target[expando]) {
5053				unbind(target);
5054			}
5055
5056			// Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
5057			if (!target.getElementsByTagName) {
5058				target = target.document;
5059			}
5060
5061			// Remove events from each child element
5062			if (target && target.getElementsByTagName) {
5063				unbind(target);
5064
5065				children = target.getElementsByTagName('*');
5066				i = children.length;
5067				while (i--) {
5068					target = children[i];
5069
5070					if (target[expando]) {
5071						unbind(target);
5072					}
5073				}
5074			}
5075
5076			return self;
5077		};
5078
5079		self.callNativeHandler = function(id, evt) {
5080			if (events) {
5081				events[id][evt.type].nativeHandler(evt);
5082			}
5083		};
5084
5085		self.destory = function() {
5086			events = {};
5087		};
5088
5089		// Legacy function calls
5090
5091		self.add = function(target, events, func, scope) {
5092			// Old API supported direct ID assignment
5093			if (typeof(target) === "string") {
5094				target = document.getElementById(target);
5095			}
5096
5097			// Old API supported multiple targets
5098			if (target && target instanceof Array) {
5099				var i = target.length;
5100
5101				while (i--) {
5102					self.add(target[i], events, func, scope);
5103				}
5104
5105				return;
5106			}
5107
5108			// Old API called ready init
5109			if (events === "init") {
5110				events = "ready";
5111			}
5112
5113			return self.bind(target, events instanceof Array ? events.join(' ') : events, func, scope);
5114		};
5115
5116		self.remove = function(target, events, func, scope) {
5117			if (!target) {
5118				return self;
5119			}
5120
5121			// Old API supported direct ID assignment
5122			if (typeof(target) === "string") {
5123				target = document.getElementById(target);
5124			}
5125
5126			// Old API supported multiple targets
5127			if (target instanceof Array) {
5128				var i = target.length;
5129
5130				while (i--) {
5131					self.remove(target[i], events, func, scope);
5132				}
5133
5134				return self;
5135			}
5136
5137			return self.unbind(target, events instanceof Array ? events.join(' ') : events, func);
5138		};
5139
5140		self.clear = function(target) {
5141			// Old API supported direct ID assignment
5142			if (typeof(target) === "string") {
5143				target = document.getElementById(target);
5144			}
5145
5146			return self.clean(target);
5147		};
5148
5149		self.cancel = function(e) {
5150			if (e) {
5151				self.prevent(e);
5152				self.stop(e);
5153			}
5154
5155			return false;
5156		};
5157
5158		self.prevent = function(e) {
5159			if (!e.preventDefault) {
5160				e = fix(e);
5161			}
5162
5163			e.preventDefault();
5164
5165			return false;
5166		};
5167
5168		self.stop = function(e) {
5169			if (!e.stopPropagation) {
5170				e = fix(e);
5171			}
5172
5173			e.stopPropagation();
5174
5175			return false;
5176		};
5177	}
5178
5179	namespace.EventUtils = EventUtils;
5180
5181	namespace.Event = new EventUtils(function(id) {
5182		return function(evt) {
5183			tinymce.dom.Event.callNativeHandler(id, evt);
5184		};
5185	});
5186
5187	// Bind ready event when tinymce script is loaded
5188	namespace.Event.bind(window, 'ready', function() {});
5189
5190	namespace = 0;
5191})(tinymce.dom, 'data-mce-expando'); // Namespace and expando
5192
5193tinymce.dom.TreeWalker = function(start_node, root_node) {
5194	var node = start_node;
5195
5196	function findSibling(node, start_name, sibling_name, shallow) {
5197		var sibling, parent;
5198
5199		if (node) {
5200			// Walk into nodes if it has a start
5201			if (!shallow && node[start_name])
5202				return node[start_name];
5203
5204			// Return the sibling if it has one
5205			if (node != root_node) {
5206				sibling = node[sibling_name];
5207				if (sibling)
5208					return sibling;
5209
5210				// Walk up the parents to look for siblings
5211				for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
5212					sibling = parent[sibling_name];
5213					if (sibling)
5214						return sibling;
5215				}
5216			}
5217		}
5218	};
5219
5220	this.current = function() {
5221		return node;
5222	};
5223
5224	this.next = function(shallow) {
5225		return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));
5226	};
5227
5228	this.prev = function(shallow) {
5229		return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));
5230	};
5231};
5232
5233(function(tinymce) {
5234	// Shorten names
5235	var each = tinymce.each,
5236		is = tinymce.is,
5237		isWebKit = tinymce.isWebKit,
5238		isIE = tinymce.isIE,
5239		Entities = tinymce.html.Entities,
5240		simpleSelectorRe = /^([a-z0-9],?)+$/i,
5241		whiteSpaceRegExp = /^[ \t\r\n]*$/;
5242
5243	tinymce.create('tinymce.dom.DOMUtils', {
5244		doc : null,
5245		root : null,
5246		files : null,
5247		pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
5248		props : {
5249			"for" : "htmlFor",
5250			"class" : "className",
5251			className : "className",
5252			checked : "checked",
5253			disabled : "disabled",
5254			maxlength : "maxLength",
5255			readonly : "readOnly",
5256			selected : "selected",
5257			value : "value",
5258			id : "id",
5259			name : "name",
5260			type : "type"
5261		},
5262
5263		DOMUtils : function(d, s) {
5264			var t = this, globalStyle, name, blockElementsMap;
5265
5266			t.doc = d;
5267			t.win = window;
5268			t.files = {};
5269			t.cssFlicker = false;
5270			t.counter = 0;
5271			t.stdMode = !tinymce.isIE || d.documentMode >= 8;
5272			t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;
5273			t.hasOuterHTML = "outerHTML" in d.createElement("a");
5274
5275			t.settings = s = tinymce.extend({
5276				keep_values : false,
5277				hex_colors : 1
5278			}, s);
5279			
5280			t.schema = s.schema;
5281			t.styles = new tinymce.html.Styles({
5282				url_converter : s.url_converter,
5283				url_converter_scope : s.url_converter_scope
5284			}, s.schema);
5285
5286			// Fix IE6SP2 flicker and check it failed for pre SP2
5287			if (tinymce.isIE6) {
5288				try {
5289					d.execCommand('BackgroundImageCache', false, true);
5290				} catch (e) {
5291					t.cssFlicker = true;
5292				}
5293			}
5294
5295			t.fixDoc(d);
5296			t.events = s.ownEvents ? new tinymce.dom.EventUtils(s.proxy) : tinymce.dom.Event;
5297			tinymce.addUnload(t.destroy, t);
5298			blockElementsMap = s.schema ? s.schema.getBlockElements() : {};
5299
5300			t.isBlock = function(node) {
5301				// Fix for #5446
5302				if (!node) {
5303					return false;
5304				}
5305
5306				// This function is called in module pattern style since it might be executed with the wrong this scope
5307				var type = node.nodeType;
5308
5309				// If it's a node then check the type and use the nodeName
5310				if (type)
5311					return !!(type === 1 && blockElementsMap[node.nodeName]);
5312
5313				return !!blockElementsMap[node];
5314			};
5315		},
5316
5317		fixDoc: function(doc) {
5318			var settings = this.settings, name;
5319
5320			if (isIE && !tinymce.isIE11 && settings.schema) {
5321				// Add missing HTML 4/5 elements to IE
5322				('abbr article aside audio canvas ' +
5323				'details figcaption figure footer ' +
5324				'header hgroup mark menu meter nav ' +
5325				'output progress section summary ' +
5326				'time video').replace(/\w+/g, function(name) {
5327					doc.createElement(name);
5328				});
5329
5330				// Create all custom elements
5331				for (name in settings.schema.getCustomElements()) {
5332					doc.createElement(name);
5333				}
5334			}
5335		},
5336
5337		clone: function(node, deep) {
5338			var self = this, clone, doc;
5339
5340			// TODO: Add feature detection here in the future
5341			if (!isIE || tinymce.isIE11 || node.nodeType !== 1 || deep) {
5342				return node.cloneNode(deep);
5343			}
5344
5345			doc = self.doc;
5346
5347			// Make a HTML5 safe shallow copy
5348			if (!deep) {
5349				clone = doc.createElement(node.nodeName);
5350
5351				// Copy attribs
5352				each(self.getAttribs(node), function(attr) {
5353					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
5354				});
5355
5356				return clone;
5357			}
5358/*
5359			// Setup HTML5 patched document fragment
5360			if (!self.frag) {
5361				self.frag = doc.createDocumentFragment();
5362				self.fixDoc(self.frag);
5363			}
5364
5365			// Make a deep copy by adding it to the document fragment then removing it this removed the :section
5366			clone = doc.createElement('div');
5367			self.frag.appendChild(clone);
5368			clone.innerHTML = node.outerHTML;
5369			self.frag.removeChild(clone);
5370*/
5371			return clone.firstChild;
5372		},
5373
5374		getRoot : function() {
5375			var t = this, s = t.settings;
5376
5377			return (s && t.get(s.root_element)) || t.doc.body;
5378		},
5379
5380		getViewPort : function(w) {
5381			var d, b;
5382
5383			w = !w ? this.win : w;
5384			d = w.document;
5385			b = this.boxModel ? d.documentElement : d.body;
5386
5387			// Returns viewport size excluding scrollbars
5388			return {
5389				x : w.pageXOffset || b.scrollLeft,
5390				y : w.pageYOffset || b.scrollTop,
5391				w : w.innerWidth || b.clientWidth,
5392				h : w.innerHeight || b.clientHeight
5393			};
5394		},
5395
5396		getRect : function(e) {
5397			var p, t = this, sr;
5398
5399			e = t.get(e);
5400			p = t.getPos(e);
5401			sr = t.getSize(e);
5402
5403			return {
5404				x : p.x,
5405				y : p.y,
5406				w : sr.w,
5407				h : sr.h
5408			};
5409		},
5410
5411		getSize : function(e) {
5412			var t = this, w, h;
5413
5414			e = t.get(e);
5415			w = t.getStyle(e, 'width');
5416			h = t.getStyle(e, 'height');
5417
5418			// Non pixel value, then force offset/clientWidth
5419			if (w.indexOf('px') === -1)
5420				w = 0;
5421
5422			// Non pixel value, then force offset/clientWidth
5423			if (h.indexOf('px') === -1)
5424				h = 0;
5425
5426			return {
5427				w : parseInt(w, 10) || e.offsetWidth || e.clientWidth,
5428				h : parseInt(h, 10) || e.offsetHeight || e.clientHeight
5429			};
5430		},
5431
5432		getParent : function(n, f, r) {
5433			return this.getParents(n, f, r, false);
5434		},
5435
5436		getParents : function(n, f, r, c) {
5437			var t = this, na, se = t.settings, o = [];
5438
5439			n = t.get(n);
5440			c = c === undefined;
5441
5442			if (se.strict_root)
5443				r = r || t.getRoot();
5444
5445			// Wrap node name as func
5446			if (is(f, 'string')) {
5447				na = f;
5448
5449				if (f === '*') {
5450					f = function(n) {return n.nodeType == 1;};
5451				} else {
5452					f = function(n) {
5453						return t.is(n, na);
5454					};
5455				}
5456			}
5457
5458			while (n) {
5459				if (n == r || !n.nodeType || n.nodeType === 9)
5460					break;
5461
5462				if (!f || f(n)) {
5463					if (c)
5464						o.push(n);
5465					else
5466						return n;
5467				}
5468
5469				n = n.parentNode;
5470			}
5471
5472			return c ? o : null;
5473		},
5474
5475		get : function(e) {
5476			var n;
5477
5478			if (e && this.doc && typeof(e) == 'string') {
5479				n = e;
5480				e = this.doc.getElementById(e);
5481
5482				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
5483				if (e && e.id !== n)
5484					return this.doc.getElementsByName(n)[1];
5485			}
5486
5487			return e;
5488		},
5489
5490		getNext : function(node, selector) {
5491			return this._findSib(node, selector, 'nextSibling');
5492		},
5493
5494		getPrev : function(node, selector) {
5495			return this._findSib(node, selector, 'previousSibling');
5496		},
5497
5498
5499		select : function(pa, s) {
5500			var t = this;
5501
5502			return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);
5503		},
5504
5505		is : function(n, selector) {
5506			var i;
5507
5508			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
5509			if (n.length === undefined) {
5510				// Simple all selector
5511				if (selector === '*')
5512					return n.nodeType == 1;
5513
5514				// Simple selector just elements
5515				if (simpleSelectorRe.test(selector)) {
5516					selector = selector.toLowerCase().split(/,/);
5517					n = n.nodeName.toLowerCase();
5518
5519					for (i = selector.length - 1; i >= 0; i--) {
5520						if (selector[i] == n)
5521							return true;
5522					}
5523
5524					return false;
5525				}
5526			}
5527
5528			return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;
5529		},
5530
5531
5532		add : function(p, n, a, h, c) {
5533			var t = this;
5534
5535			return this.run(p, function(p) {
5536				var e, k;
5537
5538				e = is(n, 'string') ? t.doc.createElement(n) : n;
5539				t.setAttribs(e, a);
5540
5541				if (h) {
5542					if (h.nodeType)
5543						e.appendChild(h);
5544					else
5545						t.setHTML(e, h);
5546				}
5547
5548				return !c ? p.appendChild(e) : e;
5549			});
5550		},
5551
5552		create : function(n, a, h) {
5553			return this.add(this.doc.createElement(n), n, a, h, 1);
5554		},
5555
5556		createHTML : function(n, a, h) {
5557			var o = '', t = this, k;
5558
5559			o += '<' + n;
5560
5561			for (k in a) {
5562				if (a.hasOwnProperty(k))
5563					o += ' ' + k + '="' + t.encode(a[k]) + '"';
5564			}
5565
5566			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
5567			if (typeof(h) != "undefined")
5568				return o + '>' + h + '</' + n + '>';
5569
5570			return o + ' >';
5571		},
5572
5573		remove : function(node, keep_children) {
5574			return this.run(node, function(node) {
5575				var child, parent = node.parentNode;
5576
5577				if (!parent)
5578					return null;
5579
5580				if (keep_children) {
5581					while (child = node.firstChild) {
5582						// IE 8 will crash if you don't remove completely empty text nodes
5583						if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)
5584							parent.insertBefore(child, node);
5585						else
5586							node.removeChild(child);
5587					}
5588				}
5589
5590				return parent.removeChild(node);
5591			});
5592		},
5593
5594		setStyle : function(n, na, v) {
5595			var t = this;
5596
5597			return t.run(n, function(e) {
5598				var s, i;
5599
5600				s = e.style;
5601
5602				// Camelcase it, if needed
5603				na = na.replace(/-(\D)/g, function(a, b){
5604					return b.toUpperCase();
5605				});
5606
5607				// Default px suffix on these
5608				if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
5609					v += 'px';
5610
5611				switch (na) {
5612					case 'opacity':
5613						// IE specific opacity
5614						if (isIE && ! tinymce.isIE11) {
5615							s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";
5616
5617							if (!n.currentStyle || !n.currentStyle.hasLayout)
5618								s.display = 'inline-block';
5619						}
5620
5621						// Fix for older browsers
5622						s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
5623						break;
5624
5625					case 'float':
5626						(isIE && ! tinymce.isIE11) ? s.styleFloat = v : s.cssFloat = v;
5627						break;
5628					
5629					default:
5630						s[na] = v || '';
5631				}
5632
5633				// Force update of the style data
5634				if (t.settings.update_styles)
5635					t.setAttrib(e, 'data-mce-style');
5636			});
5637		},
5638
5639		getStyle : function(n, na, c) {
5640			n = this.get(n);
5641
5642			if (!n)
5643				return;
5644
5645			// Gecko
5646			if (this.doc.defaultView && c) {
5647				// Remove camelcase
5648				na = na.replace(/[A-Z]/g, function(a){
5649					return '-' + a;
5650				});
5651
5652				try {
5653					return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
5654				} catch (ex) {
5655					// Old safari might fail
5656					return null;
5657				}
5658			}
5659
5660			// Camelcase it, if needed
5661			na = na.replace(/-(\D)/g, function(a, b){
5662				return b.toUpperCase();
5663			});
5664
5665			if (na == 'float')
5666				na = isIE ? 'styleFloat' : 'cssFloat';
5667
5668			// IE & Opera
5669			if (n.currentStyle && c)
5670				return n.currentStyle[na];
5671
5672			return n.style ? n.style[na] : undefined;
5673		},
5674
5675		setStyles : function(e, o) {
5676			var t = this, s = t.settings, ol;
5677
5678			ol = s.update_styles;
5679			s.update_styles = 0;
5680
5681			each(o, function(v, n) {
5682				t.setStyle(e, n, v);
5683			});
5684
5685			// Update style info
5686			s.update_styles = ol;
5687			if (s.update_styles)
5688				t.setAttrib(e, s.cssText);
5689		},
5690
5691		removeAllAttribs: function(e) {
5692			return this.run(e, function(e) {
5693				var i, attrs = e.attributes;
5694				for (i = attrs.length - 1; i >= 0; i--) {
5695					e.removeAttributeNode(attrs.item(i));
5696				}
5697			});
5698		},
5699
5700		setAttrib : function(e, n, v) {
5701			var t = this;
5702
5703			// Whats the point
5704			if (!e || !n)
5705				return;
5706
5707			// Strict XML mode
5708			if (t.settings.strict)
5709				n = n.toLowerCase();
5710
5711			return this.run(e, function(e) {
5712				var s = t.settings;
5713				var originalValue = e.getAttribute(n);
5714				if (v !== null) {
5715                                    
5716					switch (n) {
5717						case "style":
5718							if (!is(v, 'string')) {
5719								each(v, function(v, n) {
5720									t.setStyle(e, n, v);
5721								});
5722
5723								return;
5724							}
5725
5726							// No mce_style for elements with these since they might get resized by the user
5727							if (s.keep_values) {
5728								if (v && !t._isRes(v))
5729									e.setAttribute('data-mce-style', v, 2);
5730								else
5731									e.removeAttribute('data-mce-style', 2);
5732							}
5733
5734							e.style.cssText = v;
5735							break;
5736
5737						case "class":
5738							e.className = v || ''; // Fix IE null bug
5739							break;
5740
5741						case "src":
5742						case "href":
5743							if (s.keep_values) {
5744								if (s.url_converter)
5745									v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
5746
5747								t.setAttrib(e, 'data-mce-' + n, v, 2);
5748							}
5749
5750							break;
5751
5752						case "shape":
5753							e.setAttribute('data-mce-style', v);
5754							break;
5755					}
5756				}
5757				if (is(v) && v !== null && v.length !== 0)
5758					e.setAttribute(n, '' + v, 2);
5759				else
5760					e.removeAttribute(n, 2);
5761
5762				// fire onChangeAttrib event for attributes that have changed
5763				if (tinyMCE.activeEditor && originalValue != v) {
5764					var ed = tinyMCE.activeEditor;
5765					ed.onSetAttrib.dispatch(ed, e, n, v);
5766				}
5767			});
5768		},
5769
5770		setAttribs : function(e, o) {
5771			var t = this;
5772
5773			return this.run(e, function(e) {
5774				each(o, function(v, n) {
5775					t.setAttrib(e, n, v);
5776				});
5777			});
5778		},
5779
5780		getAttrib : function(e, n, dv) {
5781			var v, t = this, undef;
5782
5783			e = t.get(e);
5784
5785			if (!e || e.nodeType !== 1)
5786				return dv === undef ? false : dv;
5787
5788			if (!is(dv))
5789				dv = '';
5790
5791			// Try the mce variant for these
5792			if (/^(src|href|style|coords|shape)$/.test(n)) {
5793				v = e.getAttribute("data-mce-" + n);
5794
5795				if (v)
5796					return v;
5797			}
5798
5799			if (isIE && t.props[n]) {
5800				v = e[t.props[n]];
5801				v = v && v.nodeValue ? v.nodeValue : v;
5802			}
5803
5804			if (!v)
5805				v = e.getAttribute(n, 2);
5806
5807			// Check boolean attribs
5808			if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {
5809				if (e[t.props[n]] === true && v === '')
5810					return n;
5811
5812				return v ? n : '';
5813			}
5814
5815			// Inner input elements will override attributes on form elements
5816			if (e.nodeName === "FORM" && e.getAttributeNode(n))
5817				return e.getAttributeNode(n).nodeValue;
5818
5819			if (n === 'style') {
5820				v = v || e.style.cssText;
5821
5822				if (v) {
5823					v = t.serializeStyle(t.parseStyle(v), e.nodeName);
5824
5825					if (t.settings.keep_values && !t._isRes(v))
5826						e.setAttribute('data-mce-style', v);
5827				}
5828			}
5829
5830			// Remove Apple and WebKit stuff
5831			if (isWebKit && n === "class" && v)
5832				v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
5833
5834			// Handle IE issues
5835			if (isIE) {
5836				switch (n) {
5837					case 'rowspan':
5838					case 'colspan':
5839						// IE returns 1 as default value
5840						if (v === 1)
5841							v = '';
5842
5843						break;
5844
5845					case 'size':
5846						// IE returns +0 as default value for size
5847						if (v === '+0' || v === 20 || v === 0)
5848							v = '';
5849
5850						break;
5851
5852					case 'width':
5853					case 'height':
5854					case 'vspace':
5855					case 'checked':
5856					case 'disabled':
5857					case 'readonly':
5858						if (v === 0)
5859							v = '';
5860
5861						break;
5862
5863					case 'hspace':
5864						// IE returns -1 as default value
5865						if (v === -1)
5866							v = '';
5867
5868						break;
5869
5870					case 'maxlength':
5871					case 'tabindex':
5872						// IE returns default value
5873						if (v === 32768 || v === 2147483647 || v === '32768')
5874							v = '';
5875
5876						break;
5877
5878					case 'multiple':
5879					case 'compact':
5880					case 'noshade':
5881					case 'nowrap':
5882						if (v === 65535)
5883							return n;
5884
5885						return dv;
5886
5887					case 'shape':
5888						v = v.toLowerCase();
5889						break;
5890
5891					default:
5892						// IE has odd anonymous function for event attributes
5893						if (n.indexOf('on') === 0 && v)
5894							v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);
5895				}
5896			}
5897
5898			return (v !== undef && v !== null && v !== '') ? '' + v : dv;
5899		},
5900
5901		getPos : function(n, ro) {
5902			var t = this, x = 0, y = 0, e, d = t.doc, r;
5903
5904			n = t.get(n);
5905			ro = ro || d.body;
5906
5907			if (n) {
5908				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
5909				if (n.getBoundingClientRect) {
5910					n = n.getBoundingClientRect();
5911					e = t.boxModel ? d.documentElement : d.body;
5912
5913					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
5914					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
5915					x = n.left + (d.documentElement.scrollLeft || d.body.scrollLeft) - e.clientTop;
5916					y = n.top + (d.documentElement.scrollTop || d.body.scrollTop) - e.clientLeft;
5917
5918					return {x : x, y : y};
5919				}
5920
5921				r = n;
5922				while (r && r != ro && r.nodeType) {
5923					x += r.offsetLeft || 0;
5924					y += r.offsetTop || 0;
5925					r = r.offsetParent;
5926				}
5927
5928				r = n.parentNode;
5929				while (r && r != ro && r.nodeType) {
5930					x -= r.scrollLeft || 0;
5931					y -= r.scrollTop || 0;
5932					r = r.parentNode;
5933				}
5934			}
5935
5936			return {x : x, y : y};
5937		},
5938
5939		parseStyle : function(st) {
5940			return this.styles.parse(st);
5941		},
5942
5943		serializeStyle : function(o, name) {
5944			return this.styles.serialize(o, name);
5945		},
5946
5947		addStyle: function(cssText) {
5948			var doc = this.doc, head;
5949
5950			// Create style element if needed
5951			styleElm = doc.getElementById('mceDefaultStyles');
5952			if (!styleElm) {
5953				styleElm = doc.createElement('style'),
5954				styleElm.id = 'mceDefaultStyles';
5955				styleElm.type = 'text/css';
5956
5957				head = doc.getElementsByTagName('head')[0];
5958				if (head.firstChild) {
5959					head.insertBefore(styleElm, head.firstChild);
5960				} else {
5961					head.appendChild(styleElm);
5962				}
5963			}
5964
5965			// Append style data to old or new style element
5966			if (styleElm.styleSheet) {
5967				styleElm.styleSheet.cssText += cssText;
5968			} else {
5969				styleElm.appendChild(doc.createTextNode(cssText));
5970			}
5971		},
5972
5973		loadCSS : function(u) {
5974			var t = this, d = t.doc, head;
5975
5976			if (!u)
5977				u = '';
5978
5979			head = d.getElementsByTagName('head')[0];
5980
5981			each(u.split(','), function(u) {
5982				var link;
5983
5984				if (t.files[u])
5985					return;
5986
5987				t.files[u] = true;
5988				link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});
5989
5990				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
5991				// This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading
5992				// It's ugly but it seems to work fine.
5993				if (isIE && !tinymce.isIE11 && d.documentMode && d.recalc) {
5994					link.onload = function() {
5995						if (d.recalc)
5996							d.recalc();
5997
5998						link.onload = null;
5999					};
6000				}
6001
6002				head.appendChild(link);
6003			});
6004		},
6005
6006		addClass : function(e, c) {
6007			return this.run(e, function(e) {
6008				var o;
6009
6010				if (!c)
6011					return 0;
6012
6013				if (this.hasClass(e, c))
6014					return e.className;
6015
6016				o = this.removeClass(e, c);
6017
6018				return e.className = (o != '' ? (o + ' ') : '') + c;
6019			});
6020		},
6021
6022		removeClass : function(e, c) {
6023			var t = this, re;
6024
6025			return t.run(e, function(e) {
6026				var v;
6027
6028				if (t.hasClass(e, c)) {
6029					if (!re)
6030						re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
6031
6032					v = e.className.replace(re, ' ');
6033					v = tinymce.trim(v != ' ' ? v : '');
6034
6035					e.className = v;
6036
6037					// Empty class attr
6038					if (!v) {
6039						e.removeAttribute('class');
6040						e.removeAttribute('className');
6041					}
6042
6043					return v;
6044				}
6045
6046				return e.className;
6047			});
6048		},
6049
6050		hasClass : function(n, c) {
6051			n = this.get(n);
6052
6053			if (!n || !c)
6054				return false;
6055
6056			return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
6057		},
6058
6059		show : function(e) {
6060			return this.setStyle(e, 'display', 'block');
6061		},
6062
6063		hide : function(e) {
6064			return this.setStyle(e, 'display', 'none');
6065		},
6066
6067		isHidden : function(e) {
6068			e = this.get(e);
6069
6070			return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
6071		},
6072
6073		uniqueId : function(p) {
6074			return (!p ? 'mce_' : p) + (this.counter++);
6075		},
6076
6077		setHTML : function(element, html) {
6078			var self = this;
6079
6080			return self.run(element, function(element) {
6081				if (isIE) {
6082					// Remove all child nodes, IE keeps empty text nodes in DOM
6083					while (element.firstChild)
6084						element.removeChild(element.firstChild);
6085
6086					try {
6087						// IE will remove comments from the beginning
6088						// unless you padd the contents with something
6089						element.innerHTML = '<br />' + html;
6090						element.removeChild(element.firstChild);
6091					} catch (ex) {
6092						// IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p
6093						// This seems to fix this problem
6094
6095						// Create new div with HTML contents and a BR infront to keep comments
6096						var newElement = self.create('div');
6097						newElement.innerHTML = '<br />' + html;
6098
6099						// Add all children from div to target
6100						each (tinymce.grep(newElement.childNodes), function(node, i) {
6101							// Skip br element
6102							if (i && element.canHaveHTML)
6103								element.appendChild(node);
6104						});
6105					}
6106				} else
6107					element.innerHTML = html;
6108
6109				return html;
6110			});
6111		},
6112
6113		getOuterHTML : function(elm) {
6114			var doc, self = this;
6115
6116			elm = self.get(elm);
6117
6118			if (!elm)
6119				return null;
6120
6121			if (elm.nodeType === 1 && self.hasOuterHTML)
6122				return elm.outerHTML;
6123
6124			doc = (elm.ownerDocument || self.doc).createElement("body");
6125			doc.appendChild(elm.cloneNode(true));
6126
6127			return doc.innerHTML;
6128		},
6129
6130		setOuterHTML : function(e, h, d) {
6131			var t = this;
6132
6133			function setHTML(e, h, d) {
6134				var n, tp;
6135
6136				tp = d.createElement("body");
6137				tp.innerHTML = h;
6138
6139				n = tp.lastChild;
6140				while (n) {
6141					t.insertAfter(n.cloneNode(true), e);
6142					n = n.previousSibling;
6143				}
6144
6145				t.remove(e);
6146			};
6147
6148			return this.run(e, function(e) {
6149				e = t.get(e);
6150
6151				// Only set HTML on elements
6152				if (e.nodeType == 1) {
6153					d = d || e.ownerDocument || t.doc;
6154
6155					if (isIE) {
6156						try {
6157							// Try outerHTML for IE it sometimes produces an unknown runtime error
6158							if (isIE && e.nodeType == 1)
6159								e.outerHTML = h;
6160							else
6161								setHTML(e, h, d);
6162						} catch (ex) {
6163							// Fix for unknown runtime error
6164							setHTML(e, h, d);
6165						}
6166					} else
6167						setHTML(e, h, d);
6168				}
6169			});
6170		},
6171
6172		decode : Entities.decode,
6173
6174		encode : Entities.encodeAllRaw,
6175
6176		insertAfter : function(node, reference_node) {
6177			reference_node = this.get(reference_node);
6178
6179			return this.run(node, function(node) {
6180				var parent, nextSibling;
6181
6182				parent = reference_node.parentNode;
6183				nextSibling = reference_node.nextSibling;
6184
6185				if (nextSibling)
6186					parent.insertBefore(node, nextSibling);
6187				else
6188					parent.appendChild(node);
6189
6190				return node;
6191			});
6192		},
6193
6194		replace : function(n, o, k) {
6195			var t = this;
6196
6197			if (is(o, 'array'))
6198				n = n.cloneNode(true);
6199
6200			return t.run(o, function(o) {
6201				if (k) {
6202					each(tinymce.grep(o.childNodes), function(c) {
6203						n.appendChild(c);
6204					});
6205				}
6206
6207				return o.parentNode.replaceChild(n, o);
6208			});
6209		},
6210
6211		rename : function(elm, name) {
6212			var t = this, newElm;
6213
6214			if (elm.nodeName != name.toUpperCase()) {
6215				// Rename block element
6216				newElm = t.create(name);
6217
6218				// Copy attribs to new block
6219				each(t.getAttribs(elm), function(attr_node) {
6220					t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));
6221				});
6222
6223				// Replace block
6224				t.replace(newElm, elm, 1);
6225			}
6226
6227			return newElm || elm;
6228		},
6229
6230		findCommonAncestor : function(a, b) {
6231			var ps = a, pe;
6232
6233			while (ps) {
6234				pe = b;
6235
6236				while (pe && ps != pe)
6237					pe = pe.parentNode;
6238
6239				if (ps == pe)
6240					break;
6241
6242				ps = ps.parentNode;
6243			}
6244
6245			if (!ps && a.ownerDocument)
6246				return a.ownerDocument.documentElement;
6247
6248			return ps;
6249		},
6250
6251		toHex : function(s) {
6252			var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
6253
6254			function hex(s) {
6255				s = parseInt(s, 10).toString(16);
6256
6257				return s.length > 1 ? s : '0' + s; // 0 -> 00
6258			};
6259
6260			if (c) {
6261				s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
6262
6263				return s;
6264			}
6265
6266			return s;
6267		},
6268
6269		getClasses : function() {
6270			var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
6271
6272			if (t.classes)
6273				return t.classes;
6274
6275			function addClasses(s) {
6276				// IE style imports
6277				each(s.imports, function(r) {
6278					addClasses(r);
6279				});
6280
6281				each(s.cssRules || s.rules, function(r) {
6282					// Real type or fake it on IE
6283					switch (r.type || 1) {
6284						// Rule
6285						case 1:
6286							if (r.selectorText) {
6287								each(r.selectorText.split(','), function(v) {
6288									v = v.replace(/^\s*|\s*$|^\s\./g, "");
6289
6290									// Is internal or it doesn't contain a class
6291									if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
6292										return;
6293
6294									// Remove everything but class name
6295									ov = v;
6296									v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);
6297
6298									// Filter classes
6299									if (f && !(v = f(v, ov)))
6300										return;
6301
6302									if (!lo[v]) {
6303										cl.push({'class' : v});
6304										lo[v] = 1;
6305									}
6306								});
6307							}
6308							break;
6309
6310						// Import
6311						case 3:
6312							try {
6313								addClasses(r.styleSheet);
6314							} catch (ex) {
6315								// Ignore
6316							}
6317
6318							break;
6319					}
6320				});
6321			};
6322
6323			try {
6324				each(t.doc.styleSheets, addClasses);
6325			} catch (ex) {
6326				// Ignore
6327			}
6328
6329			if (cl.length > 0)
6330				t.classes = cl;
6331
6332			return cl;
6333		},
6334
6335		run : function(e, f, s) {
6336			var t = this, o;
6337
6338			if (t.doc && typeof(e) === 'string')
6339				e = t.get(e);
6340
6341			if (!e)
6342				return false;
6343
6344			s = s || this;
6345			if (!e.nodeType && (e.length || e.length === 0)) {
6346				o = [];
6347
6348				each(e, function(e, i) {
6349					if (e) {
6350						if (typeof(e) == 'string')
6351							e = t.doc.getElementById(e);
6352
6353						o.push(f.call(s, e, i));
6354					}
6355				});
6356
6357				return o;
6358			}
6359
6360			return f.call(s, e);
6361		},
6362
6363		getAttribs : function(n) {
6364			var o;
6365
6366			n = this.get(n);
6367
6368			if (!n)
6369				return [];
6370
6371			if (isIE) {
6372				o = [];
6373
6374				// Object will throw exception in IE
6375				if (n.nodeName == 'OBJECT')
6376					return n.attributes;
6377
6378				// IE doesn't keep the selected attribute if you clone option elements
6379				if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))
6380					o.push({specified : 1, nodeName : 'selected'});
6381
6382				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
6383				n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {
6384					o.push({specified : 1, nodeName : a});
6385				});
6386
6387				return o;
6388			}
6389
6390			return n.attributes;
6391		},
6392
6393		isEmpty : function(node, elements) {
6394			var self = this, i, attributes, type, walker, name, brCount = 0;
6395
6396			node = node.firstChild;
6397			if (node) {
6398				walker = new tinymce.dom.TreeWalker(node, node.parentNode);
6399				elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
6400
6401				do {
6402					type = node.nodeType;
6403
6404					if (type === 1) {
6405						// Ignore bogus elements
6406						if (node.getAttribute('data-mce-bogus'))
6407							continue;
6408
6409						// Keep empty elements like <img />
6410						name = node.nodeName.toLowerCase();
6411						if (elements && elements[name]) {
6412							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
6413							if (name === 'br') {
6414								brCount++;
6415								continue;
6416							}
6417
6418							return false;
6419						}
6420
6421						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
6422						attributes = self.getAttribs(node);
6423						i = node.attributes.length;
6424						while (i--) {
6425							name = node.attributes[i].nodeName;
6426							if (name === "name" || name === 'data-mce-bookmark')
6427								return false;
6428						}
6429					}
6430
6431					// Keep comment nodes
6432					if (type == 8)
6433						return false;
6434
6435					// Keep non whitespace text nodes
6436					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))
6437						return false;
6438				} while (node = walker.next());
6439			}
6440
6441			return brCount <= 1;
6442		},
6443
6444		destroy : function(s) {
6445			var t = this;
6446
6447			t.win = t.doc = t.root = t.events = t.frag = null;
6448
6449			// Manual destroy then remove unload handler
6450			if (!s)
6451				tinymce.removeUnload(t.destroy);
6452		},
6453
6454		createRng : function() {
6455			var d = this.doc;
6456
6457			return d.createRange ? d.createRange() : new tinymce.dom.Range(this);
6458		},
6459
6460		nodeIndex : function(node, normalized) {
6461			var idx = 0, lastNodeType, lastNode, nodeType;
6462
6463			if (node) {
6464				for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
6465					nodeType = node.nodeType;
6466
6467					// Normalize text nodes
6468					if (normalized && nodeType == 3) {
6469						if (nodeType == lastNodeType || !node.nodeValue.length)
6470							continue;
6471					}
6472					idx++;
6473					lastNodeType = nodeType;
6474				}
6475			}
6476
6477			return idx;
6478		},
6479
6480		split : function(pe, e, re) {
6481			var t = this, r = t.createRng(), bef, aft, pa;
6482
6483			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense
6484			// but we don't want that in our code since it serves no purpose for the end user
6485			// For example if this is chopped:
6486			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
6487			// would produce:
6488			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
6489			// this function will then trim of empty edges and produce:
6490			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
6491			function trim(node) {
6492				var i, children = node.childNodes, type = node.nodeType;
6493
6494				function surroundedBySpans(node) {
6495					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
6496					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
6497					return previousIsSpan && nextIsSpan;
6498				}
6499
6500				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')
6501					return;
6502
6503				for (i = children.length - 1; i >= 0; i--)
6504					trim(children[i]);
6505
6506				if (type != 9) {
6507					// Keep non whitespace text nodes
6508					if (type == 3 && node.nodeValue.length > 0) {
6509						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
6510						// Also keep text nodes with only spaces if surrounded by spans.
6511						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
6512						var trimmedLength = tinymce.trim(node.nodeValue).length;
6513						if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node))
6514							return;
6515					} else if (type == 1) {
6516						// If the only child is a bookmark then move it up
6517						children = node.childNodes;
6518						if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('data-mce-type') == 'bookmark')
6519							node.parentNode.insertBefore(children[0], node);
6520
6521						// Keep non empty elements or img, hr etc
6522						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))
6523							return;
6524					}
6525
6526					t.remove(node);
6527				}
6528
6529				return node;
6530			};
6531
6532			if (pe && e) {
6533				// Get before chunk
6534				r.setStart(pe.parentNode, t.nodeIndex(pe));
6535				r.setEnd(e.parentNode, t.nodeIndex(e));
6536				bef = r.extractContents();
6537
6538				// Get after chunk
6539				r = t.createRng();
6540				r.setStart(e.parentNode, t.nodeIndex(e) + 1);
6541				r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);
6542				aft = r.extractContents();
6543
6544				// Insert before chunk
6545				pa = pe.parentNode;
6546				pa.insertBefore(trim(bef), pe);
6547
6548				// Insert middle chunk
6549				if (re)
6550				pa.replaceChild(re, e);
6551			else
6552				pa.insertBefore(e, pe);
6553
6554				// Insert after chunk
6555				pa.insertBefore(trim(aft), pe);
6556				t.remove(pe);
6557
6558				return re || e;
6559			}
6560		},
6561
6562		bind : function(target, name, func, scope) {
6563			return this.events.add(target, name, func, scope || this);
6564		},
6565
6566		unbind : function(target, name, func) {
6567			return this.events.remove(target, name, func);
6568		},
6569
6570		fire : function(target, name, evt) {
6571			return this.events.fire(target, name, evt);
6572		},
6573
6574		// Returns the content editable state of a node
6575		getContentEditable: function(node) {
6576			var contentEditable;
6577
6578			// Check type
6579			if (node.nodeType != 1) {
6580				return null;
6581			}
6582
6583			// Check for fake content editable
6584			contentEditable = node.getAttribute("data-mce-contenteditable");
6585			if (contentEditable && contentEditable !== "inherit") {
6586				return contentEditable;
6587			}
6588
6589			// Check for real content editable
6590			return node.contentEditable !== "inherit" ? node.contentEditable : null;
6591		},
6592
6593
6594		_findSib : function(node, selector, name) {
6595			var t = this, f = selector;
6596
6597			if (node) {
6598				// If expression make a function of it using is
6599				if (is(f, 'string')) {
6600					f = function(node) {
6601						return t.is(node, selector);
6602					};
6603				}
6604
6605				// Loop all siblings
6606				for (node = node[name]; node; node = node[name]) {
6607					if (f(node))
6608						return node;
6609				}
6610			}
6611
6612			return null;
6613		},
6614
6615		_isRes : function(c) {
6616			// Is live resizble element
6617			return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
6618		}
6619
6620		/*
6621		walk : function(n, f, s) {
6622			var d = this.doc, w;
6623
6624			if (d.createTreeWalker) {
6625				w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
6626
6627				while ((n = w.nextNode()) != null)
6628					f.call(s || this, n);
6629			} else
6630				tinymce.walk(n, f, 'childNodes', s);
6631		}
6632		*/
6633
6634		/*
6635		toRGB : function(s) {
6636			var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
6637
6638			if (c) {
6639				// #FFF -> #FFFFFF
6640				if (!is(c[3]))
6641					c[3] = c[2] = c[1];
6642
6643				return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
6644			}
6645
6646			return s;
6647		}
6648		*/
6649	});
6650
6651	tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
6652})(tinymce);
6653
6654(function(ns) {
6655	// Range constructor
6656	function Range(dom) {
6657		var t = this,
6658			doc = dom.doc,
6659			EXTRACT = 0,
6660			CLONE = 1,
6661			DELETE = 2,
6662			TRUE = true,
6663			FALSE = false,
6664			START_OFFSET = 'startOffset',
6665			START_CONTAINER = 'startContainer',
6666			END_CONTAINER = 'endContainer',
6667			END_OFFSET = 'endOffset',
6668			extend = tinymce.extend,
6669			nodeIndex = dom.nodeIndex;
6670
6671		extend(t, {
6672			// Inital states
6673			startContainer : doc,
6674			startOffset : 0,
6675			endContainer : doc,
6676			endOffset : 0,
6677			collapsed : TRUE,
6678			commonAncestorContainer : doc,
6679
6680			// Range constants
6681			START_TO_START : 0,
6682			START_TO_END : 1,
6683			END_TO_END : 2,
6684			END_TO_START : 3,
6685
6686			// Public methods
6687			setStart : setStart,
6688			setEnd : setEnd,
6689			setStartBefore : setStartBefore,
6690			setStartAfter : setStartAfter,
6691			setEndBefore : setEndBefore,
6692			setEndAfter : setEndAfter,
6693			collapse : collapse,
6694			selectNode : selectNode,
6695			selectNodeContents : selectNodeContents,
6696			compareBoundaryPoints : compareBoundaryPoints,
6697			deleteContents : deleteContents,
6698			extractContents : extractContents,
6699			cloneContents : cloneContents,
6700			insertNode : insertNode,
6701			surroundContents : surroundContents,
6702			cloneRange : cloneRange,
6703			toStringIE : toStringIE
6704		});
6705
6706		function createDocumentFragment() {
6707			return doc.createDocumentFragment();
6708		};
6709
6710		function setStart(n, o) {
6711			_setEndPoint(TRUE, n, o);
6712		};
6713
6714		function setEnd(n, o) {
6715			_setEndPoint(FALSE, n, o);
6716		};
6717
6718		function setStartBefore(n) {
6719			setStart(n.parentNode, nodeIndex(n));
6720		};
6721
6722		function setStartAfter(n) {
6723			setStart(n.parentNode, nodeIndex(n) + 1);
6724		};
6725
6726		function setEndBefore(n) {
6727			setEnd(n.parentNode, nodeIndex(n));
6728		};
6729
6730		function setEndAfter(n) {
6731			setEnd(n.parentNode, nodeIndex(n) + 1);
6732		};
6733
6734		function collapse(ts) {
6735			if (ts) {
6736				t[END_CONTAINER] = t[START_CONTAINER];
6737				t[END_OFFSET] = t[START_OFFSET];
6738			} else {
6739				t[START_CONTAINER] = t[END_CONTAINER];
6740				t[START_OFFSET] = t[END_OFFSET];
6741			}
6742
6743			t.collapsed = TRUE;
6744		};
6745
6746		function selectNode(n) {
6747			setStartBefore(n);
6748			setEndAfter(n);
6749		};
6750
6751		function selectNodeContents(n) {
6752			setStart(n, 0);
6753			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
6754		};
6755
6756		function compareBoundaryPoints(h, r) {
6757			var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],
6758			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
6759
6760			// Check START_TO_START
6761			if (h === 0)
6762				return _compareBoundaryPoints(sc, so, rsc, rso);
6763	
6764			// Check START_TO_END
6765			if (h === 1)
6766				return _compareBoundaryPoints(ec, eo, rsc, rso);
6767	
6768			// Check END_TO_END
6769			if (h === 2)
6770				return _compareBoundaryPoints(ec, eo, rec, reo);
6771	
6772			// Check END_TO_START
6773			if (h === 3) 
6774				return _compareBoundaryPoints(sc, so, rec, reo);
6775		};
6776
6777		function deleteContents() {
6778			_traverse(DELETE);
6779		};
6780
6781		function extractContents() {
6782			return _traverse(EXTRACT);
6783		};
6784
6785		function cloneContents() {
6786			return _traverse(CLONE);
6787		};
6788
6789		function insertNode(n) {
6790			var startContainer = this[START_CONTAINER],
6791				startOffset = this[START_OFFSET], nn, o;
6792
6793			// Node is TEXT_NODE or CDATA
6794			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
6795				if (!startOffset) {
6796					// At the start of text
6797					startContainer.parentNode.insertBefore(n, startContainer);
6798				} else if (startOffset >= startContainer.nodeValue.length) {
6799					// At the end of text
6800					dom.insertAfter(n, startContainer);
6801				} else {
6802					// Middle, need to split
6803					nn = startContainer.splitText(startOffset);
6804					startContainer.parentNode.insertBefore(n, nn);
6805				}
6806			} else {
6807				// Insert element node
6808				if (startContainer.childNodes.length > 0)
6809					o = startContainer.childNodes[startOffset];
6810
6811				if (o)
6812					startContainer.insertBefore(n, o);
6813				else
6814					startContainer.appendChild(n);
6815			}
6816		};
6817
6818		function surroundContents(n) {
6819			var f = t.extractContents();
6820
6821			t.insertNode(n);
6822			n.appendChild(f);
6823			t.selectNode(n);
6824		};
6825
6826		function cloneRange() {
6827			return extend(new Range(dom), {
6828				startContainer : t[START_CONTAINER],
6829				startOffset : t[START_OFFSET],
6830				endContainer : t[END_CONTAINER],
6831				endOffset : t[END_OFFSET],
6832				collapsed : t.collapsed,
6833				commonAncestorContainer : t.commonAncestorContainer
6834			});
6835		};
6836
6837		// Private methods
6838
6839		function _getSelectedNode(container, offset) {
6840			var child;
6841
6842			if (container.nodeType == 3 /* TEXT_NODE */)
6843				return container;
6844
6845			if (offset < 0)
6846				return container;
6847
6848			child = container.firstChild;
6849			while (child && offset > 0) {
6850				--offset;
6851				child = child.nextSibling;
6852			}
6853
6854			if (child)
6855				return child;
6856
6857			return container;
6858		};
6859
6860		function _isCollapsed() {
6861			return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);
6862		};
6863
6864		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
6865			var c, offsetC, n, cmnRoot, childA, childB;
6866			
6867			// In the first case the boundary-points have the same container. A is before B
6868			// if its offset is less than the offset of B, A is equal to B if its offset is
6869			// equal to the offset of B, and A is after B if its offset is greater than the
6870			// offset of B.
6871			if (containerA == containerB) {
6872				if (offsetA == offsetB)
6873					return 0; // equal
6874
6875				if (offsetA < offsetB)
6876					return -1; // before
6877
6878				return 1; // after
6879			}
6880
6881			// In the second case a child node C of the container of A is an ancestor
6882			// container of B. In this case, A is before B if the offset of A is less than or
6883			// equal to the index of the child node C and A is after B otherwise.
6884			c = containerB;
6885			while (c && c.parentNode != containerA)
6886				c = c.parentNode;
6887
6888			if (c) {
6889				offsetC = 0;
6890				n = containerA.firstChild;
6891
6892				while (n != c && offsetC < offsetA) {
6893					offsetC++;
6894					n = n.nextSibling;
6895				}
6896
6897				if (offsetA <= offsetC)
6898					return -1; // before
6899
6900				return 1; // after
6901			}
6902
6903			// In the third case a child node C of the container of B is an ancestor container
6904			// of A. In this case, A is before B if the index of the child node C is less than
6905			// the offset of B and A is after B otherwise.
6906			c = containerA;
6907			while (c && c.parentNode != containerB) {
6908				c = c.parentNode;
6909			}
6910
6911			if (c) {
6912				offsetC = 0;
6913				n = containerB.firstChild;
6914
6915				while (n != c && offsetC < offsetB) {
6916					offsetC++;
6917					n = n.nextSibling;
6918				}
6919
6920				if (offsetC < offsetB)
6921					return -1; // before
6922
6923				return 1; // after
6924			}
6925
6926			// In the fourth case, none of three other cases hold: the containers of A and B
6927			// are siblings or descendants of sibling nodes. In this case, A is before B if
6928			// the container of A is before the container of B in a pre-order traversal of the
6929			// Ranges' context tree and A is after B otherwise.
6930			cmnRoot = dom.findCommonAncestor(containerA, containerB);
6931			childA = containerA;
6932
6933			while (childA && childA.parentNode != cmnRoot)
6934				childA = childA.parentNode;
6935
6936			if (!childA)
6937				childA = cmnRoot;
6938
6939			childB = containerB;
6940			while (childB && childB.parentNode != cmnRoot)
6941				childB = childB.parentNode;
6942
6943			if (!childB)
6944				childB = cmnRoot;
6945
6946			if (childA == childB)
6947				return 0; // equal
6948
6949			n = cmnRoot.firstChild;
6950			while (n) {
6951				if (n == childA)
6952					return -1; // before
6953
6954				if (n == childB)
6955					return 1; // after
6956
6957				n = n.nextSibling;
6958			}
6959		};
6960
6961		function _setEndPoint(st, n, o) {
6962			var ec, sc;
6963
6964			if (st) {
6965				t[START_CONTAINER] = n;
6966				t[START_OFFSET] = o;
6967			} else {
6968				t[END_CONTAINER] = n;
6969				t[END_OFFSET] = o;
6970			}
6971
6972			// If one boundary-point of a Range is set to have a root container
6973			// other than the current one for the Range, the Range is collapsed to
6974			// the new position. This enforces the restriction that both boundary-
6975			// points of a Range must have the same root container.
6976			ec = t[END_CONTAINER];
6977			while (ec.parentNode)
6978				ec = ec.parentNode;
6979
6980			sc = t[START_CONTAINER];
6981			while (sc.parentNode)
6982				sc = sc.parentNode;
6983
6984			if (sc == ec) {
6985				// The start position of a Range is guaranteed to never be after the
6986				// end position. To enforce this restriction, if the start is set to
6987				// be at a position after the end, the Range is collapsed to that
6988				// position.
6989				if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)
6990					t.collapse(st);
6991			} else
6992				t.collapse(st);
6993
6994			t.collapsed = _isCollapsed();
6995			t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);
6996		};
6997
6998		function _traverse(how) {
6999			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
7000
7001			if (t[START_CONTAINER] == t[END_CONTAINER])
7002				return _traverseSameContainer(how);
7003
7004			for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
7005				if (p == t[START_CONTAINER])
7006					return _traverseCommonStartContainer(c, how);
7007
7008				++endContainerDepth;
7009			}
7010
7011			for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
7012				if (p == t[END_CONTAINER])
7013					return _traverseCommonEndContainer(c, how);
7014
7015				++startContainerDepth;
7016			}
7017
7018			depthDiff = startContainerDepth - endContainerDepth;
7019
7020			startNode = t[START_CONTAINER];
7021			while (depthDiff > 0) {
7022				startNode = startNode.parentNode;
7023				depthDiff--;
7024			}
7025
7026			endNode = t[END_CONTAINER];
7027			while (depthDiff < 0) {
7028				endNode = endNode.parentNode;
7029				depthDiff++;
7030			}
7031
7032			// ascend the ancestor hierarchy until we have a common parent.
7033			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
7034				startNode = sp;
7035				endNode = ep;
7036			}
7037
7038			return _traverseCommonAncestors(startNode, endNode, how);
7039		};
7040
7041		 function _traverseSameContainer(how) {
7042			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
7043
7044			if (how != DELETE)
7045				frag = createDocumentFragment();
7046
7047			// If selection is empty, just return the fragment
7048			if (t[START_OFFSET] == t[END_OFFSET])
7049				return frag;
7050
7051			// Text node needs special case handling
7052			if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
7053				// get the substring
7054				s = t[START_CONTAINER].nodeValue;
7055				sub = s.substring(t[START_OFFSET], t[END_OFFSET]);
7056
7057				// set the original text node to its new value
7058				if (how != CLONE) {
7059					n = t[START_CONTAINER];
7060					start = t[START_OFFSET];
7061					len = t[END_OFFSET] - t[START_OFFSET];
7062
7063					if (start === 0 && len >= n.nodeValue.length - 1) {
7064						n.parentNode.removeChild(n);
7065					} else {
7066						n.deleteData(start, len);
7067					}
7068
7069					// Nothing is partially selected, so collapse to start point
7070					t.collapse(TRUE);
7071				}
7072
7073				if (how == DELETE)
7074					return;
7075
7076				if (sub.length > 0) {
7077					frag.appendChild(doc.createTextNode(sub));
7078				}
7079
7080				return frag;
7081			}
7082
7083			// Copy nodes between the start/end offsets.
7084			n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);
7085			cnt = t[END_OFFSET] - t[START_OFFSET];
7086
7087			while (n && cnt > 0) {
7088				sibling = n.nextSibling;
7089				xferNode = _traverseFullySelected(n, how);
7090
7091				if (frag)
7092					frag.appendChild( xferNode );
7093
7094				--cnt;
7095				n = sibling;
7096			}
7097
7098			// Nothing is partially selected, so collapse to start point
7099			if (how != CLONE)
7100				t.collapse(TRUE);
7101
7102			return frag;
7103		};
7104
7105		function _traverseCommonStartContainer(endAncestor, how) {
7106			var frag, n, endIdx, cnt, sibling, xferNode;
7107
7108			if (how != DELETE)
7109				frag = createDocumentFragment();
7110
7111			n = _traverseRightBoundary(endAncestor, how);
7112
7113			if (frag)
7114				frag.appendChild(n);
7115
7116			endIdx = nodeIndex(endAncestor);
7117			cnt = endIdx - t[START_OFFSET];
7118
7119			if (cnt <= 0) {
7120				// Collapse to just before the endAncestor, which
7121				// is partially selected.
7122				if (how != CLONE) {
7123					t.setEndBefore(endAncestor);
7124					t.collapse(FALSE);
7125				}
7126
7127				return frag;
7128			}
7129
7130			n = endAncestor.previousSibling;
7131			while (cnt > 0) {
7132				sibling = n.previousSibling;
7133				xferNode = _traverseFullySelected(n, how);
7134
7135				if (frag)
7136					frag.insertBefore(xferNode, frag.firstChild);
7137
7138				--cnt;
7139				n = sibling;
7140			}
7141
7142			// Collapse to just before the endAncestor, which
7143			// is partially selected.
7144			if (how != CLONE) {
7145				t.setEndBefore(endAncestor);
7146				t.collapse(FALSE);
7147			}
7148
7149			return frag;
7150		};
7151
7152		function _traverseCommonEndContainer(startAncestor, how) {
7153			var frag, startIdx, n, cnt, sibling, xferNode;
7154
7155			if (how != DELETE)
7156				frag = createDocumentFragment();
7157
7158			n = _traverseLeftBoundary(startAncestor, how);
7159			if (frag)
7160				frag.appendChild(n);
7161
7162			startIdx = nodeIndex(startAncestor);
7163			++startIdx; // Because we already traversed it
7164
7165			cnt = t[END_OFFSET] - startIdx;
7166			n = startAncestor.nextSibling;
7167			while (n && cnt > 0) {
7168				sibling = n.nextSibling;
7169				xferNode = _traverseFullySelected(n, how);
7170
7171				if (frag)
7172					frag.appendChild(xferNode);
7173
7174				--cnt;
7175				n = sibling;
7176			}
7177
7178			if (how != CLONE) {
7179				t.setStartAfter(startAncestor);
7180				t.collapse(TRUE);
7181			}
7182
7183			return frag;
7184		};
7185
7186		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
7187			var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
7188
7189			if (how != DELETE)
7190				frag = createDocumentFragment();
7191
7192			n = _traverseLeftBoundary(startAncestor, how);
7193			if (frag)
7194				frag.appendChild(n);
7195
7196			commonParent = startAncestor.parentNode;
7197			startOffset = nodeIndex(startAncestor);
7198			endOffset = nodeIndex(endAncestor);
7199			++startOffset;
7200
7201			cnt = endOffset - startOffset;
7202			sibling = startAncestor.nextSibling;
7203
7204			while (cnt > 0) {
7205				nextSibling = sibling.nextSibling;
7206				n = _traverseFullySelected(sibling, how);
7207
7208				if (frag)
7209					frag.appendChild(n);
7210
7211				sibling = nextSibling;
7212				--cnt;
7213			}
7214
7215			n = _traverseRightBoundary(endAncestor, how);
7216
7217			if (frag)
7218				frag.appendChild(n);
7219
7220			if (how != CLONE) {
7221				t.setStartAfter(startAncestor);
7222				t.collapse(TRUE);
7223			}
7224
7225			return frag;
7226		};
7227
7228		function _traverseRightBoundary(root, how) {
7229			var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];
7230
7231			if (next == root)
7232				return _traverseNode(next, isFullySelected, FALSE, how);
7233
7234			parent = next.parentNode;
7235			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
7236
7237			while (parent) {
7238				while (next) {
7239					prevSibling = next.previousSibling;
7240					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
7241
7242					if (how != DELETE)
7243						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
7244
7245					isFullySelected = TRUE;
7246					next = prevSibling;
7247				}
7248
7249				if (parent == root)
7250					return clonedParent;
7251
7252				next = parent.previousSibling;
7253				parent = parent.parentNode;
7254
7255				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
7256
7257				if (how != DELETE)
7258					clonedGrandParent.appendChild(clonedParent);
7259
7260				clonedParent = clonedGrandParent;
7261			}
7262		};
7263
7264		function _traverseLeftBoundary(root, how) {
7265			var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
7266
7267			if (next == root)
7268				return _traverseNode(next, isFullySelected, TRUE, how);
7269
7270			parent = next.parentNode;
7271			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
7272
7273			while (parent) {
7274				while (next) {
7275					nextSibling = next.nextSibling;
7276					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
7277
7278					if (how != DELETE)
7279						clonedParent.appendChild(clonedChild);
7280
7281					isFullySelected = TRUE;
7282					next = nextSibling;
7283				}
7284
7285				if (parent == root)
7286					return clonedParent;
7287
7288				next = parent.nextSibling;
7289				parent = parent.parentNode;
7290
7291				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
7292
7293				if (how != DELETE)
7294					clonedGrandParent.appendChild(clonedParent);
7295
7296				clonedParent = clonedGrandParent;
7297			}
7298		};
7299
7300		function _traverseNode(n, isFullySelected, isLeft, how) {
7301			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
7302
7303			if (isFullySelected)
7304				return _traverseFullySelected(n, how);
7305
7306			if (n.nodeType == 3 /* TEXT_NODE */) {
7307				txtValue = n.nodeValue;
7308
7309				if (isLeft) {
7310					offset = t[START_OFFSET];
7311					newNodeValue = txtValue.substring(offset);
7312					oldNodeValue = txtValue.substring(0, offset);
7313				} else {
7314					offset = t[END_OFFSET];
7315					newNodeValue = txtValue.substring(0, offset);
7316					oldNodeValue = txtValue.substring(offset);
7317				}
7318
7319				if (how != CLONE)
7320					n.nodeValue = oldNodeValue;
7321
7322				if (how == DELETE)
7323					return;
7324
7325				newNode = dom.clone(n, FALSE);
7326				newNode.nodeValue = newNodeValue;
7327
7328				return newNode;
7329			}
7330
7331			if (how == DELETE)
7332				return;
7333
7334			return dom.clone(n, FALSE);
7335		};
7336
7337		function _traverseFullySelected(n, how) {
7338			if (how != DELETE)
7339				return how == CLONE ? dom.clone(n, TRUE) : n;
7340
7341			n.parentNode.removeChild(n);
7342		};
7343
7344		function toStringIE() {
7345			return dom.create('body', null, cloneContents()).outerText;
7346		}
7347		
7348		return t;
7349	};
7350
7351	ns.Range = Range;
7352
7353	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
7354	Range.prototype.toString = function() {
7355		return this.toStringIE();
7356	};
7357})(tinymce.dom);
7358
7359(function() {
7360	function Selection(selection) {
7361		var self = this, dom = selection.dom, TRUE = true, FALSE = false;
7362
7363		function getPosition(rng, start) {
7364			var checkRng, startIndex = 0, endIndex, inside,
7365				children, child, offset, index, position = -1, parent;
7366
7367			// Setup test range, collapse it and get the parent
7368			checkRng = rng.duplicate();
7369			checkRng.collapse(start);
7370			parent = checkRng.parentElement();
7371
7372			// Check if the selection is within the right document
7373			if (parent.ownerDocument !== selection.dom.doc)
7374				return;
7375
7376			// IE will report non editable elements as it's parent so look for an editable one
7377			while (parent.contentEditable === "false") {
7378				parent = parent.parentNode;
7379			}
7380
7381			// If parent doesn't have any children then return that we are inside the element
7382			if (!parent.hasChildNodes()) {
7383				return {node : parent, inside : 1};
7384			}
7385
7386			// Setup node list and endIndex
7387			children = parent.children;
7388			endIndex = children.length - 1;
7389
7390			// Perform a binary search for the position
7391			while (startIndex <= endIndex) {
7392				index = Math.floor((startIndex + endIndex) / 2);
7393
7394				// Move selection to node and compare the ranges
7395				child = children[index];
7396				checkRng.moveToElementText(child);
7397				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
7398
7399				// Before/after or an exact match
7400				if (position > 0) {
7401					endIndex = index - 1;
7402				} else if (position < 0) {
7403					startIndex = index + 1;
7404				} else {
7405					return {node : child};
7406				}
7407			}
7408
7409			// Check if child position is before or we didn't find a position
7410			if (position < 0) {
7411				// No element child was found use the parent element and the offset inside that
7412				if (!child) {
7413					checkRng.moveToElementText(parent);
7414					checkRng.collapse(true);
7415					child = parent;
7416					inside = true;
7417				} else
7418					checkRng.collapse(false);
7419
7420				// Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one
7421				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
7422				offset = 0;
7423				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
7424					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
7425						break;
7426					}
7427
7428					offset++;
7429				}
7430			} else {
7431				// Child position is after the selection endpoint
7432				checkRng.collapse(true);
7433
7434				// Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one
7435				offset = 0;
7436				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
7437					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
7438						break;
7439					}
7440
7441					offset++;
7442				}
7443			}
7444
7445			return {node : child, position : position, offset : offset, inside : inside};
7446		};
7447
7448		// Returns a W3C DOM compatible range object by using the IE Range API
7449		function getRange() {
7450			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark, fail;
7451
7452			// If selection is outside the current document just return an empty range
7453			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
7454			if (element.ownerDocument != dom.doc)
7455				return domRange;
7456
7457			collapsed = selection.isCollapsed();
7458
7459			// Handle control selection
7460			if (ieRange.item) {
7461				domRange.setStart(element.parentNode, dom.nodeIndex(element));
7462				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
7463
7464				return domRange;
7465			}
7466
7467			function findEndPoint(start) {
7468				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
7469
7470				container = endPoint.node;
7471				offset = endPoint.offset;
7472
7473				if (endPoint.inside && !container.hasChildNodes()) {
7474					domRange[start ? 'setStart' : 'setEnd'](container, 0);
7475					return;
7476				}
7477
7478				if (offset === undef) {
7479					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
7480					return;
7481				}
7482
7483				if (endPoint.position < 0) {
7484					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
7485
7486					if (!sibling) {
7487						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
7488						return;
7489					}
7490
7491					if (!offset) {
7492						if (sibling.nodeType == 3)
7493							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
7494						else
7495							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
7496
7497						return;
7498					}
7499
7500					// Find the text node and offset
7501					while (sibling) {
7502						nodeValue = sibling.nodeValue;
7503						textNodeOffset += nodeValue.length;
7504
7505						// We are at or passed the position we where looking for
7506						if (textNodeOffset >= offset) {
7507							container = sibling;
7508							textNodeOffset -= offset;
7509							textNodeOffset = nodeValue.length - textNodeOffset;
7510							break;
7511						}
7512
7513						sibling = sibling.nextSibling;
7514					}
7515				} else {
7516					// Find the text node and offset
7517					sibling = container.previousSibling;
7518
7519					if (!sibling)
7520						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
7521
7522					// If there isn't any text to loop then use the first position
7523					if (!offset) {
7524						if (container.nodeType == 3)
7525							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
7526						else
7527							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
7528
7529						return;
7530					}
7531
7532					while (sibling) {
7533						textNodeOffset += sibling.nodeValue.length;
7534
7535						// We are at or passed the position we where looking for
7536						if (textNodeOffset >= offset) {
7537							container = sibling;
7538							textNodeOffset -= offset;
7539							break;
7540						}
7541
7542						sibling = sibling.previousSibling;
7543					}
7544				}
7545
7546				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
7547			};
7548
7549			try {
7550				// Find start point
7551				findEndPoint(true);
7552
7553				// Find end point if needed
7554				if (!collapsed)
7555					findEndPoint();
7556			} catch (ex) {
7557				// IE has a nasty bug where text nodes might throw "invalid argument" when you
7558				// access the nodeValue or other properties of text nodes. This seems to happend when
7559				// text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
7560				if (ex.number == -2147024809) {
7561					// Get the current selection
7562					bookmark = self.getBookmark(2);
7563
7564					// Get start element
7565					tmpRange = ieRange.duplicate();
7566					tmpRange.collapse(true);
7567					element = tmpRange.parentElement();
7568
7569					// Get end element
7570					if (!collapsed) {
7571						tmpRange = ieRange.duplicate();
7572						tmpRange.collapse(false);
7573						element2 = tmpRange.parentElement();
7574						element2.innerHTML = element2.innerHTML;
7575					}
7576
7577					// Remove the broken elements
7578					element.innerHTML = element.innerHTML;
7579
7580					// Restore the selection
7581					self.moveToBookmark(bookmark);
7582
7583					// Since the range has moved we need to re-get it
7584					ieRange = selection.getRng();
7585
7586					// Find start point
7587					findEndPoint(true);
7588
7589					// Find end point if needed
7590					if (!collapsed)
7591						findEndPoint();
7592				} else
7593					throw ex; // Throw other errors
7594			}
7595
7596			return domRange;
7597		};
7598
7599		this.getBookmark = function(type) {
7600			var rng = selection.getRng(), start, end, bookmark = {};
7601
7602			function getIndexes(node) {
7603				var parent, root, children, i, indexes = [];
7604
7605				parent = node.parentNode;
7606				root = dom.getRoot().parentNode;
7607
7608				while (parent != root && parent.nodeType !== 9) {
7609					children = parent.children;
7610
7611					i = children.length;
7612					while (i--) {
7613						if (node === children[i]) {
7614							indexes.push(i);
7615							break;
7616						}
7617					}
7618
7619					node = parent;
7620					parent = parent.parentNode;
7621				}
7622
7623				return indexes;
7624			};
7625
7626			function getBookmarkEndPoint(start) {
7627				var position;
7628
7629				position = getPosition(rng, start);
7630				if (position) {
7631					return {
7632						position : position.position,
7633						offset : position.offset,
7634						indexes : getIndexes(position.node),
7635						inside : position.inside
7636					};
7637				}
7638			};
7639
7640			// Non ubstructive bookmark
7641			if (type === 2) {
7642				// Handle text selection
7643				if (!rng.item) {
7644					bookmark.start = getBookmarkEndPoint(true);
7645
7646					if (!selection.isCollapsed())
7647						bookmark.end = getBookmarkEndPoint();
7648				} else
7649					bookmark.start = {ctrl : true, indexes : getIndexes(rng.item(0))};
7650			}
7651
7652			return bookmark;
7653		};
7654
7655		this.moveToBookmark = function(bookmark) {
7656			var rng, body = dom.doc.body;
7657
7658			function resolveIndexes(indexes) {
7659				var node, i, idx, children;
7660
7661				node = dom.getRoot();
7662				for (i = indexes.length - 1; i >= 0; i--) {
7663					children = node.children;
7664					idx = indexes[i];
7665
7666					if (idx <= children.length - 1) {
7667						node = children[idx];
7668					}
7669				}
7670
7671				return node;
7672			};
7673			
7674			function setBookmarkEndPoint(start) {
7675				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef;
7676
7677				if (endPoint) {
7678					moveLeft = endPoint.position > 0;
7679
7680					moveRng = body.createTextRange();
7681					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
7682
7683					offset = endPoint.offset;
7684					if (offset !== undef) {
7685						moveRng.collapse(endPoint.inside || moveLeft);
7686						moveRng.moveStart('character', moveLeft ? -offset : offset);
7687					} else
7688						moveRng.collapse(start);
7689
7690					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
7691
7692					if (start)
7693						rng.collapse(true);
7694				}
7695			};
7696
7697			if (bookmark.start) {
7698				if (bookmark.start.ctrl) {
7699					rng = body.createControlRange();
7700					rng.addElement(resolveIndexes(bookmark.start.indexes));
7701					rng.select();
7702				} else {
7703					rng = body.createTextRange();
7704					setBookmarkEndPoint(true);
7705					setBookmarkEndPoint();
7706					rng.select();
7707				}
7708			}
7709		};
7710
7711		this.addRange = function(rng) {
7712			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
7713				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
7714
7715			function setEndPoint(start) {
7716				var container, offset, marker, tmpRng, nodes;
7717
7718				marker = dom.create('a');
7719				container = start ? startContainer : endContainer;
7720				offset = start ? startOffset : endOffset;
7721				tmpRng = ieRng.duplicate();
7722
7723				if (container == doc || container == doc.documentElement) {
7724					container = body;
7725					offset = 0;
7726				}
7727
7728				if (container.nodeType == 3) {
7729					container.parentNode.insertBefore(marker, container);
7730					tmpRng.moveToElementText(marker);
7731					tmpRng.moveStart('character', offset);
7732					dom.remove(marker);
7733					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
7734				} else {
7735					nodes = container.childNodes;
7736
7737					if (nodes.length) {
7738						if (offset >= nodes.length) {
7739							dom.insertAfter(marker, nodes[nodes.length - 1]);
7740						} else {
7741							container.insertBefore(marker, nodes[offset]);
7742						}
7743
7744						tmpRng.moveToElementText(marker);
7745					} else if (container.canHaveHTML) {
7746						// Empty node selection for example <div>|</div>
7747						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
7748						container.innerHTML = '<span>\uFEFF</span>';
7749						marker = container.firstChild;
7750						tmpRng.moveToElementText(marker);
7751						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
7752					}
7753
7754					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
7755					dom.remove(marker);
7756				}
7757			}
7758
7759			// Setup some shorter versions
7760			startContainer = rng.startContainer;
7761			startOffset = rng.startOffset;
7762			endContainer = rng.endContainer;
7763			endOffset = rng.endOffset;
7764			ieRng = body.createTextRange();
7765
7766			// If single element selection then try making a control selection out of it
7767			if (startContainer == endContainer && startContainer.nodeType == 1) {
7768				// Trick to place the caret inside an empty block element like <p></p>
7769				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
7770					if (startContainer.canHaveHTML) {
7771						// Check if previous sibling is an empty block if it is then we need to render it
7772						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
7773						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
7774						sibling = startContainer.previousSibling;
7775						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
7776							sibling.innerHTML = '\uFEFF';
7777						} else {
7778							sibling = null;
7779						}
7780
7781						startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';
7782						ieRng.moveToElementText(startContainer.lastChild);
7783						ieRng.select();
7784						dom.doc.selection.clear();
7785						startContainer.innerHTML = '';
7786
7787						if (sibling) {
7788							sibling.innerHTML = '';
7789						}
7790						return;
7791					} else {
7792						startOffset = dom.nodeIndex(startContainer);
7793						startContainer = startContainer.parentNode;
7794					}
7795				}
7796
7797				if (startOffset == endOffset - 1) {
7798					try {
7799						ctrlElm = startContainer.childNodes[startOffset];
7800						ctrlRng = body.createControlRange();
7801						ctrlRng.addElement(ctrlElm);
7802						ctrlRng.select();
7803
7804						// Check if the range produced is on the correct element and is a control range
7805						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
7806						nativeRng = selection.getRng();
7807						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
7808							return;
7809						}
7810					} catch (ex) {
7811						// Ignore
7812					}
7813				}
7814			}
7815
7816			// Set start/end point of selection
7817			setEndPoint(true);
7818			setEndPoint();
7819
7820			// Select the new range and scroll it into view
7821			ieRng.select();
7822		};
7823
7824		// Expose range method
7825		this.getRangeAt = getRange;
7826	};
7827
7828	// Expose the selection object
7829	tinymce.dom.TridentSelection = Selection;
7830})();
7831
7832
7833/*
7834 * Sizzle CSS Selector Engine
7835 *  Copyright, The Dojo Foundation
7836 *  Released under the MIT, BSD, and GPL Licenses.
7837 *  More information: http://sizzlejs.com/
7838 */
7839(function(){
7840
7841var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
7842	expando = "sizcache",
7843	done = 0,
7844	toString = Object.prototype.toString,
7845	hasDuplicate = false,
7846	baseHasDuplicate = true,
7847	rBackslash = /\\/g,
7848	rReturn = /\r\n/g,
7849	rNonWord = /\W/;
7850
7851// Here we check if the JavaScript engine is using some sort of
7852// optimization where it does not always call our comparision
7853// function. If that is the case, discard the hasDuplicate value.
7854//   Thus far that includes Google Chrome.
7855[0, 0].sort(function() {
7856	baseHasDuplicate = false;
7857	return 0;
7858});
7859
7860var Sizzle = function( selector, context, results, seed ) {
7861	results = results || [];
7862	context = context || document;
7863
7864	var origContext = context;
7865
7866	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
7867		return [];
7868	}
7869
7870	if ( !selector || typeof selector !== "string" ) {
7871		return results;
7872	}
7873
7874	var m, set, checkSet, extra, ret, cur, pop, i,
7875		prune = true,
7876		contextXML = Sizzle.isXML( context ),
7877		parts = [],
7878		soFar = selector;
7879
7880	// Reset the position of the chunker regexp (start from head)
7881	do {
7882		chunker.exec( "" );
7883		m = chunker.exec( soFar );
7884
7885		if ( m ) {
7886			soFar = m[3];
7887
7888			parts.push( m[1] );
7889
7890			if ( m[2] ) {
7891				extra = m[3];
7892				break;
7893			}
7894		}
7895	} while ( m );
7896
7897	if ( parts.length > 1 && origPOS.exec( selector ) ) {
7898
7899		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
7900			set = posProcess( parts[0] + parts[1], context, seed );
7901
7902		} else {
7903			set = Expr.relative[ parts[0] ] ?
7904				[ context ] :
7905				Sizzle( parts.shift(), context );
7906
7907			while ( parts.length ) {
7908				selector = parts.shift();
7909
7910				if ( Expr.relative[ selector ] ) {
7911					selector += parts.shift();
7912				}
7913
7914				set = posProcess( selector, set, seed );
7915			}
7916		}
7917
7918	} else {
7919		// Take a shortcut and set the context if the root selector is an ID
7920		// (but not if it'll be faster if the inner selector is an ID)
7921		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
7922				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
7923
7924			ret = Sizzle.find( parts.shift(), context, contextXML );
7925			context = ret.expr ?
7926				Sizzle.filter( ret.expr, ret.set )[0] :
7927				ret.set[0];
7928		}
7929
7930		if ( context ) {
7931			ret = seed ?
7932				{ expr: parts.pop(), set: makeArray(seed) } :
7933				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
7934
7935			set = ret.expr ?
7936				Sizzle.filter( ret.expr, ret.set ) :
7937				ret.set;
7938
7939			if ( parts.length > 0 ) {
7940				checkSet = makeArray( set );
7941
7942			} else {
7943				prune = false;
7944			}
7945
7946			while ( parts.length ) {
7947				cur = parts.pop();
7948				pop = cur;
7949
7950				if ( !Expr.relative[ cur ] ) {
7951					cur = "";
7952				} else {
7953					pop = parts.pop();
7954				}
7955
7956				if ( pop == null ) {
7957					pop = context;
7958				}
7959
7960				Expr.relative[ cur ]( checkSet, pop, contextXML );
7961			}
7962
7963		} else {
7964			checkSet = parts = [];
7965		}
7966	}
7967
7968	if ( !checkSet ) {
7969		checkSet = set;
7970	}
7971
7972	if ( !checkSet ) {
7973		Sizzle.error( cur || selector );
7974	}
7975
7976	if ( toString.call(checkSet) === "[object Array]" ) {
7977		if ( !prune ) {
7978			results.push.apply( results, checkSet );
7979
7980		} else if ( context && context.nodeType === 1 ) {
7981			for ( i = 0; checkSet[i] != null; i++ ) {
7982				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
7983					results.push( set[i] );
7984				}
7985			}
7986
7987		} else {
7988			for ( i = 0; checkSet[i] != null; i++ ) {
7989				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
7990					results.push( set[i] );
7991				}
7992			}
7993		}
7994
7995	} else {
7996		makeArray( checkSet, results );
7997	}
7998
7999	if ( extra ) {
8000		Sizzle( extra, origContext, results, seed );
8001		Sizzle.uniqueSort( results );
8002	}
8003
8004	return results;
8005};
8006
8007Sizzle.uniqueSort = function( results ) {
8008	if ( sortOrder ) {
8009		hasDuplicate = baseHasDuplicate;
8010		results.sort( sortOrder );
8011
8012		if ( hasDuplicate ) {
8013			for ( var i = 1; i < results.length; i++ ) {
8014				if ( results[i] === results[ i - 1 ] ) {
8015					results.splice( i--, 1 );
8016				}
8017			}
8018		}
8019	}
8020
8021	return results;
8022};
8023
8024Sizzle.matches = function( expr, set ) {
8025	return Sizzle( expr, null, null, set );
8026};
8027
8028Sizzle.matchesSelector = function( node, expr ) {
8029	return Sizzle( expr, null, null, [node] ).length > 0;
8030};
8031
8032Sizzle.find = function( expr, context, isXML ) {
8033	var set, i, len, match, type, left;
8034
8035	if ( !expr ) {
8036		return [];
8037	}
8038
8039	for ( i = 0, len = Expr.order.length; i < len; i++ ) {
8040		type = Expr.order[i];
8041
8042		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
8043			left = match[1];
8044			match.splice( 1, 1 );
8045
8046			if ( left.substr( left.length - 1 ) !== "\\" ) {
8047				match[1] = (match[1] || "").replace( rBackslash, "" );
8048				set = Expr.find[ type ]( match, context, isXML );
8049
8050				if ( set != null ) {
8051					expr = expr.replace( Expr.match[ type ], "" );
8052					break;
8053				}
8054			}
8055		}
8056	}
8057
8058	if ( !set ) {
8059		set = typeof context.getElementsByTagName !== "undefined" ?
8060			context.getElementsByTagName( "*" ) :
8061			[];
8062	}
8063
8064	return { set: set, expr: expr };
8065};
8066
8067Sizzle.filter = function( expr, set, inplace, not ) {
8068	var match, anyFound,
8069		type, found, item, filter, left,
8070		i, pass,
8071		old = expr,
8072		result = [],
8073		curLoop = set,
8074		isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
8075
8076	while ( expr && set.length ) {
8077		for ( type in Expr.filter ) {
8078			if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
8079				filter = Expr.filter[ type ];
8080				left = match[1];
8081
8082				anyFound = false;
8083
8084				match.splice(1,1);
8085
8086				if ( left.substr( left.length - 1 ) === "\\" ) {
8087					continue;
8088				}
8089
8090				if ( curLoop === result ) {
8091					result = [];
8092				}
8093
8094				if ( Expr.preFilter[ type ] ) {
8095					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
8096
8097					if ( !match ) {
8098						anyFound = found = true;
8099
8100					} else if ( match === true ) {
8101						continue;
8102					}
8103				}
8104
8105				if ( match ) {
8106					for ( i = 0; (item = curLoop[i]) != null; i++ ) {
8107						if ( item ) {
8108							found = filter( item, match, i, curLoop );
8109							pass = not ^ found;
8110
8111							if ( inplace && found != null ) {
8112								if ( pass ) {
8113									anyFound = true;
8114
8115								} else {
8116									curLoop[i] = false;
8117								}
8118
8119							} else if ( pass ) {
8120								result.push( item );
8121								anyFound = true;
8122							}
8123						}
8124					}
8125				}
8126
8127				if ( found !== undefined ) {
8128					if ( !inplace ) {
8129						curLoop = result;
8130					}
8131
8132					expr = expr.replace( Expr.match[ type ], "" );
8133
8134					if ( !anyFound ) {
8135						return [];
8136					}
8137
8138					break;
8139				}
8140			}
8141		}
8142
8143		// Improper expression
8144		if ( expr === old ) {
8145			if ( anyFound == null ) {
8146				Sizzle.error( expr );
8147
8148			} else {
8149				break;
8150			}
8151		}
8152
8153		old = expr;
8154	}
8155
8156	return curLoop;
8157};
8158
8159Sizzle.error = function( msg ) {
8160	throw new Error( "Syntax error, unrecognized expression: " + msg );
8161};
8162
8163var getText = Sizzle.getText = function( elem ) {
8164    var i, node,
8165		nodeType = elem.nodeType,
8166		ret = "";
8167
8168	if ( nodeType ) {
8169		if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
8170			// Use textContent || innerText for elements
8171			if ( typeof elem.textContent === 'string' ) {
8172				return elem.textContent;
8173			} else if ( typeof elem.innerText === 'string' ) {
8174				// Replace IE's carriage returns
8175				return elem.innerText.replace( rReturn, '' );
8176			} else {
8177				// Traverse it's children
8178				for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
8179					ret += getText( elem );
8180				}
8181			}
8182		} else if ( nodeType === 3 || nodeType === 4 ) {
8183			return elem.nodeValue;
8184		}
8185	} else {
8186
8187		// If no nodeType, this is expected to be an array
8188		for ( i = 0; (node = elem[i]); i++ ) {
8189			// Do not traverse comment nodes
8190			if ( node.nodeType !== 8 ) {
8191				ret += getText( node );
8192			}
8193		}
8194	}
8195	return ret;
8196};
8197
8198var Expr = Sizzle.selectors = {
8199	order: [ "ID", "NAME", "TAG" ],
8200
8201	match: {
8202		ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
8203		CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
8204		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
8205		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
8206		TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
8207		CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
8208		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
8209		PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
8210	},
8211
8212	leftMatch: {},
8213
8214	attrMap: {
8215		"class": "className",
8216		"for": "htmlFor"
8217	},
8218
8219	attrHandle: {
8220		href: function( elem ) {
8221			return elem.getAttribute( "href" );
8222		},
8223		type: function( elem ) {
8224			return elem.getAttribute( "type" );
8225		}
8226	},
8227
8228	relative: {
8229		"+": function(checkSet, part){
8230			var isPartStr = typeof part === "string",
8231				isTag = isPartStr && !rNonWord.test( part ),
8232				isPartStrNotTag = isPartStr && !isTag;
8233
8234			if ( isTag ) {
8235				part = part.toLowerCase();
8236			}
8237
8238			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
8239				if ( (elem = checkSet[i]) ) {
8240					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
8241
8242					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
8243						elem || false :
8244						elem === part;
8245				}
8246			}
8247
8248			if ( isPartStrNotTag ) {
8249				Sizzle.filter( part, checkSet, true );
8250			}
8251		},
8252
8253		">": function( checkSet, part ) {
8254			var elem,
8255				isPartStr = typeof part === "string",
8256				i = 0,
8257				l = checkSet.length;
8258
8259			if ( isPartStr && !rNonWord.test( part ) ) {
8260				part = part.toLowerCase();
8261
8262				for ( ; i < l; i++ ) {
8263					elem = checkSet[i];
8264
8265					if ( elem ) {
8266						var parent = elem.parentNode;
8267						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
8268					}
8269				}
8270
8271			} else {
8272				for ( ; i < l; i++ ) {
8273					elem = checkSet[i];
8274
8275					if ( elem ) {
8276						checkSet[i] = isPartStr ?
8277							elem.parentNode :
8278							elem.parentNode === part;
8279					}
8280				}
8281
8282				if ( isPartStr ) {
8283					Sizzle.filter( part, checkSet, true );
8284				}
8285			}
8286		},
8287
8288		"": function(checkSet, part, isXML){
8289			var nodeCheck,
8290				doneName = done++,
8291				checkFn = dirCheck;
8292
8293			if ( typeof part === "string" && !rNonWord.test( part ) ) {
8294				part = part.toLowerCase();
8295				nodeCheck = part;
8296				checkFn = dirNodeCheck;
8297			}
8298
8299			checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
8300		},
8301
8302		"~": function( checkSet, part, isXML ) {
8303			var nodeCheck,
8304				doneName = done++,
8305				checkFn = dirCheck;
8306
8307			if ( typeof part === "string" && !rNonWord.test( part ) ) {
8308				part = part.toLowerCase();
8309				nodeCheck = part;
8310				checkFn = dirNodeCheck;
8311			}
8312
8313			checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
8314		}
8315	},
8316
8317	find: {
8318		ID: function( match, context, isXML ) {
8319			if ( typeof context.getElementById !== "undefined" && !isXML ) {
8320				var m = context.getElementById(match[1]);
8321				// Check parentNode to catch when Blackberry 4.6 returns
8322				// nodes that are no longer in the document #6963
8323				return m && m.parentNode ? [m] : [];
8324			}
8325		},
8326
8327		NAME: function( match, context ) {
8328			if ( typeof context.getElementsByName !== "undefined" ) {
8329				var ret = [],
8330					results = context.getElementsByName( match[1] );
8331
8332				for ( var i = 0, l = results.length; i < l; i++ ) {
8333					if ( results[i].getAttribute("name") === match[1] ) {
8334						ret.push( results[i] );
8335					}
8336				}
8337
8338				return ret.length === 0 ? null : ret;
8339			}
8340		},
8341
8342		TAG: function( match, context ) {
8343			if ( typeof context.getElementsByTagName !== "undefined" ) {
8344				return context.getElementsByTagName( match[1] );
8345			}
8346		}
8347	},
8348	preFilter: {
8349		CLASS: function( match, curLoop, inplace, result, not, isXML ) {
8350			match = " " + match[1].replace( rBackslash, "" ) + " ";
8351
8352			if ( isXML ) {
8353				return match;
8354			}
8355
8356			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
8357				if ( elem ) {
8358					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
8359						if ( !inplace ) {
8360							result.push( elem );
8361						}
8362
8363					} else if ( inplace ) {
8364						curLoop[i] = false;
8365					}
8366				}
8367			}
8368
8369			return false;
8370		},
8371
8372		ID: function( match ) {
8373			return match[1].replace( rBackslash, "" );
8374		},
8375
8376		TAG: function( match, curLoop ) {
8377			return match[1].replace( rBackslash, "" ).toLowerCase();
8378		},
8379
8380		CHILD: function( match ) {
8381			if ( match[1] === "nth" ) {
8382				if ( !match[2] ) {
8383					Sizzle.error( match[0] );
8384				}
8385
8386				match[2] = match[2].replace(/^\+|\s*/g, '');
8387
8388				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
8389				var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
8390					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
8391					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
8392
8393				// calculate the numbers (first)n+(last) including if they are negative
8394				match[2] = (test[1] + (test[2] || 1)) - 0;
8395				match[3] = test[3] - 0;
8396			}
8397			else if ( match[2] ) {
8398				Sizzle.error( match[0] );
8399			}
8400
8401			// TODO: Move to normal caching system
8402			match[0] = done++;
8403
8404			return match;
8405		},
8406
8407		ATTR: function( match, curLoop, inplace, result, not, isXML ) {
8408			var name = match[1] = match[1].replace( rBackslash, "" );
8409
8410			if ( !isXML && Expr.attrMap[name] ) {
8411				match[1] = Expr.attrMap[name];
8412			}
8413
8414			// Handle if an un-quoted value was used
8415			match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
8416
8417			if ( match[2] === "~=" ) {
8418				match[4] = " " + match[4] + " ";
8419			}
8420
8421			return match;
8422		},
8423
8424		PSEUDO: function( match, curLoop, inplace, result, not ) {
8425			if ( match[1] === "not" ) {
8426				// If we're dealing with a complex expression, or a simple one
8427				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
8428					match[3] = Sizzle(match[3], null, null, curLoop);
8429
8430				} else {
8431					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
8432
8433					if ( !inplace ) {
8434						result.push.apply( result, ret );
8435					}
8436
8437					return false;
8438				}
8439
8440			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
8441				return true;
8442			}
8443
8444			return match;
8445		},
8446
8447		POS: function( match ) {
8448			match.unshift( true );
8449
8450			return match;
8451		}
8452	},
8453
8454	filters: {
8455		enabled: function( elem ) {
8456			return elem.disabled === false && elem.type !== "hidden";
8457		},
8458
8459		disabled: function( elem ) {
8460			return elem.disabled === true;
8461		},
8462
8463		checked: function( elem ) {
8464			return elem.checked === true;
8465		},
8466
8467		selected: function( elem ) {
8468			// Accessing this property makes selected-by-default
8469			// options in Safari work properly
8470			if ( elem.parentNode ) {
8471				elem.parentNode.selectedIndex;
8472			}
8473
8474			return elem.selected === true;
8475		},
8476
8477		parent: function( elem ) {
8478			return !!elem.firstChild;
8479		},
8480
8481		empty: function( elem ) {
8482			return !elem.firstChild;
8483		},
8484
8485		has: function( elem, i, match ) {
8486			return !!Sizzle( match[3], elem ).length;
8487		},
8488
8489		header: function( elem ) {
8490			return (/h\d/i).test( elem.nodeName );
8491		},
8492
8493		text: function( elem ) {
8494			var attr = elem.getAttribute( "type" ), type = elem.type;
8495			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
8496			// use getAttribute instead to test this case
8497			return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
8498		},
8499
8500		radio: function( elem ) {
8501			return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
8502		},
8503
8504		checkbox: function( elem ) {
8505			return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
8506		},
8507
8508		file: function( elem ) {
8509			return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
8510		},
8511
8512		password: function( elem ) {
8513			return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
8514		},
8515
8516		submit: function( elem ) {
8517			var name = elem.nodeName.toLowerCase();
8518			return (name === "input" || name === "button") && "submit" === elem.type;
8519		},
8520
8521		image: function( elem ) {
8522			return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
8523		},
8524
8525		reset: function( elem ) {
8526			var name = elem.nodeName.toLowerCase();
8527			return (name === "input" || name === "button") && "reset" === elem.type;
8528		},
8529
8530		button: function( elem ) {
8531			var name = elem.nodeName.toLowerCase();
8532			return name === "input" && "button" === elem.type || name === "button";
8533		},
8534
8535		input: function( elem ) {
8536			return (/input|select|textarea|button/i).test( elem.nodeName );
8537		},
8538
8539		focus: function( elem ) {
8540			return elem === elem.ownerDocument.activeElement;
8541		}
8542	},
8543	setFilters: {
8544		first: function( elem, i ) {
8545			return i === 0;
8546		},
8547
8548		last: function( elem, i, match, array ) {
8549			return i === array.length - 1;
8550		},
8551
8552		even: function( elem, i ) {
8553			return i % 2 === 0;
8554		},
8555
8556		odd: function( elem, i ) {
8557			return i % 2 === 1;
8558		},
8559
8560		lt: function( elem, i, match ) {
8561			return i < match[3] - 0;
8562		},
8563
8564		gt: function( elem, i, match ) {
8565			return i > match[3] - 0;
8566		},
8567
8568		nth: function( elem, i, match ) {
8569			return match[3] - 0 === i;
8570		},
8571
8572		eq: function( elem, i, match ) {
8573			return match[3] - 0 === i;
8574		}
8575	},
8576	filter: {
8577		PSEUDO: function( elem, match, i, array ) {
8578			var name = match[1],
8579				filter = Expr.filters[ name ];
8580
8581			if ( filter ) {
8582				return filter( elem, i, match, array );
8583
8584			} else if ( name === "contains" ) {
8585				return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
8586
8587			} else if ( name === "not" ) {
8588				var not = match[3];
8589
8590				for ( var j = 0, l = not.length; j < l; j++ ) {
8591					if ( not[j] === elem ) {
8592						return false;
8593					}
8594				}
8595
8596				return true;
8597
8598			} else {
8599				Sizzle.error( name );
8600			}
8601		},
8602
8603		CHILD: function( elem, match ) {
8604			var first, last,
8605				doneName, parent, cache,
8606				count, diff,
8607				type = match[1],
8608				node = elem;
8609
8610			switch ( type ) {
8611				case "only":
8612				case "first":
8613					while ( (node = node.previousSibling) ) {
8614						if ( node.nodeType === 1 ) {
8615							return false;
8616						}
8617					}
8618
8619					if ( type === "first" ) {
8620						return true;
8621					}
8622
8623					node = elem;
8624
8625					/* falls through */
8626				case "last":
8627					while ( (node = node.nextSibling) ) {
8628						if ( node.nodeType === 1 ) {
8629							return false;
8630						}
8631					}
8632
8633					return true;
8634
8635				case "nth":
8636					first = match[2];
8637					last = match[3];
8638
8639					if ( first === 1 && last === 0 ) {
8640						return true;
8641					}
8642
8643					doneName = match[0];
8644					parent = elem.parentNode;
8645
8646					if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
8647						count = 0;
8648
8649						for ( node = parent.firstChild; node; node = node.nextSibling ) {
8650							if ( node.nodeType === 1 ) {
8651								node.nodeIndex = ++count;
8652							}
8653						}
8654
8655						parent[ expando ] = doneName;
8656					}
8657
8658					diff = elem.nodeIndex - last;
8659
8660					if ( first === 0 ) {
8661						return diff === 0;
8662
8663					} else {
8664						return ( diff % first === 0 && diff / first >= 0 );
8665					}
8666			}
8667		},
8668
8669		ID: function( elem, match ) {
8670			return elem.nodeType === 1 && elem.getAttribute("id") === match;
8671		},
8672
8673		TAG: function( elem, match ) {
8674			return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
8675		},
8676
8677		CLASS: function( elem, match ) {
8678			return (" " + (elem.className || elem.getAttribute("class")) + " ")
8679				.indexOf( match ) > -1;
8680		},
8681
8682		ATTR: function( elem, match ) {
8683			var name = match[1],
8684				result = Sizzle.attr ?
8685					Sizzle.attr( elem, name ) :
8686					Expr.attrHandle[ name ] ?
8687					Expr.attrHandle[ name ]( elem ) :
8688					elem[ name ] != null ?
8689						elem[ name ] :
8690						elem.getAttribute( name ),
8691				value = result + "",
8692				type = match[2],
8693				check = match[4];
8694
8695			return result == null ?
8696				type === "!=" :
8697				!type && Sizzle.attr ?
8698				result != null :
8699				type === "=" ?
8700				value === check :
8701				type === "*=" ?
8702				value.indexOf(check) >= 0 :
8703				type === "~=" ?
8704				(" " + value + " ").indexOf(check) >= 0 :
8705				!check ?
8706				value && result !== false :
8707				type === "!=" ?
8708				value !== check :
8709				type === "^=" ?
8710				value.indexOf(check) === 0 :
8711				type === "$=" ?
8712				value.substr(value.length - check.length) === check :
8713				type === "|=" ?
8714				value === check || value.substr(0, check.length + 1) === check + "-" :
8715				false;
8716		},
8717
8718		POS: function( elem, match, i, array ) {
8719			var name = match[2],
8720				filter = Expr.setFilters[ name ];
8721
8722			if ( filter ) {
8723				return filter( elem, i, match, array );
8724			}
8725		}
8726	}
8727};
8728
8729var origPOS = Expr.match.POS,
8730	fescape = function(all, num){
8731		return "\\" + (num - 0 + 1);
8732	};
8733
8734for ( var type in Expr.match ) {
8735	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
8736	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
8737}
8738// Expose origPOS
8739// "global" as in regardless of relation to brackets/parens
8740Expr.match.globalPOS = origPOS;
8741
8742var makeArray = function( array, results ) {
8743	array = Array.prototype.slice.call( array, 0 );
8744
8745	if ( results ) {
8746		results.push.apply( results, array );
8747		return results;
8748	}
8749
8750	return array;
8751};
8752
8753// Perform a simple check to determine if the browser is capable of
8754// converting a NodeList to an array using builtin methods.
8755// Also verifies that the returned array holds DOM nodes
8756// (which is not the case in the Blackberry browser)
8757try {
8758	Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
8759
8760// Provide a fallback method if it does not work
8761} catch( e ) {
8762	makeArray = function( array, results ) {
8763		var i = 0,
8764			ret = results || [];
8765
8766		if ( toString.call(array) === "[object Array]" ) {
8767			Array.prototype.push.apply( ret, array );
8768
8769		} else {
8770			if ( typeof array.length === "number" ) {
8771				for ( var l = array.length; i < l; i++ ) {
8772					ret.push( array[i] );
8773				}
8774
8775			} else {
8776				for ( ; array[i]; i++ ) {
8777					ret.push( array[i] );
8778				}
8779			}
8780		}
8781
8782		return ret;
8783	};
8784}
8785
8786var sortOrder, siblingCheck;
8787
8788if ( document.documentElement.compareDocumentPosition ) {
8789	sortOrder = function( a, b ) {
8790		if ( a === b ) {
8791			hasDuplicate = true;
8792			return 0;
8793		}
8794
8795		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
8796			return a.compareDocumentPosition ? -1 : 1;
8797		}
8798
8799		return a.compareDocumentPosition(b) & 4 ? -1 : 1;
8800	};
8801
8802} else {
8803	sortOrder = function( a, b ) {
8804		// The nodes are identical, we can exit early
8805		if ( a === b ) {
8806			hasDuplicate = true;
8807			return 0;
8808
8809		// Fallback to using sourceIndex (in IE) if it's available on both nodes
8810		} else if ( a.sourceIndex && b.sourceIndex ) {
8811			return a.sourceIndex - b.sourceIndex;
8812		}
8813
8814		var al, bl,
8815			ap = [],
8816			bp = [],
8817			aup = a.parentNode,
8818			bup = b.parentNode,
8819			cur = aup;
8820
8821		// If the nodes are siblings (or identical) we can do a quick check
8822		if ( aup === bup ) {
8823			return siblingCheck( a, b );
8824
8825		// If no parents were found then the nodes are disconnected
8826		} else if ( !aup ) {
8827			return -1;
8828
8829		} else if ( !bup ) {
8830			return 1;
8831		}
8832
8833		// Otherwise they're somewhere else in the tree so we need
8834		// to build up a full list of the parentNodes for comparison
8835		while ( cur ) {
8836			ap.unshift( cur );
8837			cur = cur.parentNode;
8838		}
8839
8840		cur = bup;
8841
8842		while ( cur ) {
8843			bp.unshift( cur );
8844			cur = cur.parentNode;
8845		}
8846
8847		al = ap.length;
8848		bl = bp.length;
8849
8850		// Start walking down the tree looking for a discrepancy
8851		for ( var i = 0; i < al && i < bl; i++ ) {
8852			if ( ap[i] !== bp[i] ) {
8853				return siblingCheck( ap[i], bp[i] );
8854			}
8855		}
8856
8857		// We ended someplace up the tree so do a sibling check
8858		return i === al ?
8859			siblingCheck( a, bp[i], -1 ) :
8860			siblingCheck( ap[i], b, 1 );
8861	};
8862
8863	siblingCheck = function( a, b, ret ) {
8864		if ( a === b ) {
8865			return ret;
8866		}
8867
8868		var cur = a.nextSibling;
8869
8870		while ( cur ) {
8871			if ( cur === b ) {
8872				return -1;
8873			}
8874
8875			cur = cur.nextSibling;
8876		}
8877
8878		return 1;
8879	};
8880}
8881
8882// Check to see if the browser returns elements by name when
8883// querying by getElementById (and provide a workaround)
8884(function(){
8885	// We're going to inject a fake input element with a specified name
8886	var form = document.createElement("div"),
8887		id = "script" + (new Date()).getTime(),
8888		root = document.documentElement;
8889
8890	form.innerHTML = "<a name='" + id + "'/>";
8891
8892	// Inject it into the root element, check its status, and remove it quickly
8893	root.insertBefore( form, root.firstChild );
8894
8895	// The workaround has to do additional checks after a getElementById
8896	// Which slows things down for other browsers (hence the branching)
8897	if ( document.getElementById( id ) ) {
8898		Expr.find.ID = function( match, context, isXML ) {
8899			if ( typeof context.getElementById !== "undefined" && !isXML ) {
8900				var m = context.getElementById(match[1]);
8901
8902				return m ?
8903					m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
8904						[m] :
8905						undefined :
8906					[];
8907			}
8908		};
8909
8910		Expr.filter.ID = function( elem, match ) {
8911			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
8912
8913			return elem.nodeType === 1 && node && node.nodeValue === match;
8914		};
8915	}
8916
8917	root.removeChild( form );
8918
8919	// release memory in IE
8920	root = form = null;
8921})();
8922
8923(function(){
8924	// Check to see if the browser returns only elements
8925	// when doing getElementsByTagName("*")
8926
8927	// Create a fake element
8928	var div = document.createElement("div");
8929	div.appendChild( document.createComment("") );
8930
8931	// Make sure no comments are found
8932	if ( div.getElementsByTagName("*").length > 0 ) {
8933		Expr.find.TAG = function( match, context ) {
8934			var results = context.getElementsByTagName( match[1] );
8935
8936			// Filter out possible comments
8937			if ( match[1] === "*" ) {
8938				var tmp = [];
8939
8940				for ( var i = 0; results[i]; i++ ) {
8941					if ( results[i].nodeType === 1 ) {
8942						tmp.push( results[i] );
8943					}
8944				}
8945
8946				results = tmp;
8947			}
8948
8949			return results;
8950		};
8951	}
8952
8953	// Check to see if an attribute returns normalized href attributes
8954	div.innerHTML = "<a href='#'></a>";
8955
8956	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
8957			div.firstChild.getAttribute("href") !== "#" ) {
8958
8959		Expr.attrHandle.href = function( elem ) {
8960			return elem.getAttribute( "href", 2 );
8961		};
8962	}
8963
8964	// release memory in IE
8965	div = null;
8966})();
8967
8968if ( document.querySelectorAll ) {
8969	(function(){
8970		var oldSizzle = Sizzle,
8971			div = document.createElement("div"),
8972			id = "__sizzle__";
8973
8974		div.innerHTML = "<p class='TEST'></p>";
8975
8976		// Safari can't handle uppercase or unicode characters when
8977		// in quirks mode.
8978		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
8979			return;
8980		}
8981
8982		Sizzle = function( query, context, extra, seed ) {
8983			context = context || document;
8984
8985			// Only use querySelectorAll on non-XML documents
8986			// (ID selectors don't work in non-HTML documents)
8987			if ( !seed && !Sizzle.isXML(context) ) {
8988				// See if we find a selector to speed up
8989				var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
8990
8991				if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
8992					// Speed-up: Sizzle("TAG")
8993					if ( match[1] ) {
8994						return makeArray( context.getElementsByTagName( query ), extra );
8995
8996					// Speed-up: Sizzle(".CLASS")
8997					} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
8998						return makeArray( context.getElementsByClassName( match[2] ), extra );
8999					}
9000				}
9001
9002				if ( context.nodeType === 9 ) {
9003					// Speed-up: Sizzle("body")
9004					// The body element only exists once, optimize finding it
9005					if ( query === "body" && context.body ) {
9006						return makeArray( [ context.body ], extra );
9007
9008					// Speed-up: Sizzle("#ID")
9009					} else if ( match && match[3] ) {
9010						var elem = context.getElementById( match[3] );
9011
9012						// Check parentNode to catch when Blackberry 4.6 returns
9013						// nodes that are no longer in the document #6963
9014						if ( elem && elem.parentNode ) {
9015							// Handle the case where IE and Opera return items
9016							// by name instead of ID
9017							if ( elem.id === match[3] ) {
9018								return makeArray( [ elem ], extra );
9019							}
9020
9021						} else {
9022							return makeArray( [], extra );
9023						}
9024					}
9025
9026					try {
9027						return makeArray( context.querySelectorAll(query), extra );
9028					} catch(qsaError) {}
9029
9030				// qSA works strangely on Element-rooted queries
9031				// We can work around this by specifying an extra ID on the root
9032				// and working up from there (Thanks to Andrew Dupont for the technique)
9033				// IE 8 doesn't work on object elements
9034				} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
9035					var oldContext = context,
9036						old = context.getAttribute( "id" ),
9037						nid = old || id,
9038						hasParent = context.parentNode,
9039						relativeHierarchySelector = /^\s*[+~]/.test( query );
9040
9041					if ( !old ) {
9042						context.setAttribute( "id", nid );
9043					} else {
9044						nid = nid.replace( /'/g, "\\$&" );
9045					}
9046					if ( relativeHierarchySelector && hasParent ) {
9047						context = context.parentNode;
9048					}
9049
9050					try {
9051						if ( !relativeHierarchySelector || hasParent ) {
9052							return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
9053						}
9054
9055					} catch(pseudoError) {
9056					} finally {
9057						if ( !old ) {
9058							oldContext.removeAttribute( "id" );
9059						}
9060					}
9061				}
9062			}
9063
9064			return oldSizzle(query, context, extra, seed);
9065		};
9066
9067		for ( var prop in oldSizzle ) {
9068			Sizzle[ prop ] = oldSizzle[ prop ];
9069		}
9070
9071		// release memory in IE
9072		div = null;
9073	})();
9074}
9075
9076(function(){
9077	var html = document.documentElement,
9078		matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
9079
9080	if ( matches ) {
9081		// Check to see if it's possible to do matchesSelector
9082		// on a disconnected node (IE 9 fails this)
9083		var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
9084			pseudoWorks = false;
9085
9086		try {
9087			// This should fail with an exception
9088			// Gecko does not error, returns false instead
9089			matches.call( document.documentElement, "[test!='']:sizzle" );
9090
9091		} catch( pseudoError ) {
9092			pseudoWorks = true;
9093		}
9094
9095		Sizzle.matchesSelector = function( node, expr ) {
9096			// Make sure that attribute selectors are quoted
9097			expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
9098
9099			if ( !Sizzle.isXML( node ) ) {
9100				try {
9101					if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
9102						var ret = matches.call( node, expr );
9103
9104						// IE 9's matchesSelector returns false on disconnected nodes
9105						if ( ret || !disconnectedMatch ||
9106								// As well, disconnected nodes are said to be in a document
9107								// fragment in IE 9, so check for that
9108								node.document && node.document.nodeType !== 11 ) {
9109							return ret;
9110						}
9111					}
9112				} catch(e) {}
9113			}
9114
9115			return Sizzle(expr, null, null, [node]).length > 0;
9116		};
9117	}
9118})();
9119
9120(function(){
9121	var div = document.createElement("div");
9122
9123	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
9124
9125	// Opera can't find a second classname (in 9.6)
9126	// Also, make sure that getElementsByClassName actually exists
9127	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
9128		return;
9129	}
9130
9131	// Safari caches class attributes, doesn't catch changes (in 3.2)
9132	div.lastChild.className = "e";
9133
9134	if ( div.getElementsByClassName("e").length === 1 ) {
9135		return;
9136	}
9137
9138	Expr.order.splice(1, 0, "CLASS");
9139	Expr.find.CLASS = function( match, context, isXML ) {
9140		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
9141			return context.getElementsByClassName(match[1]);
9142		}
9143	};
9144
9145	// release memory in IE
9146	div = null;
9147})();
9148
9149function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
9150	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
9151		var elem = checkSet[i];
9152
9153		if ( elem ) {
9154			var match = false;
9155
9156			elem = elem[dir];
9157
9158			while ( elem ) {
9159				if ( elem[ expando ] === doneName ) {
9160					match = checkSet[elem.sizset];
9161					break;
9162				}
9163
9164				if ( elem.nodeType === 1 && !isXML ){
9165					elem[ expando ] = doneName;
9166					elem.sizset = i;
9167				}
9168
9169				if ( elem.nodeName.toLowerCase() === cur ) {
9170					match = elem;
9171					break;
9172				}
9173
9174				elem = elem[dir];
9175			}
9176
9177			checkSet[i] = match;
9178		}
9179	}
9180}
9181
9182function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
9183	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
9184		var elem = checkSet[i];
9185
9186		if ( elem ) {
9187			var match = false;
9188
9189			elem = elem[dir];
9190
9191			while ( elem ) {
9192				if ( elem[ expando ] === doneName ) {
9193					match = checkSet[elem.sizset];
9194					break;
9195				}
9196
9197				if ( elem.nodeType === 1 ) {
9198					if ( !isXML ) {
9199						elem[ expando ] = doneName;
9200						elem.sizset = i;
9201					}
9202
9203					if ( typeof cur !== "string" ) {
9204						if ( elem === cur ) {
9205							match = true;
9206							break;
9207						}
9208
9209					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
9210						match = elem;
9211						break;
9212					}
9213				}
9214
9215				elem = elem[dir];
9216			}
9217
9218			checkSet[i] = match;
9219		}
9220	}
9221}
9222
9223if ( document.documentElement.contains ) {
9224	Sizzle.contains = function( a, b ) {
9225		return a !== b && (a.contains ? a.contains(b) : true);
9226	};
9227
9228} else if ( document.documentElement.compareDocumentPosition ) {
9229	Sizzle.contains = function( a, b ) {
9230		return !!(a.compareDocumentPosition(b) & 16);
9231	};
9232
9233} else {
9234	Sizzle.contains = function() {
9235		return false;
9236	};
9237}
9238
9239Sizzle.isXML = function( elem ) {
9240	// documentElement is verified for cases where it doesn't yet exist
9241	// (such as loading iframes in IE - #4833)
9242	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
9243
9244	return documentElement ? documentElement.nodeName !== "HTML" : false;
9245};
9246
9247var posProcess = function( selector, context, seed ) {
9248	var match,
9249		tmpSet = [],
9250		later = "",
9251		root = context.nodeType ? [context] : context;
9252
9253	// Position selectors must be done after the filter
9254	// And so must :not(positional) so we move all PSEUDOs to the end
9255	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
9256		later += match[0];
9257		selector = selector.replace( Expr.match.PSEUDO, "" );
9258	}
9259
9260	selector = Expr.relative[selector] ? selector + "*" : selector;
9261
9262	for ( var i = 0, l = root.length; i < l; i++ ) {
9263		Sizzle( selector, root[i], tmpSet, seed );
9264	}
9265
9266	return Sizzle.filter( later, tmpSet );
9267};
9268
9269// EXPOSE
9270
9271window.tinymce.dom.Sizzle = Sizzle;
9272
9273})();
9274
9275
9276(function(tinymce) {
9277	tinymce.dom.Element = function(id, settings) {
9278		var t = this, dom, el;
9279
9280		t.settings = settings = settings || {};
9281		t.id = id;
9282		t.dom = dom = settings.dom || tinymce.DOM;
9283
9284		// Only IE leaks DOM references, this is a lot faster
9285		if (!tinymce.isIE)
9286			el = dom.get(t.id);
9287
9288		tinymce.each(
9289				('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + 
9290				'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + 
9291				'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + 
9292				'isHidden,setHTML,get').split(/,/), function(k) {
9293					t[k] = function() {
9294						var a = [id], i;
9295
9296						for (i = 0; i < arguments.length; i++)
9297							a.push(arguments[i]);
9298
9299						a = dom[k].apply(dom, a);
9300						t.update(k);
9301
9302						return a;
9303					};
9304			}
9305		);
9306
9307		tinymce.extend(t, {
9308			on : function(n, f, s) {
9309				return tinymce.dom.Event.add(t.id, n, f, s);
9310			},
9311
9312			getXY : function() {
9313				return {
9314					x : parseInt(t.getStyle('left')),
9315					y : parseInt(t.getStyle('top'))
9316				};
9317			},
9318
9319			getSize : function() {
9320				var n = dom.get(t.id);
9321
9322				return {
9323					w : parseInt(t.getStyle('width') || n.clientWidth),
9324					h : parseInt(t.getStyle('height') || n.clientHeight)
9325				};
9326			},
9327
9328			moveTo : function(x, y) {
9329				t.setStyles({left : x, top : y});
9330			},
9331
9332			moveBy : function(x, y) {
9333				var p = t.getXY();
9334
9335				t.moveTo(p.x + x, p.y + y);
9336			},
9337
9338			resizeTo : function(w, h) {
9339				t.setStyles({width : w, height : h});
9340			},
9341
9342			resizeBy : function(w, h) {
9343				var s = t.getSize();
9344
9345				t.resizeTo(s.w + w, s.h + h);
9346			},
9347
9348			update : function(k) {
9349				var b;
9350
9351				if (tinymce.isIE6 && settings.blocker) {
9352					k = k || '';
9353
9354					// Ignore getters
9355					if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
9356						return;
9357
9358					// Remove blocker on remove
9359					if (k == 'remove') {
9360						dom.remove(t.blocker);
9361						return;
9362					}
9363
9364					if (!t.blocker) {
9365						t.blocker = dom.uniqueId();
9366						b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});
9367						dom.setStyle(b, 'opacity', 0);
9368					} else
9369						b = dom.get(t.blocker);
9370
9371					dom.setStyles(b, {
9372						left : t.getStyle('left', 1),
9373						top : t.getStyle('top', 1),
9374						width : t.getStyle('width', 1),
9375						height : t.getStyle('height', 1),
9376						display : t.getStyle('display', 1),
9377						zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1
9378					});
9379				}
9380			}
9381		});
9382	};
9383})(tinymce);
9384
9385(function(tinymce) {
9386	function trimNl(s) {
9387		return s.replace(/[\n\r]+/g, '');
9388	};
9389
9390	// Shorten names
9391	var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each, TreeWalker = tinymce.dom.TreeWalker;
9392
9393	tinymce.create('tinymce.dom.Selection', {
9394		Selection : function(dom, win, serializer, editor) {
9395			var t = this;
9396
9397			t.dom = dom;
9398			t.win = win;
9399			t.serializer = serializer;
9400			t.editor = editor;
9401
9402			// Add events
9403			each([
9404				'onBeforeSetContent',
9405
9406				'onBeforeGetContent',
9407
9408				'onSetContent',
9409
9410				'onGetContent'
9411			], function(e) {
9412				t[e] = new tinymce.util.Dispatcher(t);
9413			});
9414
9415			// No W3C Range support
9416			if (!t.win.getSelection)
9417				t.tridentSel = new tinymce.dom.TridentSelection(t);
9418
9419			if (tinymce.isIE && ! tinymce.isIE11 && dom.boxModel)
9420				this._fixIESelection();
9421
9422			// Prevent leaks
9423			tinymce.addUnload(t.destroy, t);
9424		},
9425
9426		setCursorLocation: function(node, offset) {
9427			var t = this; var r = t.dom.createRng();
9428			r.setStart(node, offset);
9429			r.setEnd(node, offset);
9430			t.setRng(r);
9431			t.collapse(false);
9432		},
9433		getContent : function(s) {
9434			var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;
9435
9436			s = s || {};
9437			wb = wa = '';
9438			s.get = true;
9439			s.format = s.format || 'html';
9440			s.forced_root_block = '';
9441			t.onBeforeGetContent.dispatch(t, s);
9442
9443			if (s.format == 'text')
9444				return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));
9445
9446			if (r.cloneContents) {
9447				n = r.cloneContents();
9448
9449				if (n)
9450					e.appendChild(n);
9451			} else if (is(r.item) || is(r.htmlText)) {
9452				// IE will produce invalid markup if elements are present that
9453				// it doesn't understand like custom elements or HTML5 elements.
9454				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
9455				e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText);
9456				e.removeChild(e.firstChild);
9457			} else
9458				e.innerHTML = r.toString();
9459
9460			// Keep whitespace before and after
9461			if (/^\s/.test(e.innerHTML))
9462				wb = ' ';
9463
9464			if (/\s+$/.test(e.innerHTML))
9465				wa = ' ';
9466
9467			s.getInner = true;
9468
9469			s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;
9470			t.onGetContent.dispatch(t, s);
9471
9472			return s.content;
9473		},
9474
9475		setContent : function(content, args) {
9476			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
9477
9478			args = args || {format : 'html'};
9479			args.set = true;
9480			content = args.content = content;
9481
9482			// Dispatch before set content event
9483			if (!args.no_events)
9484				self.onBeforeSetContent.dispatch(self, args);
9485
9486			content = args.content;
9487
9488			if (rng.insertNode) {
9489				// Make caret marker since insertNode places the caret in the beginning of text after insert
9490				content += '<span id="__caret">_</span>';
9491
9492				// Delete and insert new node
9493				if (rng.startContainer == doc && rng.endContainer == doc) {
9494					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
9495					doc.body.innerHTML = content;
9496				} else {
9497					rng.deleteContents();
9498
9499					if (doc.body.childNodes.length === 0) {
9500						doc.body.innerHTML = content;
9501					} else {
9502						// createContextualFragment doesn't exists in IE 9 DOMRanges
9503						if (rng.createContextualFragment) {
9504							rng.insertNode(rng.createContextualFragment(content));
9505						} else {
9506							// Fake createContextualFragment call in IE 9
9507							frag = doc.createDocumentFragment();
9508							temp = doc.createElement('div');
9509
9510							frag.appendChild(temp);
9511							temp.outerHTML = content;
9512
9513							rng.insertNode(frag);
9514						}
9515					}
9516				}
9517
9518				// Move to caret marker
9519				caretNode = self.dom.get('__caret');
9520
9521				// Make sure we wrap it compleatly, Opera fails with a simple select call
9522				rng = doc.createRange();
9523				rng.setStartBefore(caretNode);
9524				rng.setEndBefore(caretNode);
9525				self.setRng(rng);
9526
9527				// Remove the caret position
9528				self.dom.remove('__caret');
9529
9530				try {
9531					self.setRng(rng);
9532				} catch (ex) {
9533					// Might fail on Opera for some odd reason
9534				}
9535			} else {
9536				if (rng.item) {
9537					// Delete content and get caret text selection
9538					doc.execCommand('Delete', false, null);
9539					rng = self.getRng();
9540				}
9541
9542				// Explorer removes spaces from the beginning of pasted contents
9543				if (/^\s+/.test(content)) {
9544					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
9545					self.dom.remove('__mce_tmp');
9546				} else
9547					rng.pasteHTML(content);
9548			}
9549
9550			// Dispatch set content event
9551			if (!args.no_events)
9552				self.onSetContent.dispatch(self, args);
9553		},
9554
9555		getStart : function() {
9556			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
9557
9558			if (rng.duplicate || rng.item) {
9559				// Control selection, return first item
9560				if (rng.item)
9561					return rng.item(0);
9562
9563				// Get start element
9564				checkRng = rng.duplicate();
9565				checkRng.collapse(1);
9566				startElement = checkRng.parentElement();
9567				if (startElement.ownerDocument !== self.dom.doc) {
9568					startElement = self.dom.getRoot();
9569				}
9570
9571				// Check if range parent is inside the start element, then return the inner parent element
9572				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
9573				parentElement = node = rng.parentElement();
9574				while (node = node.parentNode) {
9575					if (node == startElement) {
9576						startElement = parentElement;
9577						break;
9578					}
9579				}
9580
9581				return startElement;
9582			} else {
9583				startElement = rng.startContainer;
9584
9585				if (startElement.nodeType == 1 && startElement.hasChildNodes())
9586					startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
9587
9588				if (startElement && startElement.nodeType == 3)
9589					return startElement.parentNode;
9590
9591				return startElement;
9592			}
9593		},
9594
9595		getEnd : function() {
9596			var self = this, rng = self.getRng(), endElement, endOffset;
9597
9598			if (rng.duplicate || rng.item) {
9599				if (rng.item)
9600					return rng.item(0);
9601
9602				rng = rng.duplicate();
9603				rng.collapse(0);
9604				endElement = rng.parentElement();
9605				if (endElement.ownerDocument !== self.dom.doc) {
9606					endElement = self.dom.getRoot();
9607				}
9608
9609				if (endElement && endElement.nodeName == 'BODY')
9610					return endElement.lastChild || endElement;
9611
9612				return endElement;
9613			} else {
9614				endElement = rng.endContainer;
9615				endOffset = rng.endOffset;
9616
9617				if (endElement.nodeType == 1 && endElement.hasChildNodes())
9618					endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
9619
9620				if (endElement && endElement.nodeType == 3)
9621					return endElement.parentNode;
9622
9623				return endElement;
9624			}
9625		},
9626
9627		getBookmark : function(type, normalized) {
9628			var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;
9629
9630			function findIndex(name, element) {
9631				var index = 0;
9632
9633				each(dom.select(name), function(node, i) {
9634					if (node == element)
9635						index = i;
9636				});
9637
9638				return index;
9639			};
9640
9641			function normalizeTableCellSelection(rng) {
9642				function moveEndPoint(start) {
9643					var container, offset, childNodes, prefix = start ? 'start' : 'end';
9644
9645					container = rng[prefix + 'Container'];
9646					offset = rng[prefix + 'Offset'];
9647
9648					if (container.nodeType == 1 && container.nodeName == "TR") {
9649						childNodes = container.childNodes;
9650						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
9651						if (container) {
9652							offset = start ? 0 : container.childNodes.length;
9653							rng['set' + (start ? 'Start' : 'End')](container, offset);
9654						}
9655					}
9656				};
9657
9658				moveEndPoint(true);
9659				moveEndPoint();
9660
9661				return rng;
9662			};
9663
9664			function getLocation() {
9665				var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};
9666
9667				function getPoint(rng, start) {
9668					var container = rng[start ? 'startContainer' : 'endContainer'],
9669						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
9670
9671					if (container.nodeType == 3) {
9672						if (normalized) {
9673							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)
9674								offset += node.nodeValue.length;
9675						}
9676
9677						point.push(offset);
9678					} else {
9679						childNodes = container.childNodes;
9680
9681						if (offset >= childNodes.length && childNodes.length) {
9682							after = 1;
9683							offset = Math.max(0, childNodes.length - 1);
9684						}
9685
9686						point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
9687					}
9688
9689					for (; container && container != root; container = container.parentNode)
9690						point.push(t.dom.nodeIndex(container, normalized));
9691
9692					return point;
9693				};
9694
9695				bookmark.start = getPoint(rng, true);
9696
9697				if (!t.isCollapsed())
9698					bookmark.end = getPoint(rng);
9699
9700				return bookmark;
9701			};
9702
9703			if (type == 2) {
9704				if (t.tridentSel)
9705					return t.tridentSel.getBookmark(type);
9706
9707				return getLocation();
9708			}
9709
9710			// Handle simple range
9711			if (type) {
9712				rng = t.getRng();
9713
9714				if (rng.setStart) {
9715					rng = {
9716						startContainer: rng.startContainer,
9717						startOffset: rng.startOffset,
9718						endContainer: rng.endContainer,
9719						endOffset: rng.endOffset
9720					};
9721				}
9722
9723				return {rng : rng};
9724			}
9725
9726			rng = t.getRng();
9727			id = dom.uniqueId();
9728			collapsed = tinyMCE.activeEditor.selection.isCollapsed();
9729			styles = 'overflow:hidden;line-height:0px';
9730
9731			// Explorer method
9732			if (rng.duplicate || rng.item) {
9733				// Text selection
9734				if (!rng.item) {
9735					rng2 = rng.duplicate();
9736
9737					try {
9738						// Insert start marker
9739						rng.collapse();
9740						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
9741
9742						// Insert end marker
9743						if (!collapsed) {
9744							rng2.collapse(false);
9745
9746							// Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>
9747							rng.moveToElementText(rng2.parentElement());
9748							if (rng.compareEndPoints('StartToEnd', rng2) === 0)
9749								rng2.move('character', -1);
9750
9751							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
9752						}
9753					} catch (ex) {
9754						// IE might throw unspecified error so lets ignore it
9755						return null;
9756					}
9757				} else {
9758					// Control selection
9759					element = rng.item(0);
9760					name = element.nodeName;
9761
9762					return {name : name, index : findIndex(name, element)};
9763				}
9764			} else {
9765				element = t.getNode();
9766				name = element.nodeName;
9767				if (name == 'IMG')
9768					return {name : name, index : findIndex(name, element)};
9769
9770				// W3C method
9771				rng2 = normalizeTableCellSelection(rng.cloneRange());
9772
9773				// Insert end marker
9774				if (!collapsed) {
9775					rng2.collapse(false);
9776					rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));
9777				}
9778
9779				rng = normalizeTableCellSelection(rng);
9780				rng.collapse(true);
9781				rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));
9782			}
9783
9784			t.moveToBookmark({id : id, keep : 1});
9785
9786			return {id : id};
9787		},
9788
9789		moveToBookmark : function(bookmark) {
9790			var t = this, dom = t.dom, marker1, marker2, rng, rng2, root, startContainer, endContainer, startOffset, endOffset;
9791
9792			function setEndPoint(start) {
9793				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
9794
9795				if (point) {
9796					offset = point[0];
9797
9798					// Find container node
9799					for (node = root, i = point.length - 1; i >= 1; i--) {
9800						children = node.childNodes;
9801
9802						if (point[i] > children.length - 1)
9803							return;
9804
9805						node = children[point[i]];
9806					}
9807
9808					// Move text offset to best suitable location
9809					if (node.nodeType === 3)
9810						offset = Math.min(point[0], node.nodeValue.length);
9811
9812					// Move element offset to best suitable location
9813					if (node.nodeType === 1)
9814						offset = Math.min(point[0], node.childNodes.length);
9815
9816					// Set offset within container node
9817					if (start)
9818						rng.setStart(node, offset);
9819					else
9820						rng.setEnd(node, offset);
9821				}
9822
9823				return true;
9824			};
9825
9826			function restoreEndPoint(suffix) {
9827				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
9828
9829				if (marker) {
9830					node = marker.parentNode;
9831
9832					if (suffix == 'start') {
9833						if (!keep) {
9834							idx = dom.nodeIndex(marker);
9835						} else {
9836							node = marker.firstChild;
9837							idx = 1;
9838						}
9839
9840						startContainer = endContainer = node;
9841						startOffset = endOffset = idx;
9842					} else {
9843						if (!keep) {
9844							idx = dom.nodeIndex(marker);
9845						} else {
9846							node = marker.firstChild;
9847							idx = 1;
9848						}
9849
9850						endContainer = node;
9851						endOffset = idx;
9852					}
9853
9854					if (!keep) {
9855						prev = marker.previousSibling;
9856						next = marker.nextSibling;
9857
9858						// Remove all marker text nodes
9859						each(tinymce.grep(marker.childNodes), function(node) {
9860							if (node.nodeType == 3)
9861								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
9862						});
9863
9864						// Remove marker but keep children if for example contents where inserted into the marker
9865						// Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature
9866						while (marker = dom.get(bookmark.id + '_' + suffix))
9867							dom.remove(marker, 1);
9868
9869						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
9870						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact
9871						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {
9872							idx = prev.nodeValue.length;
9873							prev.appendData(next.nodeValue);
9874							dom.remove(next);
9875
9876							if (suffix == 'start') {
9877								startContainer = endContainer = prev;
9878								startOffset = endOffset = idx;
9879							} else {
9880								endContainer = prev;
9881								endOffset = idx;
9882							}
9883						}
9884					}
9885				}
9886			};
9887
9888			function addBogus(node) {
9889				// Adds a bogus BR element for empty block elements
9890				if (dom.isBlock(node) && !node.innerHTML && !isIE)
9891					node.innerHTML = '<br data-mce-bogus="1" />';
9892
9893				return node;
9894			};
9895
9896			if (bookmark) {
9897				if (bookmark.start) {
9898					rng = dom.createRng();
9899					root = dom.getRoot();
9900
9901					if (t.tridentSel)
9902						return t.tridentSel.moveToBookmark(bookmark);
9903
9904					if (setEndPoint(true) && setEndPoint()) {
9905						t.setRng(rng);
9906					}
9907				} else if (bookmark.id) {
9908					// Restore start/end points
9909					restoreEndPoint('start');
9910					restoreEndPoint('end');
9911
9912					if (startContainer) {
9913						rng = dom.createRng();
9914						rng.setStart(addBogus(startContainer), startOffset);
9915						rng.setEnd(addBogus(endContainer), endOffset);
9916						t.setRng(rng);
9917					}
9918				} else if (bookmark.name) {
9919					t.select(dom.select(bookmark.name)[bookmark.index]);
9920				} else if (bookmark.rng) {
9921					rng = bookmark.rng;
9922
9923					if (rng.startContainer) {
9924						rng2 = t.dom.createRng();
9925
9926						try {
9927							rng2.setStart(rng.startContainer, rng.startOffset);
9928							rng2.setEnd(rng.endContainer, rng.endOffset);
9929						} catch (e) {
9930							// Might fail with index error
9931						}
9932
9933						rng = rng2;
9934					}
9935
9936					t.setRng(rng);
9937				}
9938			}
9939		},
9940
9941		select : function(node, content) {
9942			var t = this, dom = t.dom, rng = dom.createRng(), idx;
9943
9944			function setPoint(node, start) {
9945				var walker = new TreeWalker(node, node);
9946
9947				do {
9948					// Text node
9949					if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length !== 0) {
9950						if (start)
9951							rng.setStart(node, 0);
9952						else
9953							rng.setEnd(node, node.nodeValue.length);
9954
9955						return;
9956					}
9957
9958					// BR element
9959					if (node.nodeName == 'BR') {
9960						if (start)
9961							rng.setStartBefore(node);
9962						else
9963							rng.setEndBefore(node);
9964
9965						return;
9966					}
9967				} while (node = (start ? walker.next() : walker.prev()));
9968			};
9969
9970			if (node) {
9971				idx = dom.nodeIndex(node);
9972				rng.setStart(node.parentNode, idx);
9973				rng.setEnd(node.parentNode, idx + 1);
9974
9975				// Find first/last text node or BR element
9976				if (content) {
9977					setPoint(node, 1);
9978					setPoint(node);
9979				}
9980
9981				t.setRng(rng);
9982			}
9983
9984			return node;
9985		},
9986
9987		isCollapsed : function() {
9988			var t = this, r = t.getRng(), s = t.getSel();
9989
9990			if (!r || r.item)
9991				return false;
9992
9993			if (r.compareEndPoints)
9994				return r.compareEndPoints('StartToEnd', r) === 0;
9995
9996			return !s || r.collapsed;
9997		},
9998
9999		collapse : function(to_start) {
10000			var self = this, rng = self.getRng(), node;
10001
10002			// Control range on IE
10003			if (rng.item) {
10004				node = rng.item(0);
10005				rng = self.win.document.body.createTextRange();
10006				rng.moveToElementText(node);
10007			}
10008
10009			rng.collapse(!!to_start);
10010			self.setRng(rng);
10011		},
10012
10013		getSel : function() {
10014			var t = this, w = this.win;
10015
10016			return w.getSelection ? w.getSelection() : w.document.selection;
10017		},
10018
10019		getRng : function(w3c) {
10020			var self = this, selection, rng, elm, doc = self.win.document;
10021
10022			// Workaround for IE 11 not being able to select images properly see #6613 see quirk fix
10023			if (self.fakeRng) {
10024				return self.fakeRng;
10025			}
10026
10027			// Found tridentSel object then we need to use that one
10028			if (w3c && self.tridentSel) {
10029				return self.tridentSel.getRangeAt(0);
10030			}
10031
10032			try {
10033				if (selection = self.getSel()) {
10034					rng = selection.rangeCount > 0 ? selection.getRangeAt(0) : (selection.createRange ? selection.createRange() : doc.createRange());
10035				}
10036			} catch (ex) {
10037				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
10038			}
10039
10040			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
10041			if (tinymce.isIE && ! tinymce.isIE11 && rng && rng.setStart && doc.selection.createRange().item) {
10042				elm = doc.selection.createRange().item(0);
10043				rng = doc.createRange();
10044				rng.setStartBefore(elm);
10045				rng.setEndAfter(elm);
10046			}
10047
10048			// No range found then create an empty one
10049			// This can occur when the editor is placed in a hidden container element on Gecko
10050			// Or on IE when there was an exception
10051			if (!rng) {
10052				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
10053			}
10054
10055			// If range is at start of document then move it to start of body
10056			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
10057				elm = self.dom.getRoot();
10058				rng.setStart(elm, 0);
10059				rng.setEnd(elm, 0);
10060			}
10061
10062			if (self.selectedRange && self.explicitRange) {
10063				if (rng.compareBoundaryPoints(rng.START_TO_START, self.selectedRange) === 0 && rng.compareBoundaryPoints(rng.END_TO_END, self.selectedRange) === 0) {
10064					// Safari, Opera and Chrome only ever select text which causes the range to change.
10065					// This lets us use the originally set range if the selection hasn't been changed by the user.
10066					rng = self.explicitRange;
10067				} else {
10068					self.selectedRange = null;
10069					self.explicitRange = null;
10070				}
10071			}
10072
10073			return rng;
10074		},
10075
10076		setRng : function(r, forward) {
10077			var s, t = this;
10078
10079			if (!t.tridentSel) {
10080				s = t.getSel();
10081
10082				if (s) {
10083					t.explicitRange = r;
10084
10085					try {
10086						s.removeAllRanges();
10087					} catch (ex) {
10088						// IE9 might throw errors here don't know why
10089					}
10090
10091					s.addRange(r);
10092
10093					// Forward is set to false and we have an extend function
10094					if (forward === false && s.extend) {
10095						s.collapse(r.endContainer, r.endOffset);
10096						s.extend(r.startContainer, r.startOffset);
10097					}
10098
10099					// adding range isn't always successful so we need to check range count otherwise an exception can occur
10100					t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;
10101				}
10102			} else {
10103				// Is W3C Range
10104				if (r.cloneRange) {
10105					try {
10106						t.tridentSel.addRange(r);
10107						return;
10108					} catch (ex) {
10109						//IE9 throws an error here if called before selection is placed in the editor
10110					}
10111				}
10112
10113				// Is IE specific range
10114				try {
10115					r.select();
10116				} catch (ex) {
10117					// Needed for some odd IE bug #1843306
10118				}
10119			}
10120		},
10121
10122		setNode : function(n) {
10123			var t = this;
10124
10125			t.setContent(t.dom.getOuterHTML(n));
10126
10127			return n;
10128		},
10129
10130		getNode : function() {
10131			var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;
10132
10133			function skipEmptyTextNodes(n, forwards) {
10134				var orig = n;
10135				while (n && n.nodeType === 3 && n.length === 0) {
10136					n = forwards ? n.nextSibling : n.previousSibling;
10137				}
10138				return n || orig;
10139			};
10140
10141			// Range maybe lost after the editor is made visible again
10142			if (!rng)
10143				return t.dom.getRoot();
10144
10145			if (rng.setStart) {
10146				elm = rng.commonAncestorContainer;
10147
10148				// Handle selection a image or other control like element such as anchors
10149				if (!rng.collapsed) {
10150					if (rng.startContainer == rng.endContainer) {
10151						if (rng.endOffset - rng.startOffset < 2) {
10152							if (rng.startContainer.hasChildNodes())
10153								elm = rng.startContainer.childNodes[rng.startOffset];
10154						}
10155					}
10156
10157					// If the anchor node is a element instead of a text node then return this element
10158					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
10159					//	return sel.anchorNode.childNodes[sel.anchorOffset];
10160
10161					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
10162					// This happens when you double click an underlined word in FireFox.
10163					if (start.nodeType === 3 && end.nodeType === 3) {
10164						if (start.length === rng.startOffset) {
10165							start = skipEmptyTextNodes(start.nextSibling, true);
10166						} else {
10167							start = start.parentNode;
10168						}
10169						if (rng.endOffset === 0) {
10170							end = skipEmptyTextNodes(end.previousSibling, false);
10171						} else {
10172							end = end.parentNode;
10173						}
10174
10175						if (start && start === end)
10176							return start;
10177					}
10178				}
10179
10180				if (elm && elm.nodeType == 3)
10181					return elm.parentNode;
10182
10183				return elm;
10184			}
10185
10186			return rng.item ? rng.item(0) : rng.parentElement();
10187		},
10188
10189		getSelectedBlocks : function(st, en) {
10190			var t = this, dom = t.dom, sb, eb, n, bl = [];
10191
10192			sb = dom.getParent(st || t.getStart(), dom.isBlock);
10193			eb = dom.getParent(en || t.getEnd(), dom.isBlock);
10194
10195			if (sb)
10196				bl.push(sb);
10197
10198			if (sb && eb && sb != eb) {
10199				n = sb;
10200
10201				var walker = new TreeWalker(sb, dom.getRoot());
10202				while ((n = walker.next()) && n != eb) {
10203					if (dom.isBlock(n))
10204						bl.push(n);
10205				}
10206			}
10207
10208			if (eb && sb != eb)
10209				bl.push(eb);
10210
10211			return bl;
10212		},
10213
10214		isForward: function(){
10215			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
10216
10217			// No support for selection direction then always return true
10218			if (!sel || sel.anchorNode == null || sel.focusNode == null) {
10219				return true;
10220			}
10221
10222			anchorRange = dom.createRng();
10223			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
10224			anchorRange.collapse(true);
10225
10226			focusRange = dom.createRng();
10227			focusRange.setStart(sel.focusNode, sel.focusOffset);
10228			focusRange.collapse(true);
10229
10230			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
10231		},
10232
10233		normalize : function() {
10234			var self = this, rng, normalized, collapsed, node, sibling;
10235
10236			function normalizeEndPoint(start) {
10237				var container, offset, walker, dom = self.dom, body = dom.getRoot(), node, nonEmptyElementsMap, nodeName;
10238
10239				function hasBrBeforeAfter(node, left) {
10240					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
10241
10242					while (node = walker[left ? 'prev' : 'next']()) {
10243						if (node.nodeName === "BR") {
10244							return true;
10245						}
10246					}
10247				};
10248
10249				// Walks the dom left/right to find a suitable text node to move the endpoint into
10250				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
10251				function findTextNodeRelative(left, startNode) {
10252					var walker, lastInlineElement;
10253
10254					startNode = startNode || container;
10255					walker = new TreeWalker(startNode, dom.getParent(startNode.parentNode, dom.isBlock) || body);
10256
10257					// Walk left until we hit a text node we can move to or a block/br/img
10258					while (node = walker[left ? 'prev' : 'next']()) {
10259						// Found text node that has a length
10260						if (node.nodeType === 3 && node.nodeValue.length > 0) {
10261							container = node;
10262							offset = left ? node.nodeValue.length : 0;
10263							normalized = true;
10264							return;
10265						}
10266
10267						// Break if we find a block or a BR/IMG/INPUT etc
10268						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
10269							return;
10270						}
10271
10272						lastInlineElement = node;
10273					}
10274
10275					// Only fetch the last inline element when in caret mode for now
10276					if (collapsed && lastInlineElement) {
10277						container = lastInlineElement;
10278						normalized = true;
10279						offset = 0;
10280					}
10281				};
10282
10283				container = rng[(start ? 'start' : 'end') + 'Container'];
10284				offset = rng[(start ? 'start' : 'end') + 'Offset'];
10285				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
10286
10287				// If the container is a document move it to the body element
10288				if (container.nodeType === 9) {
10289					container = dom.getRoot();
10290					offset = 0;
10291				}
10292
10293				// If the container is body try move it into the closest text node or position
10294				if (container === body) {
10295					// If start is before/after a image, table etc
10296					if (start) {
10297						node = container.childNodes[offset > 0 ? offset - 1 : 0];
10298						if (node) {
10299							nodeName = node.nodeName.toLowerCase();
10300							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
10301								return;
10302							}
10303						}
10304					}
10305
10306					// Resolve the index
10307					if (container.hasChildNodes()) {
10308						container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];
10309						offset = 0;
10310
10311						// Don't walk into elements that doesn't have any child nodes like a IMG
10312						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
10313							// Walk the DOM to find a text node to place the caret at or a BR
10314							node = container;
10315							walker = new TreeWalker(container, body);
10316
10317							do {
10318								// Found a text node use that position
10319								if (node.nodeType === 3 && node.nodeValue.length > 0) {
10320									offset = start ? 0 : node.nodeValue.length;
10321									container = node;
10322									normalized = true;
10323									break;
10324								}
10325
10326								// Found a BR/IMG element that we can place the caret before
10327								if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
10328									offset = dom.nodeIndex(node);
10329									container = node.parentNode;
10330
10331									// Put caret after image when moving the end point
10332									if (node.nodeName ==  "IMG" && !start) {
10333										offset++;
10334									}
10335
10336									normalized = true;
10337									break;
10338								}
10339							} while (node = (start ? walker.next() : walker.prev()));
10340						}
10341					}
10342				}
10343
10344				// Lean the caret to the left if possible
10345				if (collapsed) {
10346					// So this: <b>x</b><i>|x</i>
10347					// Becomes: <b>x|</b><i>x</i>
10348					// Seems that only gecko has issues with this
10349					if (container.nodeType === 3 && offset === 0) {
10350						findTextNodeRelative(true);
10351					}
10352
10353					// Lean left into empty inline elements when the caret is before a BR
10354					// So this: <i><b></b><i>|<br></i>
10355					// Becomes: <i><b>|</b><i><br></i>
10356					// Seems that only gecko has issues with this
10357					if (container.nodeType === 1) {
10358						node = container.childNodes[offset];
10359						if(node && node.nodeName === 'BR' && !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
10360							findTextNodeRelative(true, container.childNodes[offset]);
10361						}
10362					}
10363				}
10364
10365				// Lean the start of the selection right if possible
10366				// So this: x[<b>x]</b>
10367				// Becomes: x<b>[x]</b>
10368				if (start && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
10369					findTextNodeRelative(false);
10370				}
10371
10372				// Set endpoint if it was normalized
10373				if (normalized)
10374					rng['set' + (start ? 'Start' : 'End')](container, offset);
10375			};
10376
10377			// Normalize only on non IE browsers for now
10378			if (tinymce.isIE)
10379				return;
10380			
10381			rng = self.getRng();
10382			collapsed = rng.collapsed;
10383
10384			// Normalize the end points
10385			normalizeEndPoint(true);
10386
10387			if (!collapsed)
10388				normalizeEndPoint();
10389
10390			// Set the selection if it was normalized
10391			if (normalized) {
10392				// If it was collapsed then make sure it still is
10393				if (collapsed) {
10394					rng.collapse(true);
10395				}
10396
10397				//console.log(self.dom.dumpRng(rng));
10398				self.setRng(rng, self.isForward());
10399			}
10400		},
10401
10402		selectorChanged: function(selector, callback) {
10403			var self = this, currentSelectors;
10404
10405			if (!self.selectorChangedData) {
10406				self.selectorChangedData = {};
10407				currentSelectors = {};
10408
10409				self.editor.onNodeChange.addToTop(function(ed, cm, node) {
10410					var dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
10411
10412					// Check for new matching selectors
10413					each(self.selectorChangedData, function(callbacks, selector) {
10414						each(parents, function(node) {
10415							if (dom.is(node, selector)) {
10416								if (!currentSelectors[selector]) {
10417									// Execute callbacks
10418									each(callbacks, function(callback) {
10419										callback(true, {node: node, selector: selector, parents: parents});
10420									});
10421
10422									currentSelectors[selector] = callbacks;
10423								}
10424
10425								matchedSelectors[selector] = callbacks;
10426								return false;
10427							}
10428						});
10429					});
10430
10431					// Check if current selectors still match
10432					each(currentSelectors, function(callbacks, selector) {
10433						if (!matchedSelectors[selector]) {
10434							delete currentSelectors[selector];
10435
10436							each(callbacks, function(callback) {
10437								callback(false, {node: node, selector: selector, parents: parents});
10438							});
10439						}
10440					});
10441				});
10442			}
10443
10444			// Add selector listeners
10445			if (!self.selectorChangedData[selector]) {
10446				self.selectorChangedData[selector] = [];
10447			}
10448
10449			self.selectorChangedData[selector].push(callback);
10450
10451			return self;
10452		},
10453
10454		scrollIntoView: function(elm) {
10455			var y, viewPort, self = this, dom = self.dom;
10456
10457			viewPort = dom.getViewPort(self.editor.getWin());
10458			y = dom.getPos(elm).y;
10459			if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
10460				self.editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25);
10461			}
10462		},
10463
10464		destroy : function(manual) {
10465			var self = this;
10466
10467			self.win = null;
10468
10469			// Manual destroy then remove unload handler
10470			if (!manual)
10471				tinymce.removeUnload(self.destroy);
10472		},
10473
10474		// IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode
10475		_fixIESelection : function() {
10476			var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;
10477
10478			// Return range from point or null if it failed
10479			function rngFromPoint(x, y) {
10480				var rng = body.createTextRange();
10481
10482				try {
10483					rng.moveToPoint(x, y);
10484				} catch (ex) {
10485					// IE sometimes throws and exception, so lets just ignore it
10486					rng = null;
10487				}
10488
10489				return rng;
10490			};
10491
10492			// Fires while the selection is changing
10493			function selectionChange(e) {
10494				var pointRng;
10495
10496				// Check if the button is down or not
10497				if (e.button) {
10498					// Create range from mouse position
10499					pointRng = rngFromPoint(e.x, e.y);
10500
10501					if (pointRng) {
10502						// Check if pointRange is before/after selection then change the endPoint
10503						if (pointRng.compareEndPoints('StartToStart', startRng) > 0)
10504							pointRng.setEndPoint('StartToStart', startRng);
10505						else
10506							pointRng.setEndPoint('EndToEnd', startRng);
10507
10508						pointRng.select();
10509					}
10510				} else
10511					endSelection();
10512			}
10513
10514			// Removes listeners
10515			function endSelection() {
10516				var rng = doc.selection.createRange();
10517
10518				// If the range is collapsed then use the last start range
10519				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0)
10520					startRng.select();
10521
10522				dom.unbind(doc, 'mouseup', endSelection);
10523				dom.unbind(doc, 'mousemove', selectionChange);
10524				startRng = started = 0;
10525			};
10526
10527			// Make HTML element unselectable since we are going to handle selection by hand
10528			doc.documentElement.unselectable = true;
10529			
10530			// Detect when user selects outside BODY
10531			dom.bind(doc, ['mousedown', 'contextmenu'], function(e) {
10532				if (e.target.nodeName === 'HTML') {
10533					if (started)
10534						endSelection();
10535
10536					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
10537					htmlElm = doc.documentElement;
10538					if (htmlElm.scrollHeight > htmlElm.clientHeight)
10539						return;
10540
10541					started = 1;
10542					// Setup start position
10543					startRng = rngFromPoint(e.x, e.y);
10544					if (startRng) {
10545						// Listen for selection change events
10546						dom.bind(doc, 'mouseup', endSelection);
10547						dom.bind(doc, 'mousemove', selectionChange);
10548
10549						dom.win.focus();
10550						startRng.select();
10551					}
10552				}
10553			});
10554		}
10555	});
10556})(tinymce);
10557
10558(function(tinymce) {
10559	tinymce.dom.Serializer = function(settings, dom, schema) {
10560		var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;
10561
10562		// Support the old apply_source_formatting option
10563		if (!settings.apply_source_formatting)
10564			settings.indent = false;
10565
10566		// Default DOM and Schema if they are undefined
10567		dom = dom || tinymce.DOM;
10568		schema = schema || new tinymce.html.Schema(settings);
10569		settings.entity_encoding = settings.entity_encoding || 'named';
10570		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
10571
10572		onPreProcess = new tinymce.util.Dispatcher(self);
10573
10574		onPostProcess = new tinymce.util.Dispatcher(self);
10575
10576		htmlParser = new tinymce.html.DomParser(settings, schema);
10577
10578		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
10579		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
10580			var i = nodes.length, node, value, internalName = 'data-mce-' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
10581
10582			while (i--) {
10583				node = nodes[i];
10584
10585				value = node.attributes.map[internalName];
10586				if (value !== undef) {
10587					// Set external name to internal value and remove internal
10588					node.attr(name, value.length > 0 ? value : null);
10589					node.attr(internalName, null);
10590				} else {
10591					// No internal attribute found then convert the value we have in the DOM
10592					value = node.attributes.map[name];
10593
10594					if (name === "style")
10595						value = dom.serializeStyle(dom.parseStyle(value), node.name);
10596					else if (urlConverter)
10597						value = urlConverter.call(urlConverterScope, value, name, node.name);
10598
10599					node.attr(name, value.length > 0 ? value : null);
10600				}
10601			}
10602		});
10603
10604		// Remove internal classes mceItem<..> or mceSelected
10605		htmlParser.addAttributeFilter('class', function(nodes, name) {
10606			var i = nodes.length, node, value;
10607
10608			while (i--) {
10609				node = nodes[i];
10610				value = node.attr('class').replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g, '');
10611				node.attr('class', value.length > 0 ? value : null);
10612			}
10613		});
10614
10615		// Remove bookmark elements
10616		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
10617			var i = nodes.length, node;
10618
10619			while (i--) {
10620				node = nodes[i];
10621
10622				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup)
10623					node.remove();
10624			}
10625		});
10626
10627		// Remove expando attributes
10628		htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name, args) {
10629			var i = nodes.length;
10630
10631			while (i--) {
10632				nodes[i].attr(name, null);
10633			}
10634		});
10635
10636		htmlParser.addNodeFilter('noscript', function(nodes) {
10637			var i = nodes.length, node;
10638
10639			while (i--) {
10640				node = nodes[i].firstChild;
10641
10642				if (node) {
10643					node.value = tinymce.html.Entities.decode(node.value);
10644				}
10645			}
10646		});
10647
10648		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
10649		htmlParser.addNodeFilter('script,style', function(nodes, name) {
10650			var i = nodes.length, node, value;
10651
10652			function trim(value) {
10653				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
10654						.replace(/^[\r\n]*|[\r\n]*$/g, '')
10655						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
10656						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
10657			};
10658
10659			while (i--) {
10660				node = nodes[i];
10661				value = node.firstChild ? node.firstChild.value : '';
10662
10663				if (name === "script") {
10664					// Remove mce- prefix from script elements
10665					node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));
10666
10667					if (value.length > 0)
10668						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
10669				} else {
10670					if (value.length > 0)
10671						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
10672				}
10673			}
10674		});
10675
10676		// Convert comments to cdata and handle protected comments
10677		htmlParser.addNodeFilter('#comment', function(nodes, name) {
10678			var i = nodes.length, node;
10679
10680			while (i--) {
10681				node = nodes[i];
10682
10683				if (node.value.indexOf('[CDATA[') === 0) {
10684					node.name = '#cdata';
10685					node.type = 4;
10686					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
10687				} else if (node.value.indexOf('mce:protected ') === 0) {
10688					node.name = "#text";
10689					node.type = 3;
10690					node.raw = true;
10691					node.value = unescape(node.value).substr(14);
10692				}
10693			}
10694		});
10695
10696		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
10697			var i = nodes.length, node;
10698
10699			while (i--) {
10700				node = nodes[i];
10701				if (node.type === 7)
10702					node.remove();
10703				else if (node.type === 1) {
10704					if (name === "input" && !("type" in node.attributes.map))
10705						node.attr('type', 'text');
10706				}
10707			}
10708		});
10709
10710		// Fix list elements, TODO: Replace this later
10711		if (settings.fix_list_elements) {
10712			htmlParser.addNodeFilter('ul,ol', function(nodes, name) {
10713				var i = nodes.length, node, parentNode;
10714
10715				while (i--) {
10716					node = nodes[i];
10717					parentNode = node.parent;
10718
10719					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
10720						if (node.prev && node.prev.name === 'li') {
10721							node.prev.append(node);
10722						}
10723					}
10724				}
10725			});
10726		}
10727
10728		// Remove internal data attributes
10729		htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style', function(nodes, name) {
10730			var i = nodes.length;
10731
10732			while (i--) {
10733				nodes[i].attr(name, null);
10734			}
10735		});
10736
10737		// Return public methods
10738		return {
10739			schema : schema,
10740
10741			addNodeFilter : htmlParser.addNodeFilter,
10742
10743			addAttributeFilter : htmlParser.addAttributeFilter,
10744
10745			onPreProcess : onPreProcess,
10746
10747			onPostProcess : onPostProcess,
10748
10749			serialize : function(node, args) {
10750				var impl, doc, oldDoc, htmlSerializer, content;
10751
10752				// Explorer won't clone contents of script and style and the
10753				// selected index of select elements are cleared on a clone operation.
10754				if (isIE && dom.select('script,style,select,map').length > 0) {
10755					content = node.innerHTML;
10756					node = node.cloneNode(false);
10757					dom.setHTML(node, content);
10758				} else
10759					node = node.cloneNode(true);
10760
10761				// Nodes needs to be attached to something in WebKit/Opera
10762				// Older builds of Opera crashes if you attach the node to an document created dynamically
10763				// and since we can't feature detect a crash we need to sniff the acutal build number
10764				// This fix will make DOM ranges and make Sizzle happy!
10765				try {
10766				impl = node.ownerDocument.implementation;
10767					if (impl.createHTMLDocument) {
10768						// Create an empty HTML document
10769						doc = impl.createHTMLDocument("");
10770
10771						// Add the element or it's children if it's a body element to the new document
10772						each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
10773							doc.body.appendChild(doc.importNode(node, true));
10774						});
10775
10776						// Grab first child or body element for serialization
10777						if (node.nodeName != 'BODY')
10778							node = doc.body.firstChild;
10779						else
10780							node = doc.body;
10781
10782						// set the new document in DOMUtils so createElement etc works
10783						oldDoc = dom.doc;
10784						dom.doc = doc;
10785					}
10786				} catch(err) {
10787					console.log('serialize: ' + err);
10788				}
10789				args = args || {};
10790				args.format = args.format || 'html';
10791
10792				// Pre process
10793				if (!args.no_events) {
10794					args.node = node;
10795					onPreProcess.dispatch(self, args);
10796				}
10797
10798				// Setup serializer
10799				htmlSerializer = new tinymce.html.Serializer(settings, schema);
10800
10801				// Parse and serialize HTML
10802				args.content = htmlSerializer.serialize(
10803					htmlParser.parse(tinymce.trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
10804				);
10805
10806				// Replace all BOM characters for now until we can find a better solution
10807				if (!args.cleanup){
10808					args.content = args.content.replace(/\uFEFF/g, '');
10809					// usuwam slasha z taga 
10810					args.content = args.content.replace('/>', '>');
10811				}
10812
10813				// Post process
10814				if (!args.no_events)
10815					onPostProcess.dispatch(self, args);
10816
10817				// Restore the old document if it was changed
10818				if (oldDoc)
10819					dom.doc = oldDoc;
10820
10821				args.node = null;
10822
10823				return args.content;
10824			},
10825
10826			addRules : function(rules) {
10827				schema.addValidElements(rules);
10828			},
10829
10830			setRules : function(rules) {
10831				schema.setValidElements(rules);
10832			}
10833		};
10834	};
10835})(tinymce);
10836(function(tinymce) {
10837	tinymce.dom.ScriptLoader = function(settings) {
10838		var QUEUED = 0,
10839			LOADING = 1,
10840			LOADED = 2,
10841			states = {},
10842			queue = [],
10843			scriptLoadedCallbacks = {},
10844			queueLoadedCallbacks = [],
10845			loading = 0,
10846			undef;
10847
10848		function loadScript(url, callback) {
10849			var t = this, dom = tinymce.DOM, elm, uri, loc, id;
10850
10851			// Execute callback when script is loaded
10852			function done() {
10853				dom.remove(id);
10854
10855				if (elm)
10856					elm.onreadystatechange = elm.onload = elm = null;
10857
10858				callback();
10859			};
10860			
10861			function error() {
10862				// Report the error so it's easier for people to spot loading errors
10863				if (typeof(console) !== "undefined" && console.log)
10864					console.log("Failed to load: " + url);
10865
10866				// We can't mark it as done if there is a load error since
10867				// A) We don't want to produce 404 errors on the server and
10868				// B) the onerror event won't fire on all browsers.
10869				// done();
10870			};
10871
10872			id = dom.uniqueId();
10873
10874			if (tinymce.isIE6) {
10875				uri = new tinymce.util.URI(url);
10876				loc = location;
10877
10878				// If script is from same domain and we
10879				// use IE 6 then use XHR since it's more reliable
10880				if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol && uri.protocol.toLowerCase() != 'file') {
10881					tinymce.util.XHR.send({
10882						url : tinymce._addVer(uri.getURI()),
10883						success : function(content) {
10884							// Create new temp script element
10885							var script = dom.create('script', {
10886								type : 'text/javascript'
10887							});
10888
10889							// Evaluate script in global scope
10890							script.text = content;
10891							document.getElementsByTagName('head')[0].appendChild(script);
10892							dom.remove(script);
10893
10894							done();
10895						},
10896						
10897						error : error
10898					});
10899
10900					return;
10901				}
10902			}
10903
10904			// Create new script element
10905			elm = document.createElement('script');
10906			elm.id = id;
10907			elm.type = 'text/javascript';
10908			elm.src = tinymce._addVer(url);
10909
10910			// Add onload listener for non IE browsers since IE9
10911			// fires onload event before the script is parsed and executed
10912			if (!tinymce.isIE || tinymce.isIE11)
10913				elm.onload = done;
10914
10915			// Add onerror event will get fired on some browsers but not all of them
10916			elm.onerror = error;
10917
10918			// Opera 9.60 doesn't seem to fire the onreadystate event at correctly
10919			if (!tinymce.isOpera) {
10920				elm.onreadystatechange = function() {
10921					var state = elm.readyState;
10922
10923					// Loaded state is passed on IE 6 however there
10924					// are known issues with this method but we can't use
10925					// XHR in a cross domain loading
10926					if (state == 'complete' || state == 'loaded')
10927						done();
10928				};
10929			}
10930
10931			// Most browsers support this feature so we report errors
10932			// for those at least to help users track their missing plugins etc
10933			// todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option
10934			/*elm.onerror = function() {
10935				alert('Failed to load: ' + url);
10936			};*/
10937
10938			// Add script to document
10939			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
10940		};
10941
10942		this.isDone = function(url) {
10943			return states[url] == LOADED;
10944		};
10945
10946		this.markDone = function(url) {
10947			states[url] = LOADED;
10948		};
10949
10950		this.add = this.load = function(url, callback, scope) {
10951			var item, state = states[url];
10952
10953			// Add url to load queue
10954			if (state == undef) {
10955				queue.push(url);
10956				states[url] = QUEUED;
10957			}
10958
10959			if (callback) {
10960				// Store away callback for later execution
10961				if (!scriptLoadedCallbacks[url])
10962					scriptLoadedCallbacks[url] = [];
10963
10964				scriptLoadedCallbacks[url].push({
10965					func : callback,
10966					scope : scope || this
10967				});
10968			}
10969		};
10970
10971		this.loadQueue = function(callback, scope) {
10972			this.loadScripts(queue, callback, scope);
10973		};
10974
10975		this.loadScripts = function(scripts, callback, scope) {
10976			var loadScripts;
10977
10978			function execScriptLoadedCallbacks(url) {
10979				// Execute URL callback functions
10980				tinymce.each(scriptLoadedCallbacks[url], function(callback) {
10981					callback.func.call(callback.scope);
10982				});
10983
10984				scriptLoadedCallbacks[url] = undef;
10985			};
10986
10987			queueLoadedCallbacks.push({
10988				func : callback,
10989				scope : scope || this
10990			});
10991
10992			loadScripts = function() {
10993				var loadingScripts = tinymce.grep(scripts);
10994
10995				// Current scripts has been handled
10996				scripts.length = 0;
10997
10998				// Load scripts that needs to be loaded
10999				tinymce.each(loadingScripts, function(url) {
11000					// Script is already loaded then execute script callbacks directly
11001					if (states[url] == LOADED) {
11002						execScriptLoadedCallbacks(url);
11003						return;
11004					}
11005
11006					// Is script not loading then start loading it
11007					if (states[url] != LOADING) {
11008						states[url] = LOADING;
11009						loading++;
11010
11011						loadScript(url, function() {
11012							states[url] = LOADED;
11013							loading--;
11014
11015							execScriptLoadedCallbacks(url);
11016
11017							// Load more scripts if they where added by the recently loaded script
11018							loadScripts();
11019						});
11020					}
11021				});
11022
11023				// No scripts are currently loading then execute all pending queue loaded callbacks
11024				if (!loading) {
11025					tinymce.each(queueLoadedCallbacks, function(callback) {
11026						callback.func.call(callback.scope);
11027					});
11028
11029					queueLoadedCallbacks.length = 0;
11030				}
11031			};
11032
11033			loadScripts();
11034		};
11035	};
11036
11037	// Global script loader
11038	tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
11039})(tinymce);
11040
11041(function(tinymce) {
11042	tinymce.dom.RangeUtils = function(dom) {
11043		var INVISIBLE_CHAR = '\uFEFF';
11044
11045		this.walk = function(rng, callback) {
11046			var startContainer = rng.startContainer,
11047				startOffset = rng.startOffset,
11048				endContainer = rng.endContainer,
11049				endOffset = rng.endOffset,
11050				ancestor, startPoint,
11051				endPoint, node, parent, siblings, nodes;
11052
11053			// Handle table cell selection the table plugin enables
11054			// you to fake select table cells and perform formatting actions on them
11055			nodes = dom.select('td.mceSelected,th.mceSelected');
11056			if (nodes.length > 0) {
11057				tinymce.each(nodes, function(node) {
11058					callback([node]);
11059				});
11060
11061				return;
11062			}
11063
11064			function exclude(nodes) {
11065				var node;
11066
11067				// First node is excluded
11068				node = nodes[0];
11069				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
11070					nodes.splice(0, 1);
11071				}
11072
11073				// Last node is excluded
11074				node = nodes[nodes.length - 1];
11075				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
11076					nodes.splice(nodes.length - 1, 1);
11077				}
11078
11079				return nodes;
11080			};
11081
11082			function collectSiblings(node, name, end_node) {
11083				var siblings = [];
11084
11085				for (; node && node != end_node; node = node[name])
11086					siblings.push(node);
11087
11088				return siblings;
11089			};
11090
11091			function findEndPoint(node, root) {
11092				do {
11093					if (node.parentNode == root)
11094						return node;
11095
11096					node = node.parentNode;
11097				} while(node);
11098			};
11099
11100			function walkBoundary(start_node, end_node, next) {
11101				var siblingName = next ? 'nextSibling' : 'previousSibling';
11102
11103				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
11104					parent = node.parentNode;
11105					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
11106
11107					if (siblings.length) {
11108						if (!next)
11109							siblings.reverse();
11110
11111						callback(exclude(siblings));
11112					}
11113				}
11114			};
11115
11116			// If index based start position then resolve it
11117			if (startContainer.nodeType == 1 && startContainer.hasChildNodes())
11118				startContainer = startContainer.childNodes[startOffset];
11119
11120			// If index based end position then resolve it
11121			if (endContainer.nodeType == 1 && endContainer.hasChildNodes())
11122				endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
11123
11124			// Same container
11125			if (startContainer == endContainer)
11126				return callback(exclude([startContainer]));
11127
11128			// Find common ancestor and end points
11129			ancestor = dom.findCommonAncestor(startContainer, endContainer);
11130				
11131			// Process left side
11132			for (node = startContainer; node; node = node.parentNode) {
11133				if (node === endContainer)
11134					return walkBoundary(startContainer, ancestor, true);
11135
11136				if (node === ancestor)
11137					break;
11138			}
11139
11140			// Process right side
11141			for (node = endContainer; node; node = node.parentNode) {
11142				if (node === startContainer)
11143					return walkBoundary(endContainer, ancestor);
11144
11145				if (node === ancestor)
11146					break;
11147			}
11148
11149			// Find start/end point
11150			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
11151			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
11152
11153			// Walk left leaf
11154			walkBoundary(startContainer, startPoint, true);
11155
11156			// Walk the middle from start to end point
11157			siblings = collectSiblings(
11158				startPoint == startContainer ? startPoint : startPoint.nextSibling,
11159				'nextSibling',
11160				endPoint == endContainer ? endPoint.nextSibling : endPoint
11161			);
11162
11163			if (siblings.length)
11164				callback(exclude(siblings));
11165
11166			// Walk right leaf
11167			walkBoundary(endContainer, endPoint);
11168		};
11169
11170		this.split = function(rng) {
11171			var startContainer = rng.startContainer,
11172				startOffset = rng.startOffset,
11173				endContainer = rng.endContainer,
11174				endOffset = rng.endOffset;
11175
11176			function splitText(node, offset) {
11177				return node.splitText(offset);
11178			};
11179
11180			// Handle single text node
11181			if (startContainer == endContainer && startContainer.nodeType == 3) {
11182				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
11183					endContainer = splitText(startContainer, startOffset);
11184					startContainer = endContainer.previousSibling;
11185
11186					if (endOffset > startOffset) {
11187						endOffset = endOffset - startOffset;
11188						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
11189						endOffset = endContainer.nodeValue.length;
11190						startOffset = 0;
11191					} else {
11192						endOffset = 0;
11193					}
11194				}
11195			} else {
11196				// Split startContainer text node if needed
11197				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
11198					startContainer = splitText(startContainer, startOffset);
11199					startOffset = 0;
11200				}
11201
11202				// Split endContainer text node if needed
11203				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
11204					endContainer = splitText(endContainer, endOffset).previousSibling;
11205					endOffset = endContainer.nodeValue.length;
11206				}
11207			}
11208
11209			return {
11210				startContainer : startContainer,
11211				startOffset : startOffset,
11212				endContainer : endContainer,
11213				endOffset : endOffset
11214			};
11215		};
11216
11217	};
11218
11219	tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {
11220		if (rng1 && rng2) {
11221			// Compare native IE ranges
11222			if (rng1.item || rng1.duplicate) {
11223				// Both are control ranges and the selected element matches
11224				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
11225					return true;
11226
11227				// Both are text ranges and the range matches
11228				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))
11229					return true;
11230			} else {
11231				// Compare w3c ranges
11232				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
11233			}
11234		}
11235
11236		return false;
11237	};
11238})(tinymce);
11239
11240(function(tinymce) {
11241	var Event = tinymce.dom.Event, each = tinymce.each;
11242
11243	tinymce.create('tinymce.ui.KeyboardNavigation', {
11244		KeyboardNavigation: function(settings, dom) {
11245			var t = this, root = settings.root, items = settings.items,
11246					enableUpDown = settings.enableUpDown, enableLeftRight = settings.enableLeftRight || !settings.enableUpDown,
11247					excludeFromTabOrder = settings.excludeFromTabOrder,
11248					itemFocussed, itemBlurred, rootKeydown, rootFocussed, focussedId;
11249
11250			dom = dom || tinymce.DOM;
11251
11252			itemFocussed = function(evt) {
11253				focussedId = evt.target.id;
11254			};
11255			
11256			itemBlurred = function(evt) {
11257				dom.setAttrib(evt.target.id, 'tabindex', '-1');
11258			};
11259			
11260			rootFocussed = function(evt) {
11261				var item = dom.get(focussedId);
11262				dom.setAttrib(item, 'tabindex', '0');
11263				item.focus();
11264			};
11265			
11266			t.focus = function() {
11267				dom.get(focussedId).focus();
11268			};
11269
11270			t.destroy = function() {
11271				each(items, function(item) {
11272					var elm = dom.get(item.id);
11273
11274					dom.unbind(elm, 'focus', itemFocussed);
11275					dom.unbind(elm, 'blur', itemBlurred);
11276				});
11277
11278				var rootElm = dom.get(root);
11279				dom.unbind(rootElm, 'focus', rootFocussed);
11280				dom.unbind(rootElm, 'keydown', rootKeydown);
11281
11282				items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;
11283				t.destroy = function() {};
11284			};
11285			
11286			t.moveFocus = function(dir, evt) {
11287				var idx = -1, controls = t.controls, newFocus;
11288
11289				if (!focussedId)
11290					return;
11291
11292				each(items, function(item, index) {
11293					if (item.id === focussedId) {
11294						idx = index;
11295						return false;
11296					}
11297				});
11298
11299				idx += dir;
11300				if (idx < 0) {
11301					idx = items.length - 1;
11302				} else if (idx >= items.length) {
11303					idx = 0;
11304				}
11305				
11306				newFocus = items[idx];
11307				dom.setAttrib(focussedId, 'tabindex', '-1');
11308				dom.setAttrib(newFocus.id, 'tabindex', '0');
11309				dom.get(newFocus.id).focus();
11310
11311				if (settings.actOnFocus) {
11312					settings.onAction(newFocus.id);
11313				}
11314
11315				if (evt)
11316					Event.cancel(evt);
11317			};
11318			
11319			rootKeydown = function(evt) {
11320				var DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40, DOM_VK_ESCAPE = 27, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32;
11321				
11322				switch (evt.keyCode) {
11323					case DOM_VK_LEFT:
11324						if (enableLeftRight) t.moveFocus(-1);
11325						Event.cancel(evt);
11326						break;
11327	
11328					case DOM_VK_RIGHT:
11329						if (enableLeftRight) t.moveFocus(1);
11330						Event.cancel(evt);
11331						break;
11332	
11333					case DOM_VK_UP:
11334						if (enableUpDown) t.moveFocus(-1);
11335						Event.cancel(evt);
11336						break;
11337
11338					case DOM_VK_DOWN:
11339						if (enableUpDown) t.moveFocus(1);
11340						Event.cancel(evt);
11341						break;
11342
11343					case DOM_VK_ESCAPE:
11344						if (settings.onCancel) {
11345							settings.onCancel();
11346							Event.cancel(evt);
11347						}
11348						break;
11349
11350					case DOM_VK_ENTER:
11351					case DOM_VK_RETURN:
11352					case DOM_VK_SPACE:
11353						if (settings.onAction) {
11354							settings.onAction(focussedId);
11355							Event.cancel(evt);
11356						}
11357						break;
11358				}
11359			};
11360
11361			// Set up state and listeners for each item.
11362			each(items, function(item, idx) {
11363				var tabindex, elm;
11364
11365				if (!item.id) {
11366					item.id = dom.uniqueId('_mce_item_');
11367				}
11368
11369				elm = dom.get(item.id);
11370
11371				if (excludeFromTabOrder) {
11372					dom.bind(elm, 'blur', itemBlurred);
11373					tabindex = '-1';
11374				} else {
11375					tabindex = (idx === 0 ? '0' : '-1');
11376				}
11377
11378				elm.setAttribute('tabindex', tabindex);
11379				dom.bind(elm, 'focus', itemFocussed);
11380			});
11381			
11382			// Setup initial state for root element.
11383			if (items[0]){
11384				focussedId = items[0].id;
11385			}
11386
11387			dom.setAttrib(root, 'tabindex', '-1');
11388
11389			// Setup listeners for root element.
11390			var rootElm = dom.get(root);
11391			dom.bind(rootElm, 'focus', rootFocussed);
11392			dom.bind(rootElm, 'keydown', rootKeydown);
11393		}
11394	});
11395})(tinymce);
11396
11397(function(tinymce) {
11398	// Shorten class names
11399	var DOM = tinymce.DOM, is = tinymce.is;
11400
11401	tinymce.create('tinymce.ui.Control', {
11402		Control : function(id, s, editor) {
11403			this.id = id;
11404			this.settings = s = s || {};
11405			this.rendered = false;
11406			this.onRender = new tinymce.util.Dispatcher(this);
11407			this.classPrefix = '';
11408			this.scope = s.scope || this;
11409			this.disabled = 0;
11410			this.active = 0;
11411			this.editor = editor;
11412		},
11413		
11414		setAriaProperty : function(property, value) {
11415			var element = DOM.get(this.id + '_aria') || DOM.get(this.id);
11416			if (element) {
11417				DOM.setAttrib(element, 'aria-' + property, !!value);
11418			}
11419		},
11420		
11421		focus : function() {
11422			DOM.get(this.id).focus();
11423		},
11424
11425		setDisabled : function(s) {
11426			if (s != this.disabled) {
11427				this.setAriaProperty('disabled', s);
11428
11429				this.setState('Disabled', s);
11430				this.setState('Enabled', !s);
11431				this.disabled = s;
11432			}
11433		},
11434
11435		isDisabled : function() {
11436			return this.disabled;
11437		},
11438
11439		setActive : function(s) {
11440			if (s != this.active) {
11441				this.setState('Active', s);
11442				this.active = s;
11443				this.setAriaProperty('pressed', s);
11444			}
11445		},
11446
11447		isActive : function() {
11448			return this.active;
11449		},
11450
11451		setState : function(c, s) {
11452			var n = DOM.get(this.id);
11453
11454			c = this.classPrefix + c;
11455
11456			if (s)
11457				DOM.addClass(n, c);
11458			else
11459				DOM.removeClass(n, c);
11460		},
11461
11462		isRendered : function() {
11463			return this.rendered;
11464		},
11465
11466		renderHTML : function() {
11467		},
11468
11469		renderTo : function(n) {
11470			DOM.setHTML(n, this.renderHTML());
11471		},
11472
11473		postRender : function() {
11474			var t = this, b;
11475
11476			// Set pending states
11477			if (is(t.disabled)) {
11478				b = t.disabled;
11479				t.disabled = -1;
11480				t.setDisabled(b);
11481			}
11482
11483			if (is(t.active)) {
11484				b = t.active;
11485				t.active = -1;
11486				t.setActive(b);
11487			}
11488		},
11489
11490		remove : function() {
11491			DOM.remove(this.id);
11492			this.destroy();
11493		},
11494
11495		destroy : function() {
11496			tinymce.dom.Event.clear(this.id);
11497		}
11498	});
11499})(tinymce);
11500tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
11501	Container : function(id, s, editor) {
11502		this.parent(id, s, editor);
11503
11504		this.controls = [];
11505
11506		this.lookup = {};
11507	},
11508
11509	add : function(c) {
11510		this.lookup[c.id] = c;
11511		this.controls.push(c);
11512
11513		return c;
11514	},
11515
11516	get : function(n) {
11517		return this.lookup[n];
11518	}
11519});
11520
11521
11522tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
11523	Separator : function(id, s) {
11524		this.parent(id, s);
11525		this.classPrefix = 'mceSeparator';
11526		this.setDisabled(true);
11527	},
11528
11529	renderHTML : function() {
11530		return tinymce.DOM.createHTML('span', {'class' : this.classPrefix, role : 'separator', 'aria-orientation' : 'vertical', tabindex : '-1'});
11531	}
11532});
11533
11534(function(tinymce) {
11535	var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
11536
11537	tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {
11538		MenuItem : function(id, s) {
11539			this.parent(id, s);
11540			this.classPrefix = 'mceMenuItem';
11541		},
11542
11543		setSelected : function(s) {
11544			this.setState('Selected', s);
11545			this.setAriaProperty('checked', !!s);
11546			this.selected = s;
11547		},
11548
11549		isSelected : function() {
11550			return this.selected;
11551		},
11552
11553		postRender : function() {
11554			var t = this;
11555			
11556			t.parent();
11557
11558			// Set pending state
11559			if (is(t.selected))
11560				t.setSelected(t.selected);
11561		}
11562	});
11563})(tinymce);
11564
11565(function(tinymce) {
11566	var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
11567
11568	tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {
11569		Menu : function(id, s) {
11570			var t = this;
11571
11572			t.parent(id, s);
11573			t.items = {};
11574			t.collapsed = false;
11575			t.menuCount = 0;
11576			t.onAddItem = new tinymce.util.Dispatcher(this);
11577		},
11578
11579		expand : function(d) {
11580			var t = this;
11581
11582			if (d) {
11583				walk(t, function(o) {
11584					if (o.expand)
11585						o.expand();
11586				}, 'items', t);
11587			}
11588
11589			t.collapsed = false;
11590		},
11591
11592		collapse : function(d) {
11593			var t = this;
11594
11595			if (d) {
11596				walk(t, function(o) {
11597					if (o.collapse)
11598						o.collapse();
11599				}, 'items', t);
11600			}
11601
11602			t.collapsed = true;
11603		},
11604
11605		isCollapsed : function() {
11606			return this.collapsed;
11607		},
11608
11609		add : function(o) {
11610			if (!o.settings)
11611				o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);
11612
11613			this.onAddItem.dispatch(this, o);
11614
11615			return this.items[o.id] = o;
11616		},
11617
11618		addSeparator : function() {
11619			return this.add({separator : true});
11620		},
11621
11622		addMenu : function(o) {
11623			if (!o.collapse)
11624				o = this.createMenu(o);
11625
11626			this.menuCount++;
11627
11628			return this.add(o);
11629		},
11630
11631		hasMenus : function() {
11632			return this.menuCount !== 0;
11633		},
11634
11635		remove : function(o) {
11636			delete this.items[o.id];
11637		},
11638
11639		removeAll : function() {
11640			var t = this;
11641
11642			walk(t, function(o) {
11643				if (o.removeAll)
11644					o.removeAll();
11645				else
11646					o.remove();
11647
11648				o.destroy();
11649			}, 'items', t);
11650
11651			t.items = {};
11652		},
11653
11654		createMenu : function(o) {
11655			var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);
11656
11657			m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);
11658
11659			return m;
11660		}
11661	});
11662})(tinymce);
11663(function(tinymce) {
11664	var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;
11665
11666	tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {
11667		DropMenu : function(id, s) {
11668			s = s || {};
11669			s.container = s.container || DOM.doc.body;
11670			s.offset_x = s.offset_x || 0;
11671			s.offset_y = s.offset_y || 0;
11672			s.vp_offset_x = s.vp_offset_x || 0;
11673			s.vp_offset_y = s.vp_offset_y || 0;
11674
11675			if (is(s.icons) && !s.icons)
11676				s['class'] += ' mceNoIcons';
11677
11678			this.parent(id, s);
11679			this.onShowMenu = new tinymce.util.Dispatcher(this);
11680			this.onHideMenu = new tinymce.util.Dispatcher(this);
11681			this.classPrefix = 'mceMenu';
11682		},
11683
11684		createMenu : function(s) {
11685			var t = this, cs = t.settings, m;
11686
11687			s.container = s.container || cs.container;
11688			s.parent = t;
11689			s.constrain = s.constrain || cs.constrain;
11690			s['class'] = s['class'] || cs['class'];
11691			s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
11692			s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
11693			s.keyboard_focus = cs.keyboard_focus;
11694			m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);
11695
11696			m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);
11697
11698			return m;
11699		},
11700		
11701		focus : function() {
11702			var t = this;
11703			if (t.keyboardNav) {
11704				t.keyboardNav.focus();
11705			}
11706		},
11707
11708		update : function() {
11709			var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
11710
11711			tw = s.max_width ? Math.min(tb.offsetWidth, s.max_width) : tb.offsetWidth;
11712			th = s.max_height ? Math.min(tb.offsetHeight, s.max_height) : tb.offsetHeight;
11713
11714			if (!DOM.boxModel)
11715				t.element.setStyles({width : tw + 2, height : th + 2});
11716			else
11717				t.element.setStyles({width : tw, height : th});
11718
11719			if (s.max_width)
11720				DOM.setStyle(co, 'width', tw);
11721
11722			if (s.max_height) {
11723				DOM.setStyle(co, 'height', th);
11724
11725				if (tb.clientHeight < s.max_height)
11726					DOM.setStyle(co, 'overflow', 'hidden');
11727			}
11728		},
11729
11730		showMenu : function(x, y, px) {
11731			var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;
11732
11733			t.collapse(1);
11734
11735			if (t.isMenuVisible)
11736				return;
11737
11738			if (!t.rendered) {
11739				co = DOM.add(t.settings.container, t.renderNode());
11740
11741				each(t.items, function(o) {
11742					o.postRender();
11743				});
11744
11745				t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
11746			} else
11747				co = DOM.get('menu_' + t.id);
11748
11749			// Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug
11750			if (!tinymce.isOpera)
11751				DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});
11752
11753			DOM.show(co);
11754			t.update();
11755
11756			x += s.offset_x || 0;
11757			y += s.offset_y || 0;
11758			vp.w -= 4;
11759			vp.h -= 4;
11760
11761			// Move inside viewport if not submenu
11762			if (s.constrain) {
11763				w = co.clientWidth - ot;
11764				h = co.clientHeight - ot;
11765				mx = vp.x + vp.w;
11766				my = vp.y + vp.h;
11767
11768				if ((x + s.vp_offset_x + w) > mx)
11769					x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);
11770
11771				if ((y + s.vp_offset_y + h) > my)
11772					y = Math.max(0, (my - s.vp_offset_y) - h);
11773			}
11774
11775			DOM.setStyles(co, {left : x , top : y});
11776			t.element.update();
11777
11778			t.isMenuVisible = 1;
11779			t.mouseClickFunc = Event.add(co, 'click', function(e) {
11780				var m;
11781
11782				e = e.target;
11783
11784				if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {
11785					m = t.items[e.id];
11786
11787					if (m.isDisabled())
11788						return;
11789
11790					dm = t;
11791
11792					while (dm) {
11793						if (dm.hideMenu)
11794							dm.hideMenu();
11795
11796						dm = dm.settings.parent;
11797					}
11798
11799					if (m.settings.onclick)
11800						m.settings.onclick(e);
11801
11802					return false; // Cancel to fix onbeforeunload problem
11803				}
11804			});
11805
11806			if (t.hasMenus()) {
11807				t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
11808					var m, r, mi;
11809
11810					e = e.target;
11811					if (e && (e = DOM.getParent(e, 'tr'))) {
11812						m = t.items[e.id];
11813
11814						if (t.lastMenu)
11815							t.lastMenu.collapse(1);
11816
11817						if (m.isDisabled())
11818							return;
11819
11820						if (e && DOM.hasClass(e, cp + 'ItemSub')) {
11821							//p = DOM.getPos(s.container);
11822							r = DOM.getRect(e);
11823							m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
11824							t.lastMenu = m;
11825							DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');
11826						}
11827					}
11828				});
11829			}
11830			
11831			Event.add(co, 'keydown', t._keyHandler, t);
11832
11833			t.onShowMenu.dispatch(t);
11834
11835			if (s.keyboard_focus) { 
11836				t._setupKeyboardNav(); 
11837			}
11838		},
11839
11840		hideMenu : function(c) {
11841			var t = this, co = DOM.get('menu_' + t.id), e;
11842
11843			if (!t.isMenuVisible)
11844				return;
11845
11846			if (t.keyboardNav) t.keyboardNav.destroy();
11847			Event.remove(co, 'mouseover', t.mouseOverFunc);
11848			Event.remove(co, 'click', t.mouseClickFunc);
11849			Event.remove(co, 'keydown', t._keyHandler);
11850			DOM.hide(co);
11851			t.isMenuVisible = 0;
11852
11853			if (!c)
11854				t.collapse(1);
11855
11856			if (t.element)
11857				t.element.hide();
11858
11859			if (e = DOM.get(t.id))
11860				DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');
11861
11862			t.onHideMenu.dispatch(t);
11863		},
11864
11865		add : function(o) {
11866			var t = this, co;
11867
11868			o = t.parent(o);
11869
11870			if (t.isRendered && (co = DOM.get('menu_' + t.id)))
11871				t._add(DOM.select('tbody', co)[0], o);
11872
11873			return o;
11874		},
11875
11876		collapse : function(d) {
11877			this.parent(d);
11878			this.hideMenu(1);
11879		},
11880
11881		remove : function(o) {
11882			DOM.remove(o.id);
11883			this.destroy();
11884
11885			return this.parent(o);
11886		},
11887
11888		destroy : function() {
11889			var t = this, co = DOM.get('menu_' + t.id);
11890
11891			if (t.keyboardNav) t.keyboardNav.destroy();
11892			Event.remove(co, 'mouseover', t.mouseOverFunc);
11893			Event.remove(DOM.select('a', co), 'focus', t.mouseOverFunc);
11894			Event.remove(co, 'click', t.mouseClickFunc);
11895			Event.remove(co, 'keydown', t._keyHandler);
11896
11897			if (t.element)
11898				t.element.remove();
11899
11900			DOM.remove(co);
11901		},
11902
11903		renderNode : function() {
11904			var t = this, s = t.settings, n, tb, co, w;
11905
11906			w = DOM.create('div', {role: 'listbox', id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000;outline:0'});
11907			if (t.settings.parent) {
11908				DOM.setAttrib(w, 'aria-parent', 'menu_' + t.settings.parent.id);
11909			}
11910			co = DOM.add(w, 'div', {role: 'presentation', id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});
11911			t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
11912
11913			if (s.menu_line)
11914				DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});
11915
11916//			n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
11917			n = DOM.add(co, 'table', {role: 'presentation', id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});
11918			tb = DOM.add(n, 'tbody');
11919
11920			each(t.items, function(o) {
11921				t._add(tb, o);
11922			});
11923
11924			t.rendered = true;
11925
11926			return w;
11927		},
11928
11929		// Internal functions
11930		_setupKeyboardNav : function(){
11931			var contextMenu, menuItems, t=this; 
11932			contextMenu = DOM.get('menu_' + t.id);
11933			menuItems = DOM.select('a[role=option]', 'menu_' + t.id);
11934			menuItems.splice(0,0,contextMenu);
11935			t.keyboardNav = new tinymce.ui.KeyboardNavigation({
11936				root: 'menu_' + t.id,
11937				items: menuItems,
11938				onCancel: function() {
11939					t.hideMenu();
11940				},
11941				enableUpDown: true
11942			});
11943			contextMenu.focus();
11944		},
11945
11946		_keyHandler : function(evt) {
11947			var t = this, e;
11948			switch (evt.keyCode) {
11949				case 37: // Left
11950					if (t.settings.parent) {
11951						t.hideMenu();
11952						t.settings.parent.focus();
11953						Event.cancel(evt);
11954					}
11955					break;
11956				case 39: // Right
11957					if (t.mouseOverFunc)
11958						t.mouseOverFunc(evt);
11959					break;
11960			}
11961		},
11962
11963		_add : function(tb, o) {
11964			var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;
11965
11966			if (s.separator) {
11967				ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});
11968				DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});
11969
11970				if (n = ro.previousSibling)
11971					DOM.addClass(n, 'mceLast');
11972
11973				return;
11974			}
11975
11976			n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});
11977			n = it = DOM.add(n, s.titleItem ? 'th' : 'td');
11978			n = a = DOM.add(n, 'a', {id: o.id + '_aria',  role: s.titleItem ? 'presentation' : 'option', href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});
11979
11980			if (s.parent) {
11981				DOM.setAttrib(a, 'aria-haspopup', 'true');
11982				DOM.setAttrib(a, 'aria-owns', 'menu_' + o.id);
11983			}
11984
11985			DOM.addClass(it, s['class']);
11986//			n = DOM.add(n, 'span', {'class' : 'item'});
11987
11988			ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});
11989
11990			if (s.icon_src)
11991				DOM.add(ic, 'img', {src : s.icon_src});
11992
11993			n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);
11994
11995			if (o.settings.style) {
11996				if (typeof o.settings.style == "function")
11997					o.settings.style = o.settings.style();
11998
11999				DOM.setAttrib(n, 'style', o.settings.style);
12000			}
12001
12002			if (tb.childNodes.length == 1)
12003				DOM.addClass(ro, 'mceFirst');
12004
12005			if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))
12006				DOM.addClass(ro, 'mceFirst');
12007
12008			if (o.collapse)
12009				DOM.addClass(ro, cp + 'ItemSub');
12010
12011			if (n = ro.previousSibling)
12012				DOM.removeClass(n, 'mceLast');
12013
12014			DOM.addClass(ro, 'mceLast');
12015		}
12016	});
12017})(tinymce);
12018(function(tinymce) {
12019	var DOM = tinymce.DOM;
12020
12021	tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
12022		Button : function(id, s, ed) {
12023			this.parent(id, s, ed);
12024			this.classPrefix = 'mceButton';
12025		},
12026
12027		renderHTML : function() {
12028			var cp = this.classPrefix, s = this.settings, h, l;
12029
12030			l = DOM.encode(s.label || '');
12031			h = '<a role="button" id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" aria-labelledby="' + this.id + '_voice" title="' + DOM.encode(s.title) + '">';
12032			if (s.image && !(this.editor  &&this.editor.forcedHighContrastMode) )
12033				h += '<span class="mceIcon ' + s['class'] + '"><img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" /></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
12034			else
12035				h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
12036
12037			h += '<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="' + this.id + '_voice">
12037' + s.title + '</span>';
12038			h += '</a>';
12039			return h;
12040		},
12041
12042		postRender : function() {
12043			var t = this, s = t.settings, imgBookmark;
12044
12045			// In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so
12046			// need to keep the selection in case the selection is lost
12047			if (tinymce.isIE && t.editor) {
12048				tinymce.dom.Event.add(t.id, 'mousedown', function(e) {
12049					var nodeName = t.editor.selection.getNode().nodeName;
12050					imgBookmark = nodeName === 'IMG' ? t.editor.selection.getBookmark() : null;
12051				});
12052			}
12053			tinymce.dom.Event.add(t.id, 'click', function(e) {
12054				if (!t.isDisabled()) {
12055					// restore the selection in case the selection is lost in IE
12056					if (tinymce.isIE && t.editor && imgBookmark !== null) {
12057						t.editor.selection.moveToBookmark(imgBookmark);
12058					}
12059					return s.onclick.call(s.scope, e);
12060				}
12061			});
12062			tinymce.dom.Event.add(t.id, 'keydown', function(e) {
12063				if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR) {
12064					tinymce.dom.Event.cancel(e);
12065					return s.onclick.call(s.scope, e);
12066				}
12067			});
12068		}
12069	});
12070})(tinymce);
12071
12072(function(tinymce) {
12073	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
12074
12075	tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
12076		ListBox : function(id, s, ed) {
12077			var t = this;
12078
12079			t.parent(id, s, ed);
12080
12081			t.items = [];
12082
12083			t.onChange = new Dispatcher(t);
12084
12085			t.onPostRender = new Dispatcher(t);
12086
12087			t.onAdd = new Dispatcher(t);
12088
12089			t.onRenderMenu = new tinymce.util.Dispatcher(this);
12090
12091			t.classPrefix = 'mceListBox';
12092			t.marked = {};
12093		},
12094
12095		select : function(va) {
12096			var t = this, fv, f;
12097
12098			t.marked = {};
12099
12100			if (va == undef)
12101				return t.selectByIndex(-1);
12102
12103			// Is string or number make function selector
12104			if (va && typeof(va)=="function")
12105				f = va;
12106			else {
12107				f = function(v) {
12108					return v == va;
12109				};
12110			}
12111
12112			// Do we need to do something?
12113			if (va != t.selectedValue) {
12114				// Find item
12115				each(t.items, function(o, i) {
12116					if (f(o.value)) {
12117						fv = 1;
12118						t.selectByIndex(i);
12119						return false;
12120					}
12121				});
12122
12123				if (!fv)
12124					t.selectByIndex(-1);
12125			}
12126		},
12127
12128		selectByIndex : function(idx) {
12129			var t = this, e, o, label;
12130
12131			t.marked = {};
12132
12133			if (idx != t.selectedIndex) {
12134				e = DOM.get(t.id + '_text');
12135				label = DOM.get(t.id + '_voiceDesc');
12136				o = t.items[idx];
12137
12138				if (o) {
12139					t.selectedValue = o.value;
12140					t.selectedIndex = idx;
12141					DOM.setHTML(e, DOM.encode(o.title));
12142					DOM.setHTML(label, t.settings.title + " - " + o.title);
12143					DOM.removeClass(e, 'mceTitle');
12144					DOM.setAttrib(t.id, 'aria-valuenow', o.title);
12145				} else {
12146					DOM.setHTML(e, DOM.encode(t.settings.title));
12147					DOM.setHTML(label, DOM.encode(t.settings.title));
12148					DOM.addClass(e, 'mceTitle');
12149					t.selectedValue = t.selectedIndex = null;
12150					DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);
12151				}
12152				e = 0;
12153			}
12154		},
12155
12156		mark : function(value) {
12157			this.marked[value] = true;
12158		},
12159
12160		add : function(n, v, o) {
12161			var t = this;
12162
12163			o = o || {};
12164			o = tinymce.extend(o, {
12165				title : n,
12166				value : v
12167			});
12168
12169			t.items.push(o);
12170			t.onAdd.dispatch(t, o);
12171		},
12172
12173		getLength : function() {
12174			return this.items.length;
12175		},
12176
12177		renderHTML : function() {
12178			var h = '', t = this, s = t.settings, cp = t.classPrefix;
12179
12180			h = '<span role="listbox" aria-haspopup="true" aria-labelledby="' + t.id +'_voiceDesc" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
12181			h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); 
12182			h += DOM.createHTML('a', {id : t.id + '_text', tabindex : -1, href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';
12183			h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>') + '</td>';
12184			h += '</tr></tbody></table></span>';
12185
12186			return h;
12187		},
12188
12189		showMenu : function() {
12190			var t = this, p2, e = DOM.get(this.id), m;
12191
12192			if (t.isDisabled() || t.items.length === 0)
12193				return;
12194
12195			if (t.menu && t.menu.isMenuVisible)
12196				return t.hideMenu();
12197
12198			if (!t.isMenuRendered) {
12199				t.renderMenu();
12200				t.isMenuRendered = true;
12201			}
12202
12203			p2 = DOM.getPos(e);
12204
12205			m = t.menu;
12206			m.settings.offset_x = p2.x;
12207			m.settings.offset_y = p2.y;
12208			m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
12209
12210			// Select in menu
12211			each(t.items, function(o) {
12212				if (m.items[o.id]) {
12213					m.items[o.id].setSelected(0);
12214				}
12215			});
12216
12217			each(t.items, function(o) {
12218				if (m.items[o.id] && t.marked[o.value]) {
12219					m.items[o.id].setSelected(1);
12220				}
12221
12222				if (o.value === t.selectedValue) {
12223					m.items[o.id].setSelected(1);
12224				}
12225			});
12226
12227			m.showMenu(0, e.clientHeight);
12228
12229			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
12230			DOM.addClass(t.id, t.classPrefix + 'Selected');
12231
12232			//DOM.get(t.id + '_text').focus();
12233		},
12234
12235		hideMenu : function(e) {
12236			var t = this;
12237
12238			if (t.menu && t.menu.isMenuVisible) {
12239				DOM.removeClass(t.id, t.classPrefix + 'Selected');
12240
12241				// Prevent double toogles by canceling the mouse click event to the button
12242				if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
12243					return;
12244
12245				if (!e || !DOM.getParent(e.target, '.mceMenu')) {
12246					DOM.removeClass(t.id, t.classPrefix + 'Selected');
12247					Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
12248					t.menu.hideMenu();
12249				}
12250			}
12251		},
12252
12253		renderMenu : function() {
12254			var t = this, m;
12255
12256			m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
12257				menu_line : 1,
12258				'class' : t.classPrefix + 'Menu mceNoIcons',
12259				max_width : 250,
12260				max_height : 150
12261			});
12262
12263			m.onHideMenu.add(function() {
12264				t.hideMenu();
12265				t.focus();
12266			});
12267
12268			m.add({
12269				title : t.settings.title,
12270				'class' : 'mceMenuItemTitle',
12271				onclick : function() {
12272					if (t.settings.onselect('') !== false)
12273						t.select(''); // Must be runned after
12274				}
12275			});
12276
12277			each(t.items, function(o) {
12278				// No value then treat it as a title
12279				if (o.value === undef) {
12280					m.add({
12281						title : o.title,
12282						role : "option",
12283						'class' : 'mceMenuItemTitle',
12284						onclick : function() {
12285							if (t.settings.onselect('') !== false)
12286								t.select(''); // Must be runned after
12287						}
12288					});
12289				} else {
12290					o.id = DOM.uniqueId();
12291					o.role= "option";
12292					o.onclick = function() {
12293						if (t.settings.onselect(o.value) !== false)
12294							t.select(o.value); // Must be runned after
12295					};
12296
12297					m.add(o);
12298				}
12299			});
12300
12301			t.onRenderMenu.dispatch(t, m);
12302			t.menu = m;
12303		},
12304
12305		postRender : function() {
12306			var t = this, cp = t.classPrefix;
12307
12308			Event.add(t.id, 'click', t.showMenu, t);
12309			Event.add(t.id, 'keydown', function(evt) {
12310				if (evt.keyCode == 32) { // Space
12311					t.showMenu(evt);
12312					Event.cancel(evt);
12313				}
12314			});
12315			Event.add(t.id, 'focus', function() {
12316				if (!t._focused) {
12317					t.keyDownHandler = Event.add(t.id, 'keydown', function(e) {
12318						if (e.keyCode == 40) {
12319							t.showMenu();
12320							Event.cancel(e);
12321						}
12322					});
12323					t.keyPressHandler = Event.add(t.id, 'keypress', function(e) {
12324						var v;
12325						if (e.keyCode == 13) {
12326							// Fake select on enter
12327							v = t.selectedValue;
12328							t.selectedValue = null; // Needs to be null to fake change
12329							Event.cancel(e);
12330							t.settings.onselect(v);
12331						}
12332					});
12333				}
12334
12335				t._focused = 1;
12336			});
12337			Event.add(t.id, 'blur', function() {
12338				Event.remove(t.id, 'keydown', t.keyDownHandler);
12339				Event.remove(t.id, 'keypress', t.keyPressHandler);
12340				t._focused = 0;
12341			});
12342
12343			// Old IE doesn't have hover on all elements
12344			if (tinymce.isIE6 || !DOM.boxModel) {
12345				Event.add(t.id, 'mouseover', function() {
12346					if (!DOM.hasClass(t.id, cp + 'Disabled'))
12347						DOM.addClass(t.id, cp + 'Hover');
12348				});
12349
12350				Event.add(t.id, 'mouseout', function() {
12351					if (!DOM.hasClass(t.id, cp + 'Disabled'))
12352						DOM.removeClass(t.id, cp + 'Hover');
12353				});
12354			}
12355
12356			t.onPostRender.dispatch(t, DOM.get(t.id));
12357		},
12358
12359		destroy : function() {
12360			this.parent();
12361
12362			Event.clear(this.id + '_text');
12363			Event.clear(this.id + '_open');
12364		}
12365	});
12366})(tinymce);
12367
12368(function(tinymce) {
12369	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
12370
12371	tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
12372		NativeListBox : function(id, s) {
12373			this.parent(id, s);
12374			this.classPrefix = 'mceNativeListBox';
12375		},
12376
12377		setDisabled : function(s) {
12378			DOM.get(this.id).disabled = s;
12379			this.setAriaProperty('disabled', s);
12380		},
12381
12382		isDisabled : function() {
12383			return DOM.get(this.id).disabled;
12384		},
12385
12386		select : function(va) {
12387			var t = this, fv, f;
12388
12389			if (va == undef)
12390				return t.selectByIndex(-1);
12391
12392			// Is string or number make function selector
12393			if (va && typeof(va)=="function")
12394				f = va;
12395			else {
12396				f = function(v) {
12397					return v == va;
12398				};
12399			}
12400
12401			// Do we need to do something?
12402			if (va != t.selectedValue) {
12403				// Find item
12404				each(t.items, function(o, i) {
12405					if (f(o.value)) {
12406						fv = 1;
12407						t.selectByIndex(i);
12408						return false;
12409					}
12410				});
12411
12412				if (!fv)
12413					t.selectByIndex(-1);
12414			}
12415		},
12416
12417		selectByIndex : function(idx) {
12418			DOM.get(this.id).selectedIndex = idx + 1;
12419			this.selectedValue = this.items[idx] ? this.items[idx].value : null;
12420		},
12421
12422		add : function(n, v, a) {
12423			var o, t = this;
12424
12425			a = a || {};
12426			a.value = v;
12427
12428			if (t.isRendered())
12429				DOM.add(DOM.get(this.id), 'option', a, n);
12430
12431			o = {
12432				title : n,
12433				value : v,
12434				attribs : a
12435			};
12436
12437			t.items.push(o);
12438			t.onAdd.dispatch(t, o);
12439		},
12440
12441		getLength : function() {
12442			return this.items.length;
12443		},
12444
12445		renderHTML : function() {
12446			var h, t = this;
12447
12448			h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');
12449
12450			each(t.items, function(it) {
12451				h += DOM.createHTML('option', {value : it.value}, it.title);
12452			});
12453
12454			h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox', 'aria-labelledby': t.id + '_aria'}, h);
12455			h += DOM.createHTML('span', {id : t.id + '_aria', 'style': 'display: none'}, t.settings.title);
12456			return h;
12457		},
12458
12459		postRender : function() {
12460			var t = this, ch, changeListenerAdded = true;
12461
12462			t.rendered = true;
12463
12464			function onChange(e) {
12465				var v = t.items[e.target.selectedIndex - 1];
12466
12467				if (v && (v = v.value)) {
12468					t.onChange.dispatch(t, v);
12469
12470					if (t.settings.onselect)
12471						t.settings.onselect(v);
12472				}
12473			};
12474
12475			Event.add(t.id, 'change', onChange);
12476
12477			// Accessibility keyhandler
12478			Event.add(t.id, 'keydown', function(e) {
12479				var bf, DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32;
12480
12481				Event.remove(t.id, 'change', ch);
12482				changeListenerAdded = false;
12483
12484				bf = Event.add(t.id, 'blur', function() {
12485					if (changeListenerAdded) return;
12486					changeListenerAdded = true;
12487					Event.add(t.id, 'change', onChange);
12488					Event.remove(t.id, 'blur', bf);
12489				});
12490
12491				if (e.keyCode == DOM_VK_RETURN || e.keyCode == DOM_VK_SPACE) {
12492					onChange(e);
12493					return Event.cancel(e);
12494				} else if (e.keyCode == DOM_VK_DOWN || e.keyCode == DOM_VK_UP) {
12495					// allow native implementation (navigate select element options)
12496					e.stopImmediatePropagation();
12497				}
12498			});
12499
12500			t.onPostRender.dispatch(t, DOM.get(t.id));
12501		}
12502	});
12503})(tinymce);
12504
12505(function(tinymce) {
12506	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
12507
12508	tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
12509		MenuButton : function(id, s, ed) {
12510			this.parent(id, s, ed);
12511
12512			this.onRenderMenu = new tinymce.util.Dispatcher(this);
12513
12514			s.menu_container = s.menu_container || DOM.doc.body;
12515		},
12516
12517		showMenu : function() {
12518			var t = this, p1, p2, e = DOM.get(t.id), m;
12519
12520			if (t.isDisabled())
12521				return;
12522
12523			if (!t.isMenuRendered) {
12524				t.renderMenu();
12525				t.isMenuRendered = true;
12526			}
12527
12528			if (t.isMenuVisible)
12529				return t.hideMenu();
12530
12531			p1 = DOM.getPos(t.settings.menu_container);
12532			p2 = DOM.getPos(e);
12533
12534			m = t.menu;
12535			m.settings.offset_x = p2.x;
12536			m.settings.offset_y = p2.y;
12537			m.settings.vp_offset_x = p2.x;
12538			m.settings.vp_offset_y = p2.y;
12539			m.settings.keyboard_focus = t._focused;
12540			m.showMenu(0, e.firstChild.clientHeight);
12541
12542			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
12543			t.setState('Selected', 1);
12544
12545			t.isMenuVisible = 1;
12546		},
12547
12548		renderMenu : function() {
12549			var t = this, m;
12550
12551			m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
12552				menu_line : 1,
12553				'class' : this.classPrefix + 'Menu',
12554				icons : t.settings.icons
12555			});
12556
12557			m.onHideMenu.add(function() {
12558				t.hideMenu();
12559				t.focus();
12560			});
12561
12562			t.onRenderMenu.dispatch(t, m);
12563			t.menu = m;
12564		},
12565
12566		hideMenu : function(e) {
12567			var t = this;
12568
12569			// Prevent double toogles by canceling the mouse click event to the button
12570			if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))
12571				return;
12572
12573			if (!e || !DOM.getParent(e.target, '.mceMenu')) {
12574				t.setState('Selected', 0);
12575				Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
12576				if (t.menu)
12577					t.menu.hideMenu();
12578			}
12579
12580			t.isMenuVisible = 0;
12581		},
12582
12583		postRender : function() {
12584			var t = this, s = t.settings;
12585
12586			Event.add(t.id, 'click', function() {
12587				if (!t.isDisabled()) {
12588					if (s.onclick)
12589						s.onclick(t.value);
12590
12591					t.showMenu();
12592				}
12593			});
12594		}
12595	});
12596})(tinymce);
12597
12598(function(tinymce) {
12599	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
12600
12601	tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
12602		SplitButton : function(id, s, ed) {
12603			this.parent(id, s, ed);
12604			this.classPrefix = 'mceSplitButton';
12605		},
12606
12607		renderHTML : function() {
12608			var h, t = this, s = t.settings, h1;
12609
12610			h = '<tbody><tr>';
12611
12612			if (s.image)
12613				h1 = DOM.createHTML('img ', {src : s.image, role: 'presentation', 'class' : 'mceAction ' + s['class']});
12614			else
12615				h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');
12616
12617			h1 += DOM.createHTML('span', {'class': 'mceVoiceLabel mceIconOnly', id: t.id + '_voice', style: 'display:none;'}, s.title);
12618			h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_action', tabindex: '-1', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';
12619	
12620			h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']}, '<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');
12621			h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_open', tabindex: '-1', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';
12622
12623			h += '</tr></tbody>';
12624			h = DOM.createHTML('table', { role: 'presentation',   'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);
12625			return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);
12626		},
12627
12628		postRender : function() {
12629			var t = this, s = t.settings, activate;
12630
12631			if (s.onclick) {
12632				activate = function(evt) {
12633					if (!t.isDisabled()) {
12634						s.onclick(t.value);
12635						Event.cancel(evt);
12636					}
12637				};
12638				Event.add(t.id + '_action', 'click', activate);
12639				Event.add(t.id, ['click', 'keydown'], function(evt) {
12640					var DOM_VK_SPACE = 32, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_UP = 38, DOM_VK_DOWN = 40;
12641					if ((evt.keyCode === 32 || evt.keyCode === 13 || evt.keyCode === 14) && !evt.altKey && !evt.ctrlKey && !evt.metaKey) {
12642						activate();
12643						Event.cancel(evt);
12644					} else if (evt.type === 'click' || evt.keyCode === DOM_VK_DOWN) {
12645						t.showMenu();
12646						Event.cancel(evt);
12647					}
12648				});
12649			}
12650
12651			Event.add(t.id + '_open', 'click', function (evt) {
12652				t.showMenu();
12653				Event.cancel(evt);
12654			});
12655			Event.add([t.id, t.id + '_open'], 'focus', function() {t._focused = 1;});
12656			Event.add([t.id, t.id + '_open'], 'blur', function() {t._focused = 0;});
12657
12658			// Old IE doesn't have hover on all elements
12659			if (tinymce.isIE6 || !DOM.boxModel) {
12660				Event.add(t.id, 'mouseover', function() {
12661					if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
12662						DOM.addClass(t.id, 'mceSplitButtonHover');
12663				});
12664
12665				Event.add(t.id, 'mouseout', function() {
12666					if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
12667						DOM.removeClass(t.id, 'mceSplitButtonHover');
12668				});
12669			}
12670		},
12671
12672		destroy : function() {
12673			this.parent();
12674
12675			Event.clear(this.id + '_action');
12676			Event.clear(this.id + '_open');
12677			Event.clear(this.id);
12678		}
12679	});
12680})(tinymce);
12681
12682(function(tinymce) {
12683	var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;
12684
12685	tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
12686		ColorSplitButton : function(id, s, ed) {
12687			var t = this;
12688
12689			t.parent(id, s, ed);
12690
12691			t.settings = s = tinymce.extend({
12692				colors : '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF',
12693				grid_width : 8,
12694				default_color : '#888888'
12695			}, t.settings);
12696
12697			t.onShowMenu = new tinymce.util.Dispatcher(t);
12698
12699			t.onHideMenu = new tinymce.util.Dispatcher(t);
12700
12701			t.value = s.default_color;
12702		},
12703
12704		showMenu : function() {
12705			var t = this, r, p, e, p2;
12706
12707			if (t.isDisabled())
12708				return;
12709
12710			if (!t.isMenuRendered) {
12711				t.renderMenu();
12712				t.isMenuRendered = true;
12713			}
12714
12715			if (t.isMenuVisible)
12716				return t.hideMenu();
12717
12718			e = DOM.get(t.id);
12719			DOM.show(t.id + '_menu');
12720			DOM.addClass(e, 'mceSplitButtonSelected');
12721			p2 = DOM.getPos(e);
12722			DOM.setStyles(t.id + '_menu', {
12723				left : p2.x,
12724				top : p2.y + e.firstChild.clientHeight,
12725				zIndex : 200000
12726			});
12727			e = 0;
12728
12729			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
12730			t.onShowMenu.dispatch(t);
12731
12732			if (t._focused) {
12733				t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
12734					if (e.keyCode == 27)
12735						t.hideMenu();
12736				});
12737
12738				DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
12739			}
12740
12741			t.keyboardNav = new tinymce.ui.KeyboardNavigation({
12742				root: t.id + '_menu',
12743				items: DOM.select('a', t.id + '_menu'),
12744				onCancel: function() {
12745					t.hideMenu();
12746					t.focus();
12747				}
12748			});
12749
12750			t.keyboardNav.focus();
12751			t.isMenuVisible = 1;
12752		},
12753
12754		hideMenu : function(e) {
12755			var t = this;
12756
12757			if (t.isMenuVisible) {
12758				// Prevent double toogles by canceling the mouse click event to the button
12759				if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
12760					return;
12761
12762				if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
12763					DOM.removeClass(t.id, 'mceSplitButtonSelected');
12764					Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
12765					Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
12766					DOM.hide(t.id + '_menu');
12767				}
12768
12769				t.isMenuVisible = 0;
12770				t.onHideMenu.dispatch();
12771				t.keyboardNav.destroy();
12772			}
12773		},
12774
12775		renderMenu : function() {
12776			var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;
12777
12778			w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s.menu_class + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
12779			m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
12780			DOM.add(m, 'span', {'class' : 'mceMenuLine'});
12781
12782			n = DOM.add(m, 'table', {role: 'presentation', 'class' : 'mceColorSplitMenu'});
12783			tb = DOM.add(n, 'tbody');
12784
12785			// Generate color grid
12786			i = 0;
12787			each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
12788				c = c.replace(/^#/, '');
12789
12790				if (!i--) {
12791					tr = DOM.add(tb, 'tr');
12792					i = s.grid_width - 1;
12793				}
12794
12795				n = DOM.add(tr, 'td');
12796				var settings = {
12797					href : 'javascript:;',
12798					style : {
12799						backgroundColor : '#' + c
12800					},
12801					'title': t.editor.getLang('colors.' + c, c),
12802					'data-mce-color' : '#' + c
12803				};
12804
12805				// adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.
12806				if (!tinymce.isIE ) {
12807					settings.role = 'option';
12808				}
12809
12810				n = DOM.add(n, 'a', settings);
12811
12812				if (t.editor.forcedHighContrastMode) {
12813					n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });
12814					if (n.getContext && (context = n.getContext("2d"))) {
12815						context.fillStyle = '#' + c;
12816						context.fillRect(0, 0, 16, 16);
12817					} else {
12818						// No point leaving a canvas element around if it's not supported for drawing on anyway.
12819						DOM.remove(n);
12820					}
12821				}
12822			});
12823
12824			if (s.more_colors_func) {
12825				n = DOM.add(tb, 'tr');
12826				n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});
12827				n = DOM.add(n, 'a', {role: 'option', id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);
12828
12829				Event.add(n, 'click', function(e) {
12830					s.more_colors_func.call(s.more_colors_scope || this);
12831					return Event.cancel(e); // Cancel to fix onbeforeunload problem
12832				});
12833			}
12834
12835			DOM.addClass(m, 'mceColorSplitMenu');
12836
12837			// Prevent IE from scrolling and hindering click to occur #4019
12838			Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});
12839
12840			Event.add(t.id + '_menu', 'click', function(e) {
12841				var c;
12842
12843				e = DOM.getParent(e.target, 'a', tb);
12844
12845				if (e && e.nodeName.toLowerCase() == 'a' && (c = e.getAttribute('data-mce-color')))
12846					t.setColor(c);
12847
12848				return false; // Prevent IE auto save warning
12849			});
12850
12851			return w;
12852		},
12853
12854		setColor : function(c) {
12855			this.displayColor(c);
12856			this.hideMenu();
12857			this.settings.onselect(c);
12858		},
12859		
12860		displayColor : function(c) {
12861			var t = this;
12862
12863			DOM.setStyle(t.id + '_preview', 'backgroundColor', c);
12864
12865			t.value = c;
12866		},
12867
12868		postRender : function() {
12869			var t = this, id = t.id;
12870
12871			t.parent();
12872			DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
12873			DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);
12874		},
12875
12876		destroy : function() {
12877			var self = this;
12878
12879			self.parent();
12880
12881			Event.clear(self.id + '_menu');
12882			Event.clear(self.id + '_more');
12883			DOM.remove(self.id + '_menu');
12884
12885			if (self.keyboardNav) {
12886				self.keyboardNav.destroy();
12887			}
12888		}
12889	});
12890})(tinymce);
12891
12892(function(tinymce) {
12893// Shorten class names
12894var dom = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event;
12895tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {
12896	renderHTML : function() {
12897		var t = this, h = [], controls = t.controls, each = tinymce.each, settings = t.settings;
12898
12899		h.push('<div id="' + t.id + '" role="group" aria-labelledby="' + t.id + '_voice">');
12900		//TODO: ACC test this out - adding a role = application for getting the landmarks working well.
12901		h.push("<span role='application'>");
12902		h.push('<span id="' + t.id + '_voice" class="mceVoiceLabel" style="display:none;">' + dom.encode(settings.name) + '</span>');
12903		each(controls, function(toolbar) {
12904			h.push(toolbar.renderHTML());
12905		});
12906		h.push("</span>");
12907		h.push('</div>');
12908
12909		return h.join('');
12910	},
12911	
12912	focus : function() {
12913		var t = this;
12914		dom.get(t.id).focus();
12915	},
12916	
12917	postRender : function() {
12918		var t = this, items = [];
12919
12920		each(t.controls, function(toolbar) {
12921			each (toolbar.controls, function(control) {
12922				if (control.id) {
12923					items.push(control);
12924				}
12925			});
12926		});
12927
12928		t.keyNav = new tinymce.ui.KeyboardNavigation({
12929			root: t.id,
12930			items: items,
12931			onCancel: function() {
12932				//Move focus if webkit so that navigation back will read the item.
12933				if (tinymce.isWebKit) {
12934					dom.get(t.editor.id+"_ifr").focus();
12935				}
12936				t.editor.focus();
12937			},
12938			excludeFromTabOrder: !t.settings.tab_focus_toolbar
12939		});
12940	},
12941	
12942	destroy : function() {
12943		var self = this;
12944
12945		self.parent();
12946		self.keyNav.destroy();
12947		Event.clear(self.id);
12948	}
12949});
12950})(tinymce);
12951
12952(function(tinymce) {
12953// Shorten class names
12954var dom = tinymce.DOM, each = tinymce.each;
12955tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12956	renderHTML : function() {
12957		var t = this, h = '', c, co, s = t.settings, i, pr, nx, cl;
12958
12959		cl = t.controls;
12960		for (i=0; i<cl.length; i++) {
12961			// Get current control, prev control, next control and if the control is a list box or not
12962			co = cl[i];
12963			pr = cl[i - 1];
12964			nx = cl[i + 1];
12965
12966			// Add toolbar start
12967			if (i === 0) {
12968				c = 'mceToolbarStart';
12969
12970				if (co.Button)
12971					c += ' mceToolbarStartButton';
12972				else if (co.SplitButton)
12973					c += ' mceToolbarStartSplitButton';
12974				else if (co.ListBox)
12975					c += ' mceToolbarStartListBox';
12976
12977				h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
12978			}
12979
12980			// Add toolbar end before list box and after the previous button
12981			// This is to fix the o2k7 editor skins
12982			if (pr && co.ListBox) {
12983				if (pr.Button || pr.SplitButton)
12984					h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));
12985			}
12986
12987			// Render control HTML
12988
12989			// IE 8 quick fix, needed to propertly generate a hit area for anchors
12990			if (dom.stdMode)
12991				h += '<td style="position: relative">' + co.renderHTML() + '</td>';
12992			else
12993				h += '<td>' + co.renderHTML() + '</td>';
12994
12995			// Add toolbar start after list box and before the next button
12996			// This is to fix the o2k7 editor skins
12997			if (nx && co.ListBox) {
12998				if (nx.Button || nx.SplitButton)
12999					h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));
13000			}
13001		}
13002
13003		c = 'mceToolbarEnd';
13004
13005		if (co.Button)
13006			c += ' mceToolbarEndButton';
13007		else if (co.SplitButton)
13008			c += ' mceToolbarEndSplitButton';
13009		else if (co.ListBox)
13010			c += ' mceToolbarEndListBox';
13011
13012		h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
13013
13014		return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || '', role: 'presentation', tabindex: '-1'}, '<tbody><tr>' + h + '</tr></tbody>');
13015	}
13016});
13017})(tinymce);
13018
13019(function(tinymce) {
13020	var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
13021
13022	tinymce.create('tinymce.AddOnManager', {
13023		AddOnManager : function() {
13024			var self = this;
13025
13026			self.items = [];
13027			self.urls = {};
13028			self.lookup = {};
13029			self.onAdd = new Dispatcher(self);
13030		},
13031
13032		get : function(n) {
13033			if (this.lookup[n]) {
13034				return this.lookup[n].instance;
13035			} else {
13036				return undefined;
13037			}
13038		},
13039
13040		dependencies : function(n) {
13041			var result;
13042			if (this.lookup[n]) {
13043				result = this.lookup[n].dependencies;
13044			}
13045			return result || [];
13046		},
13047
13048		requireLangPack : function(n) {
13049			var s = tinymce.settings;
13050
13051			if (s && s.language && s.language_load !== false)
13052				tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');
13053		},
13054
13055		add : function(id, o, dependencies) {
13056			this.items.push(o);
13057			this.lookup[id] = {instance:o, dependencies:dependencies};
13058			this.onAdd.dispatch(this, id, o);
13059
13060			return o;
13061		},
13062		createUrl: function(baseUrl, dep) {
13063			if (typeof dep === "object") {
13064				return dep
13065			} else {
13066				return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
13067			}
13068		},
13069
13070		addComponents: function(pluginName, scripts) {
13071			var pluginUrl = this.urls[pluginName];
13072			tinymce.each(scripts, function(script){
13073				tinymce.ScriptLoader.add(pluginUrl+"/"+script);	
13074			});
13075		},
13076
13077		load : function(n, u, cb, s) {
13078			var t = this, url = u;
13079
13080			function loadDependencies() {
13081				var dependencies = t.dependencies(n);
13082				tinymce.each(dependencies, function(dep) {
13083					var newUrl = t.createUrl(u, dep);
13084					t.load(newUrl.resource, newUrl, undefined, undefined);
13085				});
13086				if (cb) {
13087					if (s) {
13088						cb.call(s);
13089					} else {
13090						cb.call(tinymce.ScriptLoader);
13091					}
13092				}
13093			}
13094
13095			if (t.urls[n])
13096				return;
13097			if (typeof u === "object")
13098				url = u.prefix + u.resource + u.suffix;
13099
13100			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1)
13101				url = tinymce.baseURL + '/' + url;
13102
13103			t.urls[n] = url.substring(0, url.lastIndexOf('/'));
13104
13105			if (t.lookup[n]) {
13106				loadDependencies();
13107			} else {
13108				tinymce.ScriptLoader.add(url, loadDependencies, s);
13109			}
13110		}
13111	});
13112
13113	// Create plugin and theme managers
13114	tinymce.PluginManager = new tinymce.AddOnManager();
13115	tinymce.ThemeManager = new tinymce.AddOnManager();
13116}(tinymce));
13117
13118(function(tinymce) {
13119	// Shorten names
13120	var each = tinymce.each, extend = tinymce.extend,
13121		DOM = tinymce.DOM, Event = tinymce.dom.Event,
13122		ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
13123		explode = tinymce.explode,
13124		Dispatcher = tinymce.util.Dispatcher, undef, instanceCounter = 0;
13125
13126	// Setup some URLs where the editor API is located and where the document is
13127	tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
13128	if (!/[\/\\]$/.test(tinymce.documentBaseURL))
13129		tinymce.documentBaseURL += '/';
13130
13131	tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
13132
13133	tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);
13134
13135	// Add before unload listener
13136	// This was required since IE was leaking memory if you added and removed beforeunload listeners
13137	// with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
13138	tinymce.onBeforeUnload = new Dispatcher(tinymce);
13139
13140	// Must be on window or IE will leak if the editor is placed in frame or iframe
13141	Event.add(window, 'beforeunload', function(e) {
13142		tinymce.onBeforeUnload.dispatch(tinymce, e);
13143	});
13144
13145	tinymce.onAddEditor = new Dispatcher(tinymce);
13146
13147	tinymce.onRemoveEditor = new Dispatcher(tinymce);
13148
13149	tinymce.EditorManager = extend(tinymce, {
13150		editors : [],
13151
13152		i18n : {},
13153
13154		activeEditor : null,
13155
13156		init : function(s) {
13157			var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;
13158
13159			function createId(elm) {
13160				var id = elm.id;
13161	
13162				// Use element id, or unique name or generate a unique id
13163				if (!id) {
13164					id = elm.name;
13165	
13166					if (id && !DOM.get(id)) {
13167						id = elm.name;
13168					} else {
13169						// Generate unique name
13170						id = DOM.uniqueId();
13171					}
13172
13173					elm.setAttribute('id', id);
13174				}
13175
13176				return id;
13177			};
13178
13179			function execCallback(se, n, s) {
13180				var f = se[n];
13181
13182				if (!f)
13183					return;
13184
13185				if (tinymce.is(f, 'string')) {
13186					s = f.replace(/\.\w+$/, '');
13187					s = s ? tinymce.resolve(s) : 0;
13188					f = tinymce.resolve(f);
13189				}
13190
13191				return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
13192			};
13193
13194			function hasClass(n, c) {
13195				return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
13196			};
13197
13198			t.settings = s;
13199
13200			// Legacy call
13201			Event.bind(window, 'ready', function() {
13202				var l, co;
13203
13204				execCallback(s, 'onpageload');
13205
13206				switch (s.mode) {
13207					case "exact":
13208						l = s.elements || '';
13209
13210						if(l.length > 0) {
13211							each(explode(l), function(v) {
13212								if (DOM.get(v)) {
13213									ed = new tinymce.Editor(v, s);
13214									el.push(ed);
13215									ed.render(1);
13216								} else {
13217									each(document.forms, function(f) {
13218										each(f.elements, function(e) {
13219											if (e.name === v) {
13220												v = 'mce_editor_' + instanceCounter++;
13221												DOM.setAttrib(e, 'id', v);
13222
13223												ed = new tinymce.Editor(v, s);
13224												el.push(ed);
13225												ed.render(1);
13226											}
13227										});
13228									});
13229								}
13230							});
13231						}
13232						break;
13233
13234					case "textareas":
13235					case "specific_textareas":
13236						each(DOM.select('textarea'), function(elm) {
13237							if (s.editor_deselector && hasClass(elm, s.editor_deselector))
13238								return;
13239
13240							if (!s.editor_selector || hasClass(elm, s.editor_selector)) {
13241								ed = new tinymce.Editor(createId(elm), s);
13242								el.push(ed);
13243								ed.render(1);
13244							}
13245						});
13246						break;
13247					
13248					default:
13249						if (s.types) {
13250							// Process type specific selector
13251							each(s.types, function(type) {
13252								each(DOM.select(type.selector), function(elm) {
13253									var editor = new tinymce.Editor(createId(elm), tinymce.extend({}, s, type));
13254									el.push(editor);
13255									editor.render(1);
13256								});
13257							});
13258						} else if (s.selector) {
13259							// Process global selector
13260							each(DOM.select(s.selector), function(elm) {
13261								var editor = new tinymce.Editor(createId(elm), s);
13262								el.push(editor);
13263								editor.render(1);
13264							});
13265						}
13266				}
13267
13268				// Call onInit when all editors are initialized
13269				if (s.oninit) {
13270					l = co = 0;
13271
13272					each(el, function(ed) {
13273						co++;
13274
13275						if (!ed.initialized) {
13276							// Wait for it
13277							ed.onInit.add(function() {
13278								l++;
13279
13280								// All done
13281								if (l == co)
13282									execCallback(s, 'oninit');
13283							});
13284						} else
13285							l++;
13286
13287						// All done
13288						if (l == co)
13289							execCallback(s, 'oninit');					
13290					});
13291				}
13292			});
13293		},
13294
13295		get : function(id) {
13296			if (id === undef)
13297				return this.editors;
13298
13299			if (!this.editors.hasOwnProperty(id))
13300				return undef;
13301
13302			return this.editors[id];
13303		},
13304
13305		getInstanceById : function(id) {
13306			return this.get(id);
13307		},
13308
13309		add : function(editor) {
13310			var self = this, editors = self.editors;
13311
13312			// Add named and index editor instance
13313			editors[editor.id] = editor;
13314			editors.push(editor);
13315
13316			self._setActive(editor);
13317			self.onAddEditor.dispatch(self, editor);
13318
13319
13320			return editor;
13321		},
13322
13323		remove : function(editor) {
13324			var t = this, i, editors = t.editors;
13325
13326			// Not in the collection
13327			if (!editors[editor.id])
13328				return null;
13329
13330			delete editors[editor.id];
13331
13332			for (i = 0; i < editors.length; i++) {
13333				if (editors[i] == editor) {
13334					editors.splice(i, 1);
13335					break;
13336				}
13337			}
13338
13339			// Select another editor since the active one was removed
13340			if (t.activeEditor == editor)
13341				t._setActive(editors[0]);
13342
13343			editor.destroy();
13344			t.onRemoveEditor.dispatch(t, editor);
13345
13346			return editor;
13347		},
13348
13349		execCommand : function(c, u, v) {
13350			var t = this, ed = t.get(v), w;
13351
13352			function clr() {
13353				ed.destroy();
13354				w.detachEvent('onunload', clr);
13355				w = w.tinyMCE = w.tinymce = null; // IE leak
13356			};
13357
13358			// Manager commands
13359			switch (c) {
13360				case "mceFocus":
13361					ed.focus();
13362					return true;
13363
13364				case "mceAddEditor":
13365				case "mceAddControl":
13366					if (!t.get(v))
13367						new tinymce.Editor(v, t.settings).render();
13368
13369					return true;
13370
13371				case "mceAddFrameControl":
13372					w = v.window;
13373
13374					// Add tinyMCE global instance and tinymce namespace to specified window
13375					w.tinyMCE = tinyMCE;
13376					w.tinymce = tinymce;
13377
13378					tinymce.DOM.doc = w.document;
13379					tinymce.DOM.win = w;
13380
13381					ed = new tinymce.Editor(v.element_id, v);
13382					ed.render();
13383
13384					// Fix IE memory leaks
13385					if (tinymce.isIE && ! tinymce.isIE11) {
13386						w.attachEvent('onunload', clr);
13387					}
13388
13389					v.page_window = null;
13390
13391					return true;
13392
13393				case "mceRemoveEditor":
13394				case "mceRemoveControl":
13395					if (ed)
13396						ed.remove();
13397
13398					return true;
13399
13400				case 'mceToggleEditor':
13401					if (!ed) {
13402						t.execCommand('mceAddControl', 0, v);
13403						return true;
13404					}
13405
13406					if (ed.isHidden())
13407						ed.show();
13408					else
13409						ed.hide();
13410
13411					return true;
13412			}
13413
13414			// Run command on active editor
13415			if (t.activeEditor)
13416				return t.activeEditor.execCommand(c, u, v);
13417
13418			return false;
13419		},
13420
13421		execInstanceCommand : function(id, c, u, v) {
13422			var ed = this.get(id);
13423
13424			if (ed)
13425				return ed.execCommand(c, u, v);
13426
13427			return false;
13428		},
13429
13430		triggerSave : function() {
13431			each(this.editors, function(e) {
13432				e.save();
13433			});
13434		},
13435
13436		addI18n : function(p, o) {
13437			var lo, i18n = this.i18n;
13438
13439			if (!tinymce.is(p, 'string')) {
13440				each(p, function(o, lc) {
13441					each(o, function(o, g) {
13442						each(o, function(o, k) {
13443							if (g === 'common')
13444								i18n[lc + '.' + k] = o;
13445							else
13446								i18n[lc + '.' + g + '.' + k] = o;
13447						});
13448					});
13449				});
13450			} else {
13451				each(o, function(o, k) {
13452					i18n[p + '.' + k] = o;
13453				});
13454			}
13455		},
13456
13457		// Private methods
13458
13459		_setActive : function(editor) {
13460			this.selectedInstance = this.activeEditor = editor;
13461		}
13462	});
13463})(tinymce);
13464
13465(function(tinymce) {
13466	// Shorten these names
13467	var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,
13468		each = tinymce.each, isGecko = tinymce.isGecko,
13469		isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,
13470		ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
13471		explode = tinymce.explode;
13472
13473	tinymce.create('tinymce.Editor', {
13474		Editor : function(id, settings) {
13475			var self = this, TRUE = true;
13476
13477			self.settings = settings = extend({
13478				id : id,
13479				language : 'en',
13480				theme : 'advanced',
13481				skin : 'default',
13482				delta_width : 0,
13483				delta_height : 0,
13484				popup_css : '',
13485				plugins : '',
13486				document_base_url : tinymce.documentBaseURL,
13487				add_form_submit_trigger : TRUE,
13488				submit_patch : TRUE,
13489				add_unload_trigger : TRUE,
13490				convert_urls : TRUE,
13491				relative_urls : TRUE,
13492				remove_script_host : TRUE,
13493				table_inline_editing : false,
13494				object_resizing : TRUE,
13495				accessibility_focus : TRUE,
13496				doctype : tinymce.isIE6 ? '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' : '<!DOCTYPE>', // Use old doctype on IE 6 to avoid horizontal scroll
13497				visual : TRUE,
13498				font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
13499				font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
13500				apply_source_formatting : TRUE,
13501				directionality : 'ltr',
13502				forced_root_block : 'p',
13503				hidden_input : TRUE,
13504				padd_empty_editor : TRUE,
13505				render_ui : TRUE,
13506				indentation : '30px',
13507				fix_table_elements : TRUE,
13508				inline_styles : TRUE,
13509				convert_fonts_to_spans : TRUE,
13510				indent : 'simple',
13511				indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
13512				indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
13513				validate : TRUE,
13514				entity_encoding : 'named',
13515				url_converter : self.convertURL,
13516				url_converter_scope : self,
13517				ie7_compat : TRUE
13518			}, settings);
13519
13520			self.id = self.editorId = id;
13521
13522			self.isNotDirty = false;
13523
13524			self.plugins = {};
13525
13526			self.documentBaseURI = new tinymce.util.URI(settings.document_base_url || tinymce.documentBaseURL, {
13527				base_uri : tinyMCE.baseURI
13528			});
13529
13530			self.baseURI = tinymce.baseURI;
13531
13532			self.contentCSS = [];
13533
13534			self.contentStyles = [];
13535
13536			// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
13537			self.setupEvents();
13538
13539			// Internal command handler objects
13540			self.execCommands = {};
13541			self.queryStateCommands = {};
13542			self.queryValueCommands = {};
13543
13544			// Call setup
13545			self.execCallback('setup', self);
13546		},
13547
13548		render : function(nst) {
13549			var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
13550
13551			// Page is not loaded yet, wait for it
13552			if (!Event.domLoaded) {
13553				Event.add(window, 'ready', function() {
13554					t.render();
13555				});
13556				return;
13557			}
13558
13559			tinyMCE.settings = s;
13560
13561			// Element not found, then skip initialization
13562			if (!t.getElement())
13563				return;
13564
13565			// Is a iPad/iPhone and not on iOS5, then skip initialization. We need to sniff 
13566			// here since the browser says it has contentEditable support but there is no visible caret.
13567			if (tinymce.isIDevice && !tinymce.isIOS5)
13568				return;
13569
13570			// Add hidden input for non input elements inside form elements
13571			if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
13572				DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
13573
13574			// Hide target element early to prevent content flashing
13575			if (!s.content_editable) {
13576				t.orgVisibility = t.getElement().style.visibility;
13577				t.getElement().style.visibility = 'hidden';
13578			}
13579
13580			if (tinymce.WindowManager)
13581				t.windowManager = new tinymce.WindowManager(t);
13582
13583			if (s.encoding == 'xml') {
13584				t.onGetContent.add(function(ed, o) {
13585					if (o.save)
13586						o.content = DOM.encode(o.content);
13587				});
13588			}
13589
13590			if (s.add_form_submit_trigger) {
13591				t.onSubmit.addToTop(function() {
13592					if (t.initialized) {
13593						t.save();
13594						t.isNotDirty = 1;
13595					}
13596				});
13597			}
13598
13599			if (s.add_unload_trigger) {
13600				t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
13601					if (t.initialized && !t.destroyed && !t.isHidden())
13602						t.save({format : 'raw', no_events : true});
13603				});
13604			}
13605
13606			tinymce.addUnload(t.destroy, t);
13607
13608			if (s.submit_patch) {
13609				t.onBeforeRenderUI.add(function() {
13610					var n = t.getElement().form;
13611
13612					if (!n)
13613						return;
13614
13615					// Already patched
13616					if (n._mceOldSubmit)
13617						return;
13618
13619					// Check page uses id="submit" or name="submit" for it's submit button
13620					if (!n.submit.nodeType && !n.submit.length) {
13621						t.formElement = n;
13622						n._mceOldSubmit = n.submit;
13623						n.submit = function() {
13624							// Save all instances
13625							tinymce.triggerSave();
13626							t.isNotDirty = 1;
13627
13628							return t.formElement._mceOldSubmit(t.formElement);
13629						};
13630					}
13631
13632					n = null;
13633				});
13634			}
13635
13636			// Load scripts
13637			function loadScripts() {
13638				if (s.language && s.language_load !== false)
13639					sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
13640
13641				if (s.theme && typeof s.theme != "function" && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
13642					ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
13643
13644				each(explode(s.plugins), function(p) {
13645					if (p &&!PluginManager.urls[p]) {
13646						if (p.charAt(0) == '-') {
13647							p = p.substr(1, p.length);
13648							var dependencies = PluginManager.dependencies(p);
13649							each(dependencies, function(dep) {
13650								var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};
13651								dep = PluginManager.createUrl(defaultSettings, dep);
13652								PluginManager.load(dep.resource, dep);
13653							});
13654						} else {
13655							// Skip safari plugin, since it is removed as of 3.3b1
13656							if (p == 'safari') {
13657								return;
13658							}
13659							PluginManager.load(p, {prefix:'plugins/', resource: p, suffix:'/editor_plugin' + tinymce.suffix + '.js'});
13660						}
13661					}
13662				});
13663
13664				// Init when que is loaded
13665				sl.loadQueue(function() {
13666					if (!t.removed)
13667						t.init();
13668				});
13669			};
13670
13671			loadScripts();
13672		},
13673
13674		init : function() {
13675			var n, t = this, s = t.settings, w, h, mh, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
13676
13677			tinymce.add(t);
13678
13679			s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));
13680
13681			if (s.theme) {
13682				if (typeof s.theme != "function") {
13683					s.theme = s.theme.replace(/-/, '');
13684					o = ThemeManager.get(s.theme);
13685					t.theme = new o();
13686
13687					if (t.theme.init)
13688						t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
13689				} else {
13690					t.theme = s.theme;
13691				}
13692			}
13693
13694			function initPlugin(p) {
13695				var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
13696				if (c && tinymce.inArray(initializedPlugins,p) === -1) {
13697					each(PluginManager.dependencies(p), function(dep){
13698						initPlugin(dep);
13699					});
13700					po = new c(t, u);
13701
13702					t.plugins[p] = po;
13703
13704					if (po.init) {
13705						po.init(t, u);
13706						initializedPlugins.push(p);
13707					}
13708				}
13709			}
13710			
13711			// Create all plugins
13712			each(explode(s.plugins.replace(/\-/g, '')), initPlugin);
13713
13714			// Setup popup CSS path(s)
13715			if (s.popup_css !== false) {
13716				if (s.popup_css)
13717					s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
13718				else
13719					s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
13720			}
13721
13722			if (s.popup_css_add)
13723				s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
13724
13725			t.controlManager = new tinymce.ControlManager(t);
13726
13727			// Enables users to override the control factory
13728			t.onBeforeRenderUI.dispatch(t, t.controlManager);
13729
13730			// Measure box
13731			if (s.render_ui && t.theme) {
13732				t.orgDisplay = e.style.display;
13733
13734				if (typeof s.theme != "function") {
13735					w = s.width || e.style.width || e.offsetWidth;
13736					h = s.height || e.style.height || e.offsetHeight;
13737					mh = s.min_height || 100;
13738					re = /^[0-9\.]+(|px)$/i;
13739
13740					if (re.test('' + w))
13741						w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
13742
13743					if (re.test('' + h))
13744						h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), mh);
13745
13746					// Render UI
13747					o = t.theme.renderUI({
13748						targetNode : e,
13749						width : w,
13750						height : h,
13751						deltaWidth : s.delta_width,
13752						deltaHeight : s.delta_height
13753					});
13754
13755					// Resize editor
13756					DOM.setStyles(o.sizeContainer || o.editorContainer, {
13757						width : w,
13758						height : h
13759					});
13760
13761					h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
13762					if (h < mh)
13763						h = mh;
13764				} else {
13765					o = s.theme(t, e);
13766
13767					// Convert element type to id:s
13768					if (o.editorContainer.nodeType) {
13769						o.editorContainer = o.editorContainer.id = o.editorContainer.id || t.id + "_parent";
13770					}
13771
13772					// Convert element type to id:s
13773					if (o.iframeContainer.nodeType) {
13774						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || t.id + "_iframecontainer";
13775					}
13776
13777					// Use specified iframe height or the targets offsetHeight
13778					h = o.iframeHeight || e.offsetHeight;
13779
13780					// Store away the selection when it's changed to it can be restored later with a editor.focus() call
13781					if (isIE) {
13782						t.onInit.add(function(ed) {
13783							ed.dom.bind(ed.getBody(), 'beforedeactivate keydown keyup', function() {
13784								ed.bookmark = ed.selection.getBookmark(1);
13785							});
13786						});
13787
13788						t.onNodeChange.add(function(ed) {
13789							if (document.activeElement.id == ed.id + "_ifr") {
13790								ed.bookmark = ed.selection.getBookmark(1);
13791							}
13792						});
13793					}
13794				}
13795
13796				t.editorContainer = o.editorContainer;
13797			}
13798
13799			// Load specified content CSS last
13800			if (s.content_css) {
13801				each(explode(s.content_css), function(u) {
13802					t.contentCSS.push(t.documentBaseURI.toAbsolute(u));
13803				});
13804			}
13805
13806			// Load specified content CSS last
13807			if (s.content_style) {
13808				t.contentStyles.push(s.content_style);
13809			}
13810
13811			// Content editable mode ends here
13812			if (s.content_editable) {
13813				e = n = o = null; // Fix IE leak
13814				return t.initContentBody();
13815			}
13816
13817			// User specified a document.domain value
13818			if (document.domain && location.hostname != document.domain)
13819				tinymce.relaxedDomain = document.domain;
13820
13821			t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
13822
13823			// We only need to override paths if we have to
13824			// IE has a bug where it remove site absolute urls to relative ones if this is specified
13825			if (s.document_base_url != tinymce.documentBaseURL)
13826				t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';
13827
13828			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
13829			if (tinymce.isIE8) {
13830				if (s.ie7_compat)
13831					t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
13832				else
13833					t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';
13834			}
13835
13836			t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
13837
13838			// Load the CSS by injecting them into the HTML this will reduce "flicker"
13839			for (i = 0; i < t.contentCSS.length; i++) {
13840				t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';
13841			}
13842
13843			t.contentCSS = [];
13844
13845			bi = s.body_id || 'tinymce';
13846			if (bi.indexOf('=') != -1) {
13847				bi = t.getParam('body_id', '', 'hash');
13848				bi = bi[t.id] || bi;
13849			}
13850
13851			bc = s.body_class || '';
13852			if (bc.indexOf('=') != -1) {
13853				bc = t.getParam('body_class', '', 'hash');
13854				bc = bc[t.id] || '';
13855			}
13856
13857			t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';
13858
13859			// Domain relaxing enabled, then set document domain
13860			if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
13861				// We need to write the contents here in IE since multiple writes messes up refresh button and back button
13862				u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()';
13863			}
13864
13865			// Create iframe
13866			// TODO: ACC add the appropriate description on this.
13867			n = DOM.add(o.iframeContainer, 'iframe', { 
13868				id : t.id + "_ifr",
13869				src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
13870				frameBorder : '0',
13871				allowTransparency : "true",
13872				title : s.aria_label,
13873				style : {
13874					width : '100%',
13875					height : h,
13876					display : 'block' // Important for Gecko to render the iframe correctly
13877				}
13878			});
13879
13880			t.contentAreaContainer = o.iframeContainer;
13881
13882			if (o.editorContainer) {
13883				DOM.get(o.editorContainer).style.display = t.orgDisplay;
13884			}
13885
13886			// Restore visibility on target element
13887			e.style.visibility = t.orgVisibility;
13888
13889			DOM.get(t.id).style.display = 'none';
13890			DOM.setAttrib(t.id, 'aria-hidden', true);
13891
13892			if (!tinymce.relaxedDomain || !u)
13893				t.initContentBody();
13894
13895			e = n = o = null; // Cleanup
13896		},
13897
13898		initContentBody : function() {
13899			var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body, contentCssText;
13900
13901			// Setup iframe body
13902			if ((!isIE || !tinymce.relaxedDomain) && !settings.content_editable) {
13903				doc.open();
13904				doc.write(self.iframeHTML);
13905				doc.close();
13906
13907				if (tinymce.relaxedDomain)
13908					doc.domain = tinymce.relaxedDomain;
13909			}
13910
13911			if (settings.content_editable) {
13912				DOM.addClass(targetElm, 'mceContentBody');
13913				self.contentDocument = doc = settings.content_document || document;
13914				self.contentWindow = settings.content_window || window;
13915				self.bodyElement = targetElm;
13916
13917				// Prevent leak in IE
13918				settings.content_document = settings.content_window = null;
13919			}
13920
13921			// It will not steal focus while setting contentEditable
13922			body = self.getBody();
13923			body.disabled = true;
13924
13925			if (!settings.readonly)
13926				body.contentEditable = self.getParam('content_editable_state', true);
13927
13928			body.disabled = false;
13929
13930			self.schema = new tinymce.html.Schema(settings);
13931
13932			self.dom = new tinymce.dom.DOMUtils(doc, {
13933				keep_values : true,
13934				url_converter : self.convertURL,
13935				url_converter_scope : self,
13936				hex_colors : settings.force_hex_style_colors,
13937				class_filter : settings.class_filter,
13938				update_styles : true,
13939				root_element : settings.content_editable ? self.id : null,
13940				schema : self.schema
13941			});
13942
13943			self.parser = new tinymce.html.DomParser(settings, self.schema);
13944
13945			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
13946			self.parser.addAttributeFilter('src,href,style', function(nodes, name) {
13947				var i = nodes.length, node, dom = self.dom, value, internalName;
13948
13949				while (i--) {
13950					node = nodes[i];
13951					value = node.attr(name);
13952					internalName = 'data-mce-' + name;
13953
13954					// Add internal attribute if we need to we don't on a refresh of the document
13955					if (!node.attributes.map[internalName]) {	
13956						if (name === "style")
13957							node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
13958						else
13959							node.attr(internalName, self.convertURL(value, name, node.name));
13960					}
13961				}
13962			});
13963
13964			// Keep scripts from executing
13965			self.parser.addNodeFilter('script', function(nodes, name) {
13966				var i = nodes.length, node;
13967
13968				while (i--) {
13969					node = nodes[i];
13970					node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
13971				}
13972			});
13973
13974			self.parser.addNodeFilter('#cdata', function(nodes, name) {
13975				var i = nodes.length, node;
13976
13977				while (i--) {
13978					node = nodes[i];
13979					node.type = 8;
13980					node.name = '#comment';
13981					node.value = '[CDATA[' + node.value + ']]';
13982				}
13983			});
13984
13985			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
13986				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
13987
13988				while (i--) {
13989					node = nodes[i];
13990
13991					if (node.isEmpty(nonEmptyElements))
13992						node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;
13993				}
13994			});
13995
13996			self.serializer = new tinymce.dom.Serializer(settings, self.dom, self.schema);
13997
13998			self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer, self);
13999
14000			self.formatter = new tinymce.Formatter(self);
14001
14002			self.undoManager = new tinymce.UndoManager(self);
14003
14004			self.forceBlocks = new tinymce.ForceBlocks(self);
14005			self.enterKey = new tinymce.EnterKey(self);
14006			self.editorCommands = new tinymce.EditorCommands(self);
14007
14008			self.onExecCommand.add(function(editor, command) {
14009				// Don't refresh the select lists until caret move
14010				if (!/^(FontName|FontSize)$/.test(command))
14011					self.nodeChanged();
14012			});
14013
14014			// Pass through
14015			self.serializer.onPreProcess.add(function(se, o) {
14016				return self.onPreProcess.dispatch(self, o, se);
14017			});
14018
14019			self.serializer.onPostProcess.add(function(se, o) {
14020				return self.onPostProcess.dispatch(self, o, se);
14021			});
14022
14023			self.onPreInit.dispatch(self);
14024
14025			if (!settings.browser_spellcheck && !settings.gecko_spellcheck)
14026				doc.body.spellcheck = false;
14027
14028			if (!settings.readonly) {
14029				self.bindNativeEvents();
14030			}
14031
14032			self.controlManager.onPostRender.dispatch(self, self.controlManager);
14033			self.onPostRender.dispatch(self);
14034
14035			self.quirks = tinymce.util.Quirks(self);
14036
14037			if (settings.directionality)
14038				body.dir = settings.directionality;
14039
14040			if (settings.nowrap)
14041				body.style.whiteSpace = "nowrap";
14042
14043			if (settings.protect) {
14044				self.onBeforeSetContent.add(function(ed, o) {
14045					each(settings.protect, function(pattern) {
14046						o.content = o.content.replace(pattern, function(str) {
14047							return '<!--mce:protected ' + escape(str) + '-->';
14048						});
14049					});
14050				});
14051			}
14052
14053			// Add visual aids when new contents is added
14054			self.onSetContent.add(function() {
14055				self.addVisual(self.getBody());
14056			});
14057
14058			// Remove empty contents
14059			if (settings.padd_empty_editor) {
14060				self.onPostProcess.add(function(ed, o) {
14061					o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
14062				});
14063			}
14064
14065			self.load({initial : true, format : 'html'});
14066			self.startContent = self.getContent({format : 'raw'});
14067
14068			self.initialized = true;
14069
14070			self.onInit.dispatch(self);
14071			self.execCallback('setupcontent_callback', self.id, body, doc);
14072			self.execCallback('init_instance_callback', self);
14073			self.focus(true);
14074			self.nodeChanged({initial : true});
14075
14076			// Add editor specific CSS styles
14077			if (self.contentStyles.length > 0) {
14078				contentCssText = '';
14079
14080				each(self.contentStyles, function(style) {
14081					contentCssText += style + "\r\n";
14082				});
14083
14084				self.dom.addStyle(contentCssText);
14085			}
14086
14087			// Load specified content CSS last
14088			each(self.contentCSS, function(url) {
14089				self.dom.loadCSS(url);
14090			});
14091
14092			// Handle auto focus
14093			if (settings.auto_focus) {
14094				setTimeout(function () {
14095					var ed = tinymce.get(settings.auto_focus);
14096
14097					ed.selection.select(ed.getBody(), 1);
14098					ed.selection.collapse(1);
14099					ed.getBody().focus();
14100					ed.getWin().focus();
14101				}, 100);
14102			}
14103
14104			// Clean up references for IE
14105			targetElm = doc = body = null;
14106		},
14107
14108		focus : function(skip_focus) {
14109			var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, ieRng, controlElm, doc = self.getDoc(), body;
14110
14111			if (!skip_focus) {
14112				if (self.bookmark) {
14113					selection.moveToBookmark(self.bookmark);
14114					self.bookmark = null;
14115				}
14116
14117				// Get selected control element
14118				ieRng = selection.getRng();
14119				if (ieRng.item) {
14120					controlElm = ieRng.item(0);
14121				}
14122
14123				self._refreshContentEditable();
14124
14125				// Focus the window iframe
14126				if (!contentEditable) {
14127					self.getWin().focus();
14128				}
14129
14130				// Focus the body as well since it's contentEditable
14131				if (tinymce.isGecko || contentEditable) {
14132					body = self.getBody();
14133
14134					// Check for setActive since it doesn't scroll to the element
14135					if (body.setActive && ! tinymce.isIE11) {
14136						body.setActive();
14137					} else {
14138						body.focus();
14139					}
14140
14141					if (contentEditable) {
14142						selection.normalize();
14143					}
14144				}
14145
14146				// Restore selected control element
14147				// This is needed when for example an image is selected within a
14148				// layer a call to focus will then remove the control selection
14149				if (controlElm && controlElm.ownerDocument == doc) {
14150					ieRng = doc.body.createControlRange();
14151					ieRng.addElement(controlElm);
14152					ieRng.select();
14153				}
14154			}
14155
14156			if (tinymce.activeEditor != self) {
14157				if ((oed = tinymce.activeEditor) != null)
14158					oed.onDeactivate.dispatch(oed, self);
14159
14160				self.onActivate.dispatch(self, oed);
14161			}
14162
14163			tinymce._setActive(self);
14164		},
14165
14166		execCallback : function(n) {
14167			var t = this, f = t.settings[n], s;
14168
14169			if (!f)
14170				return;
14171
14172			// Look through lookup
14173			if (t.callbackLookup && (s = t.callbackLookup[n])) {
14174				f = s.func;
14175				s = s.scope;
14176			}
14177
14178			if (is(f, 'string')) {
14179				s = f.replace(/\.\w+$/, '');
14180				s = s ? tinymce.resolve(s) : 0;
14181				f = tinymce.resolve(f);
14182				t.callbackLookup = t.callbackLookup || {};
14183				t.callbackLookup[n] = {func : f, scope : s};
14184			}
14185
14186			return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
14187		},
14188
14189		translate : function(s) {
14190			var c = this.settings.language || 'en', i18n = tinymce.i18n;
14191
14192			if (!s)
14193				return '';
14194
14195			return i18n[c + '.' + s] || s.replace(/\{\#([^\}]+)\}/g, function(a, b) {
14196				return i18n[c + '.' + b] || '{#' + b + '}';
14197			});
14198		},
14199
14200		getLang : function(n, dv) {
14201			return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
14202		},
14203
14204		getParam : function(n, dv, ty) {
14205			var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
14206
14207			if (ty === 'hash') {
14208				o = {};
14209
14210				if (is(v, 'string')) {
14211					each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
14212						v = v.split('=');
14213
14214						if (v.length > 1)
14215							o[tr(v[0])] = tr(v[1]);
14216						else
14217							o[tr(v[0])] = tr(v);
14218					});
14219				} else
14220					o = v;
14221
14222				return o;
14223			}
14224
14225			return v;
14226		},
14227
14228		nodeChanged : function(o) {
14229			var self = this, selection = self.selection, node;
14230
14231			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
14232			if (self.initialized) {
14233				o = o || {};
14234
14235				// Get start node
14236				node = selection.getStart() || self.getBody();
14237				node = isIE && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
14238
14239				// Get parents and add them to object
14240				o.parents = [];
14241				self.dom.getParent(node, function(node) {
14242					if (node.nodeName == 'BODY')
14243						return true;
14244
14245					o.parents.push(node);
14246				});
14247
14248				self.onNodeChange.dispatch(
14249					self,
14250					o ? o.controlManager || self.controlManager : self.controlManager,
14251					node,
14252					selection.isCollapsed(),
14253					o
14254				);
14255			}
14256		},
14257
14258		addButton : function(name, settings) {
14259			var self = this;
14260
14261			self.buttons = self.buttons || {};
14262			self.buttons[name] = settings;
14263		},
14264
14265		addCommand : function(name, callback, scope) {
14266			this.execCommands[name] = {func : callback, scope : scope || this};
14267		},
14268
14269		addQueryStateHandler : function(name, callback, scope) {
14270			this.queryStateCommands[name] = {func : callback, scope : scope || this};
14271		},
14272
14273		addQueryValueHandler : function(name, callback, scope) {
14274			this.queryValueCommands[name] = {func : callback, scope : scope || this};
14275		},
14276
14277		addShortcut : function(pa, desc, cmd_func, sc) {
14278			var t = this, c;
14279
14280			if (t.settings.custom_shortcuts === false)
14281				return false;
14282
14283			t.shortcuts = t.shortcuts || {};
14284
14285			if (is(cmd_func, 'string')) {
14286				c = cmd_func;
14287
14288				cmd_func = function() {
14289					t.execCommand(c, false, null);
14290				};
14291			}
14292
14293			if (is(cmd_func, 'object')) {
14294				c = cmd_func;
14295
14296				cmd_func = function() {
14297					t.execCommand(c[0], c[1], c[2]);
14298				};
14299			}
14300
14301			each(explode(pa), function(pa) {
14302				var o = {
14303					func : cmd_func,
14304					scope : sc || this,
14305					desc : t.translate(desc),
14306					alt : false,
14307					ctrl : false,
14308					shift : false
14309				};
14310
14311				each(explode(pa, '+'), function(v) {
14312					switch (v) {
14313						case 'alt':
14314						case 'ctrl':
14315						case 'shift':
14316							o[v] = true;
14317							break;
14318
14319						default:
14320							o.charCode = v.charCodeAt(0);
14321							o.keyCode = v.toUpperCase().charCodeAt(0);
14322					}
14323				});
14324
14325				t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
14326			});
14327
14328			return true;
14329		},
14330
14331		execCommand : function(cmd, ui, val, a) {
14332			var t = this, s = 0, o, st;
14333
14334			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
14335				t.focus();
14336
14337			a = extend({}, a);
14338			t.onBeforeExecCommand.dispatch(t, cmd, ui, val, a);
14339			if (a.terminate)
14340				return false;
14341
14342			// Command callback
14343			if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
14344				t.onExecCommand.dispatch(t, cmd, ui, val, a);
14345				return true;
14346			}
14347
14348			// Registred commands
14349			if (o = t.execCommands[cmd]) {
14350				st = o.func.call(o.scope, ui, val);
14351
14352				// Fall through on true
14353				if (st !== true) {
14354					t.onExecCommand.dispatch(t, cmd, ui, val, a);
14355					return st;
14356				}
14357			}
14358
14359			// Plugin commands
14360			each(t.plugins, function(p) {
14361				if (p.execCommand && p.execCommand(cmd, ui, val)) {
14362					t.onExecCommand.dispatch(t, cmd, ui, val, a);
14363					s = 1;
14364					return false;
14365				}
14366			});
14367
14368			if (s)
14369				return true;
14370
14371			// Theme commands
14372			if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
14373				t.onExecCommand.dispatch(t, cmd, ui, val, a);
14374				return true;
14375			}
14376
14377			// Editor commands
14378			if (t.editorCommands.execCommand(cmd, ui, val)) {
14379				t.onExecCommand.dispatch(t, cmd, ui, val, a);
14380				return true;
14381			}
14382
14383			// Browser commands
14384			t.getDoc().execCommand(cmd, ui, val);
14385			t.onExecCommand.dispatch(t, cmd, ui, val, a);
14386		},
14387
14388		queryCommandState : function(cmd) {
14389			var t = this, o, s;
14390
14391			// Is hidden then return undefined
14392			if (t._isHidden())
14393				return;
14394
14395			// Registred commands
14396			if (o = t.queryStateCommands[cmd]) {
14397				s = o.func.call(o.scope);
14398
14399				// Fall though on true
14400				if (s !== true)
14401					return s;
14402			}
14403
14404			// Registred commands
14405			o = t.editorCommands.queryCommandState(cmd);
14406			if (o !== -1)
14407				return o;
14408
14409			// Browser commands
14410			try {
14411				return this.getDoc().queryCommandState(cmd);
14412			} catch (ex) {
14413				// Fails sometimes see bug: 1896577
14414			}
14415		},
14416
14417		queryCommandValue : function(c) {
14418			var t = this, o, s;
14419
14420			// Is hidden then return undefined
14421			if (t._isHidden())
14422				return;
14423
14424			// Registred commands
14425			if (o = t.queryValueCommands[c]) {
14426				s = o.func.call(o.scope);
14427
14428				// Fall though on true
14429				if (s !== true)
14430					return s;
14431			}
14432
14433			// Registred commands
14434			o = t.editorCommands.queryCommandValue(c);
14435			if (is(o))
14436				return o;
14437
14438			// Browser commands
14439			try {
14440				return this.getDoc().queryCommandValue(c);
14441			} catch (ex) {
14442				// Fails sometimes see bug: 1896577
14443			}
14444		},
14445
14446		show : function() {
14447			var self = this;
14448
14449			DOM.show(self.getContainer());
14450			DOM.hide(self.id);
14451			self.load();
14452		},
14453
14454		hide : function() {
14455			var self = this, doc = self.getDoc();
14456
14457			// Fixed bug where IE has a blinking cursor left from the editor
14458			if (isIE && doc)
14459				doc.execCommand('SelectAll');
14460
14461			// We must save before we hide so Safari doesn't crash
14462			self.save();
14463
14464			// defer the call to hide to prevent an IE9 crash #4921
14465			DOM.hide(self.getContainer());
14466			DOM.setStyle(self.id, 'display', self.orgDisplay);
14467		},
14468
14469		isHidden : function() {
14470			return !DOM.isHidden(this.id);
14471		},
14472
14473		setProgressState : function(b, ti, o) {
14474			this.onSetProgressState.dispatch(this, b, ti, o);
14475
14476			return b;
14477		},
14478
14479		load : function(o) {
14480			var t = this, e = t.getElement(), h;
14481
14482			if (e) {
14483				o = o || {};
14484				o.load = true;
14485
14486				// Double encode existing entities in the value
14487				h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
14488				o.element = e;
14489
14490				if (!o.no_events)
14491					t.onLoadContent.dispatch(t, o);
14492
14493				o.element = e = null;
14494
14495				return h;
14496			}
14497		},
14498
14499		save : function(o) {
14500			var t = this, e = t.getElement(), h, f;
14501
14502			if (!e || !t.initialized)
14503				return;
14504
14505			o = o || {};
14506			o.save = true;
14507
14508			o.element = e;
14509			h = o.content = t.getContent(o);
14510
14511			if (!o.no_events)
14512				t.onSaveContent.dispatch(t, o);
14513
14514			h = o.content;
14515
14516			if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
14517				e.innerHTML = h;
14518
14519				// Update hidden form element
14520				if (f = DOM.getParent(t.id, 'form')) {
14521					each(f.elements, function(e) {
14522						if (e.name == t.id) {
14523							e.value = h;
14524							return false;
14525						}
14526					});
14527				}
14528			} else
14529				e.value = h;
14530
14531			o.element = e = null;
14532
14533			return h;
14534		},
14535
14536		setContent : function(content, args) {
14537			var self = this, rootNode, body = self.getBody(), forcedRootBlockName;
14538
14539			// Setup args object
14540			args = args || {};
14541			args.format = args.format || 'html';
14542			args.set = true;
14543			args.content = content;
14544
14545			// Do preprocessing
14546			if (!args.no_events)
14547				self.onBeforeSetContent.dispatch(self, args);
14548
14549			content = args.content;
14550
14551			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
14552			// It will also be impossible to place the caret in the editor unless there is a BR element present
14553			if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {
14554				forcedRootBlockName = self.settings.forced_root_block;
14555				if (forcedRootBlockName)
14556					content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';
14557				else
14558					content = '<br data-mce-bogus="1">';
14559
14560				body.innerHTML = content;
14561				self.selection.select(body, true);
14562				self.selection.collapse(true);
14563				return;
14564			}
14565
14566			// Parse and serialize the html
14567			if (args.format !== 'raw') {
14568				content = new tinymce.html.Serializer({}, self.schema).serialize(
14569					self.parser.parse(content)
14570				);
14571			}
14572
14573			// Set the new cleaned contents to the editor
14574			args.content = tinymce.trim(content);
14575			self.dom.setHTML(body, args.content);
14576
14577			// Do post processing
14578			if (!args.no_events)
14579				self.onSetContent.dispatch(self, args);
14580
14581			// Don't normalize selection if the focused element isn't the body in content editable mode since it will steal focus otherwise
14582			if (!self.settings.content_editable || document.activeElement === self.getBody()) {
14583				self.selection.normalize();
14584			}
14585
14586			return args.content;
14587		},
14588
14589		getContent : function(args) {
14590			var self = this, content, body = self.getBody();
14591
14592			// Setup args object
14593			args = args || {};
14594			args.format = args.format || 'html';
14595			args.get = true;
14596			args.getInner = true;
14597
14598			// Do preprocessing
14599			if (!args.no_events)
14600				self.onBeforeGetContent.dispatch(self, args);
14601
14602			// Get raw contents or by default the cleaned contents
14603			if (args.format == 'raw')
14604				content = body.innerHTML;
14605			else if (args.format == 'text')
14606				content = body.innerText || body.textContent;
14607			else
14608				content = self.serializer.serialize(body, args);
14609
14610			// Trim whitespace in beginning/end of HTML
14611			if (args.format != 'text') {
14612				args.content = tinymce.trim(content);
14613			} else {
14614				args.content = content;
14615			}
14616
14617			// Do post processing
14618			if (!args.no_events)
14619				self.onGetContent.dispatch(self, args);
14620
14621			return args.content;
14622		},
14623
14624		isDirty : function() {
14625			var self = this;
14626
14627			return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;
14628		},
14629
14630		getContainer : function() {
14631			var self = this;
14632
14633			if (!self.container)
14634				self.container = DOM.get(self.editorContainer || self.id + '_parent');
14635
14636			return self.container;
14637		},
14638
14639		getContentAreaContainer : function() {
14640			return this.contentAreaContainer;
14641		},
14642
14643		getElement : function() {
14644			return DOM.get(this.settings.content_element || this.id);
14645		},
14646
14647		getWin : function() {
14648			var self = this, elm;
14649
14650			if (!self.contentWindow) {
14651				elm = DOM.get(self.id + "_ifr");
14652
14653				if (elm)
14654					self.contentWindow = elm.contentWindow;
14655			}
14656
14657			return self.contentWindow;
14658		},
14659
14660		getDoc : function() {
14661			var self = this, win;
14662
14663			if (!self.contentDocument) {
14664				win = self.getWin();
14665
14666				if (win)
14667					self.contentDocument = win.document;
14668			}
14669
14670			return self.contentDocument;
14671		},
14672
14673		getBody : function() {
14674			return this.bodyElement || this.getDoc().body;
14675		},
14676
14677		convertURL : function(url, name, elm) {
14678			var self = this, settings = self.settings;
14679
14680			// Use callback instead
14681			if (settings.urlconverter_callback)
14682				return self.execCallback('urlconverter_callback', url, elm, true, name);
14683
14684			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
14685			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0)
14686				return url;
14687
14688			// Convert to relative
14689			if (settings.relative_urls)
14690				return self.documentBaseURI.toRelative(url);
14691
14692			// Convert to absolute
14693			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
14694
14695			return url;
14696		},
14697
14698		addVisual : function(elm) {
14699			var self = this, settings = self.settings, dom = self.dom, cls;
14700
14701			elm = elm || self.getBody();
14702
14703			if (!is(self.hasVisual))
14704				self.hasVisual = settings.visual;
14705
14706			each(dom.select('table,a', elm), function(elm) {
14707				var value;
14708
14709				switch (elm.nodeName) {
14710					case 'TABLE':
14711						cls = settings.visual_table_class || 'mceItemTable';
14712						value = dom.getAttrib(elm, 'border');
14713
14714						if (!value || value == '0') {
14715							if (self.hasVisual)
14716								dom.addClass(elm, cls);
14717							else
14718								dom.removeClass(elm, cls);
14719						}
14720
14721						return;
14722
14723					case 'A':
14724						if (!dom.getAttrib(elm, 'href', false)) {
14725							value = dom.getAttrib(elm, 'name') || elm.id;
14726							cls = 'mceItemAnchor';
14727
14728							if (value) {
14729								if (self.hasVisual)
14730									dom.addClass(elm, cls);
14731								else
14732									dom.removeClass(elm, cls);
14733							}
14734						}
14735
14736						return;
14737				}
14738			});
14739
14740			self.onVisualAid.dispatch(self, elm, self.hasVisual);
14741		},
14742
14743		remove : function() {
14744			var self = this, elm = self.getContainer(), doc = self.getDoc();
14745
14746			if (!self.removed) {
14747				self.removed = 1; // Cancels post remove event execution
14748
14749				// Fixed bug where IE has a blinking cursor left from the editor
14750				if (isIE && doc)
14751					doc.execCommand('SelectAll');
14752
14753				// We must save before we hide so Safari doesn't crash
14754				self.save();
14755
14756				DOM.setStyle(self.id, 'display', self.orgDisplay);
14757
14758				// Don't clear the window or document if content editable
14759				// is enabled since other instances might still be present
14760				if (!self.settings.content_editable) {
14761					Event.unbind(self.getWin());
14762					Event.unbind(self.getDoc());
14763				}
14764
14765				Event.unbind(self.getBody());
14766				Event.clear(elm);
14767
14768				self.execCallback('remove_instance_callback', self);
14769				self.onRemove.dispatch(self);
14770
14771				// Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
14772				self.onExecCommand.listeners = [];
14773
14774				tinymce.remove(self);
14775				DOM.remove(elm);
14776			}
14777		},
14778
14779		destroy : function(s) {
14780			var t = this;
14781
14782			// One time is enough
14783			if (t.destroyed)
14784				return;
14785
14786			// We must unbind on Gecko since it would otherwise produce the pesky "attempt to run compile-and-go script on a cleared scope" message
14787			if (isGecko) {
14788				Event.unbind(t.getDoc());
14789				Event.unbind(t.getWin());
14790				Event.unbind(t.getBody());
14791			}
14792
14793			if (!s) {
14794				tinymce.removeUnload(t.destroy);
14795				tinyMCE.onBeforeUnload.remove(t._beforeUnload);
14796
14797				// Manual destroy
14798				if (t.theme && t.theme.destroy)
14799					t.theme.destroy();
14800
14801				// Destroy controls, selection and dom
14802				t.controlManager.destroy();
14803				t.selection.destroy();
14804				t.dom.destroy();
14805			}
14806
14807			if (t.formElement) {
14808				t.formElement.submit = t.formElement._mceOldSubmit;
14809				t.formElement._mceOldSubmit = null;
14810			}
14811
14812			t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
14813
14814			if (t.selection)
14815				t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
14816
14817			t.destroyed = 1;
14818		},
14819
14820		// Internal functions
14821
14822		_refreshContentEditable : function() {
14823			var self = this, body, parent;
14824
14825			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
14826			if (self._isHidden()) {
14827				body = self.getBody();
14828				parent = body.parentNode;
14829
14830				parent.removeChild(body);
14831				parent.appendChild(body);
14832
14833				body.focus();
14834			}
14835		},
14836
14837		_isHidden : function() {
14838			var s;
14839
14840			if (!isGecko)
14841				return 0;
14842
14843			// Weird, wheres that cursor selection?
14844			s = this.selection.getSel();
14845			return (!s || !s.rangeCount || s.rangeCount === 0);
14846		}
14847	});
14848})(tinymce);
14849(function(tinymce) {
14850	var each = tinymce.each;
14851
14852	tinymce.Editor.prototype.setupEvents = function() {
14853		var self = this, settings = self.settings;
14854
14855		// Add events to the editor
14856		each([
14857			'onPreInit',
14858
14859			'onBeforeRenderUI',
14860
14861			'onPostRender',
14862
14863			'onLoad',
14864
14865			'onInit',
14866
14867			'onRemove',
14868
14869			'onActivate',
14870
14871			'onDeactivate',
14872
14873			'onClick',
14874
14875			'onEvent',
14876
14877			'onMouseUp',
14878
14879			'onMouseDown',
14880
14881			'onDblClick',
14882
14883			'onKeyDown',
14884
14885			'onKeyUp',
14886
14887			'onKeyPress',
14888
14889			'onContextMenu',
14890
14891			'onSubmit',
14892
14893			'onReset',
14894
14895			'onPaste',
14896
14897			'onPreProcess',
14898
14899			'onPostProcess',
14900
14901			'onBeforeSetContent',
14902
14903			'onBeforeGetContent',
14904
14905			'onSetContent',
14906
14907			'onGetContent',
14908
14909			'onLoadContent',
14910
14911			'onSaveContent',
14912
14913			'onNodeChange',
14914
14915			'onChange',
14916
14917			'onBeforeExecCommand',
14918
14919			'onExecCommand',
14920
14921			'onUndo',
14922
14923			'onRedo',
14924
14925			'onVisualAid',
14926
14927			'onSetProgressState',
14928
14929			'onSetAttrib'
14930		], function(name) {
14931			self[name] = new tinymce.util.Dispatcher(self);
14932		});
14933
14934		// Handle legacy cleanup_callback option
14935		if (settings.cleanup_callback) {
14936			self.onBeforeSetContent.add(function(ed, o) {
14937				o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
14938			});
14939
14940			self.onPreProcess.add(function(ed, o) {
14941				if (o.set)
14942					ed.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
14943
14944				if (o.get)
14945					ed.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
14946			});
14947
14948			self.onPostProcess.add(function(ed, o) {
14949				if (o.set)
14950					o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
14951
14952				if (o.get)						
14953					o.content = ed.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
14954			});
14955		}
14956
14957		// Handle legacy save_callback option
14958		if (settings.save_callback) {
14959			self.onGetContent.add(function(ed, o) {
14960				if (o.save)
14961					o.content = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());
14962			});
14963		}
14964
14965		// Handle legacy handle_event_callback option
14966		if (settings.handle_event_callback) {
14967			self.onEvent.add(function(ed, e, o) {
14968				if (self.execCallback('handle_event_callback', e, ed, o) === false) {
14969					e.preventDefault();
14970					e.stopPropagation();
14971				}
14972			});
14973		}
14974
14975		// Handle legacy handle_node_change_callback option
14976		if (settings.handle_node_change_callback) {
14977			self.onNodeChange.add(function(ed, cm, n) {
14978				ed.execCallback('handle_node_change_callback', ed.id, n, -1, -1, true, ed.selection.isCollapsed());
14979			});
14980		}
14981
14982		// Handle legacy save_callback option
14983		if (settings.save_callback) {
14984			self.onSaveContent.add(function(ed, o) {
14985				var h = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());
14986
14987				if (h)
14988					o.content = h;
14989			});
14990		}
14991
14992		// Handle legacy onchange_callback option
14993		if (settings.onchange_callback) {
14994			self.onChange.add(function(ed, l) {
14995				ed.execCallback('onchange_callback', ed, l);
14996			});
14997		}
14998	};
14999
15000	tinymce.Editor.prototype.bindNativeEvents = function() {
15001		// 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
15002		var self = this, i, settings = self.settings, dom = self.dom, nativeToDispatcherMap;
15003
15004		nativeToDispatcherMap = {
15005			mouseup : 'onMouseUp',
15006			mousedown : 'onMouseDown',
15007			click : 'onClick',
15008			keyup : 'onKeyUp',
15009			keydown : 'onKeyDown',
15010			keypress : 'onKeyPress',
15011			submit : 'onSubmit',
15012			reset : 'onReset',
15013			contextmenu : 'onContextMenu',
15014			dblclick : 'onDblClick',
15015			paste : 'onPaste' // Doesn't work in all browsers yet
15016		};
15017
15018		// Handler that takes a native event and sends it out to a dispatcher like onKeyDown
15019		function eventHandler(evt, args) {
15020			var type = evt.type;
15021
15022			// Don't fire events when it's removed
15023			if (self.removed)
15024				return;
15025
15026			// Sends the native event out to a global dispatcher then to the specific event dispatcher
15027			if (self.onEvent.dispatch(self, evt, args) !== false) {
15028				self[nativeToDispatcherMap[evt.fakeType || evt.type]].dispatch(self, evt, args);
15029			}
15030		};
15031
15032		// Opera doesn't support focus event for contentEditable elements so we need to fake it
15033		function doOperaFocus(e) {
15034			self.focus(true);
15035		};
15036
15037		function nodeChanged(ed, e) {
15038			// 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
15039			if (e.keyCode != 65 || !tinymce.VK.metaKeyPressed(e)) {
15040				self.selection.normalize();
15041			}
15042
15043			self.nodeChanged();
15044		}
15045
15046		// Add DOM events
15047		each(nativeToDispatcherMap, function(dispatcherName, nativeName) {
15048			var root = settings.content_editable ? self.getBody() : self.getDoc();
15049
15050			switch (nativeName) {
15051				case 'contextmenu':
15052					dom.bind(root, nativeName, eventHandler);
15053					break;
15054
15055				case 'paste':
15056					dom.bind(self.getBody(), nativeName, eventHandler);
15057					break;
15058
15059				case 'submit':
15060				case 'reset':
15061					dom.bind(self.getElement().form || tinymce.DOM.getParent(self.id, 'form'), nativeName, eventHandler);
15062					break;
15063
15064				default:
15065					dom.bind(root, nativeName, eventHandler);
15066			}
15067		});
15068
15069		// Set the editor as active when focused
15070		dom.bind(settings.content_editable ? self.getBody() : (tinymce.isGecko ? self.getDoc() : self.getWin()), 'focus', function(e) {
15071			self.focus(true);
15072		});
15073
15074		if (settings.content_editable && tinymce.isOpera) {
15075			dom.bind(self.getBody(), 'click', doOperaFocus);
15076			dom.bind(self.getBody(), 'keydown', doOperaFocus);
15077		}
15078
15079		// Add node change handler
15080		self.onMouseUp.add(nodeChanged);
15081
15082		self.onKeyUp.add(function(ed, e) {
15083			var keyCode = e.keyCode;
15084
15085			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey)
15086				nodeChanged(ed, e);
15087		});
15088
15089		// Add reset handler
15090		self.onReset.add(function() {
15091			self.setContent(self.startContent, {format : 'raw'});
15092		});
15093
15094		// Add shortcuts
15095		function handleShortcut(e, execute) {
15096			if (e.altKey || e.ctrlKey || e.metaKey) {
15097				each(self.shortcuts, function(shortcut) {
15098					var ctrlState = tinymce.isMac ? e.metaKey : e.ctrlKey;
15099
15100					if (shortcut.ctrl != ctrlState || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey)
15101						return;
15102
15103					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
15104						e.preventDefault();
15105
15106						if (execute) {
15107							shortcut.func.call(shortcut.scope);
15108						}
15109
15110						return true;
15111					}
15112				});
15113			}
15114		};
15115
15116		self.onKeyUp.add(function(ed, e) {
15117			handleShortcut(e);
15118		});
15119
15120		self.onKeyPress.add(function(ed, e) {
15121			handleShortcut(e);
15122		});
15123
15124		self.onKeyDown.add(function(ed, e) {
15125			handleShortcut(e, true);
15126		});
15127
15128		if (tinymce.isOpera) {
15129			self.onClick.add(function(ed, e) {
15130				e.preventDefault();
15131			});
15132		}
15133	};
15134})(tinymce);
15135(function(tinymce) {
15136	// Added for compression purposes
15137	var each = tinymce.each, undef, TRUE = true, FALSE = false;
15138
15139	tinymce.EditorCommands = function(editor) {
15140		var dom = editor.dom,
15141			selection = editor.selection,
15142			commands = {state: {}, exec : {}, value : {}},
15143			settings = editor.settings,
15144			formatter = editor.formatter,
15145			bookmark;
15146
15147		function execCommand(command, ui, value) {
15148			var func;
15149
15150			command = command.toLowerCase();
15151			if (func = commands.exec[command]) {
15152				func(command, ui, value);
15153				return TRUE;
15154			}
15155
15156			return FALSE;
15157		};
15158
15159		function queryCommandState(command) {
15160			var func;
15161
15162			command = command.toLowerCase();
15163			if (func = commands.state[command])
15164				return func(command);
15165
15166			return -1;
15167		};
15168
15169		function queryCommandValue(command) {
15170			var func;
15171
15172			command = command.toLowerCase();
15173			if (func = commands.value[command])
15174				return func(command);
15175
15176			return FALSE;
15177		};
15178
15179		function addCommands(command_list, type) {
15180			type = type || 'exec';
15181
15182			each(command_list, function(callback, command) {
15183				each(command.toLowerCase().split(','), function(command) {
15184					commands[type][command] = callback;
15185				});
15186			});
15187		};
15188
15189		// Expose public methods
15190		tinymce.extend(this, {
15191			execCommand : execCommand,
15192			queryCommandState : queryCommandState,
15193			queryCommandValue : queryCommandValue,
15194			addCommands : addCommands
15195		});
15196
15197		// Private methods
15198
15199		function execNativeCommand(command, ui, value) {
15200			if (ui === undef)
15201				ui = FALSE;
15202
15203			if (value === undef)
15204				value = null;
15205
15206			return editor.getDoc().execCommand(command, ui, value);
15207		};
15208
15209		function isFormatMatch(name) {
15210			return formatter.match(name);
15211		};
15212
15213		function toggleFormat(name, value) {
15214			formatter.toggle(name, value ? {value : value} : undef);
15215		};
15216
15217		function storeSelection(type) {
15218			bookmark = selection.getBookmark(type);
15219		};
15220
15221		function restoreSelection() {
15222			selection.moveToBookmark(bookmark);
15223		};
15224
15225		// Add execCommand overrides
15226		addCommands({
15227			// Ignore these, added for compatibility
15228			'mceResetDesignMode,mceBeginUndoLevel' : function() {},
15229
15230			// Add undo manager logic
15231			'mceEndUndoLevel,mceAddUndoLevel' : function() {
15232				editor.undoManager.add();
15233			},
15234
15235			'Cut,Copy,Paste' : function(command) {
15236				var doc = editor.getDoc(), failed;
15237
15238				// Try executing the native command
15239				try {
15240					execNativeCommand(command);
15241				} catch (ex) {
15242					// Command failed
15243					failed = TRUE;
15244				}
15245
15246				// Present alert message about clipboard access not being available
15247				if (failed || !doc.queryCommandSupported(command)) {
15248					if (tinymce.isGecko) {
15249						editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {
15250							if (state)
15251								open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');
15252						});
15253					} else
15254						editor.windowManager.alert(editor.getLang('clipboard_no_support'));
15255				}
15256			},
15257
15258			// Override unlink command
15259			unlink : function(command) {
15260				if (selection.isCollapsed())
15261					selection.select(selection.getNode());
15262
15263				execNativeCommand(command);
15264				selection.collapse(FALSE);
15265			},
15266
15267			// Override justify commands to use the text formatter engine
15268			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {
15269				var align = command.substring(7);
15270
15271				// Remove all other alignments first
15272				each('left,center,right,full'.split(','), function(name) {
15273					if (align != name)
15274						formatter.remove('align' + name);
15275				});
15276
15277				toggleFormat('align' + align);
15278				execCommand('mceRepaint');
15279			},
15280
15281			// Override list commands to fix WebKit bug
15282			'InsertUnorderedList,InsertOrderedList' : function(command) {
15283				var listElm, listParent;
15284
15285				execNativeCommand(command);
15286
15287				// WebKit produces lists within block elements so we need to split them
15288				// we will replace the native list creation logic to custom logic later on
15289				// TODO: Remove this when the list creation logic is removed
15290				listElm = dom.getParent(selection.getNode(), 'ol,ul');
15291				if (listElm) {
15292					listParent = listElm.parentNode;
15293
15294					// If list is within a text block then split that block
15295					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
15296						storeSelection();
15297						dom.split(listParent, listElm);
15298						restoreSelection();
15299					}
15300				}
15301			},
15302
15303			// Override commands to use the text formatter engine
15304			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {
15305				toggleFormat(command);
15306			},
15307
15308			// Override commands to use the text formatter engine
15309			'ForeColor,HiliteColor,FontName' : function(command, ui, value) {
15310				toggleFormat(command, value);
15311			},
15312
15313			FontSize : function(command, ui, value) {
15314				var fontClasses, fontSizes;
15315
15316				// Convert font size 1-7 to styles
15317				if (value >= 1 && value <= 7) {
15318					fontSizes = tinymce.explode(settings.font_size_style_values);
15319					fontClasses = tinymce.explode(settings.font_size_classes);
15320
15321					if (fontClasses)
15322						value = fontClasses[value - 1] || value;
15323					else
15324						value = fontSizes[value - 1] || value;
15325				}
15326
15327				toggleFormat(command, value);
15328			},
15329
15330			RemoveFormat : function(command) {
15331				formatter.remove(command);
15332			},
15333
15334			mceBlockQuote : function(command) {
15335				toggleFormat('blockquote');
15336			},
15337
15338			FormatBlock : function(command, ui, value) {
15339				return toggleFormat(value || 'p');
15340			},
15341
15342			mceCleanup : function() {
15343				var bookmark = selection.getBookmark();
15344
15345				editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});
15346
15347				selection.moveToBookmark(bookmark);
15348			},
15349
15350			mceRemoveNode : function(command, ui, value) {
15351				var node = value || selection.getNode();
15352
15353				// Make sure that the body node isn't removed
15354				if (node != editor.getBody()) {
15355					storeSelection();
15356					editor.dom.remove(node, TRUE);
15357					restoreSelection();
15358				}
15359			},
15360
15361			mceSelectNodeDepth : function(command, ui, value) {
15362				var counter = 0;
15363
15364				dom.getParent(selection.getNode(), function(node) {
15365					if (node.nodeType == 1 && counter++ == value) {
15366						selection.select(node);
15367						return FALSE;
15368					}
15369				}, editor.getBody());
15370			},
15371
15372			mceSelectNode : function(command, ui, value) {
15373				selection.select(value);
15374			},
15375
15376			mceInsertContent : function(command, ui, value) {
15377				var parser, serializer, parentNode, rootNode, fragment, args,
15378					marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;
15379
15380				//selection.normalize();
15381
15382				// Setup parser and serializer
15383				parser = editor.parser;
15384				serializer = new tinymce.html.Serializer({}, editor.schema);
15385				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';
15386
15387				// Run beforeSetContent handlers on the HTML to be inserted
15388				args = {content: value, format: 'html'};
15389				selection.onBeforeSetContent.dispatch(selection, args);
15390				value = args.content;
15391
15392				// Add caret at end of contents if it's missing
15393				if (value.indexOf('{$caret}') == -1)
15394					value += '{$caret}';
15395
15396				// Replace the caret marker with a span bookmark element
15397				value = value.replace(/\{\$caret\}/, bookmarkHtml);
15398
15399				// Insert node maker where we will insert the new HTML and get it's parent
15400				if (!selection.isCollapsed())
15401					editor.getDoc().execCommand('Delete', false, null);
15402
15403				parentNode = selection.getNode();
15404
15405				// Parse the fragment within the context of the parent node
15406				args = {context : parentNode.nodeName.toLowerCase()};
15407				fragment = parser.parse(value, args);
15408
15409				// Move the caret to a more suitable location
15410				node = fragment.lastChild;
15411				if (node.attr('id') == 'mce_marker') {
15412					marker = node;
15413
15414					for (node = node.prev; node; node = node.walk(true)) {
15415						if (node.type == 3 || !dom.isBlock(node.name)) {
15416							node.parent.insert(marker, node, node.name === 'br');
15417							break;
15418						}
15419					}
15420				}
15421
15422				// If parser says valid we can insert the contents into that parent
15423				if (!args.invalid) {
15424					value = serializer.serialize(fragment);
15425
15426					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
15427					node = parentNode.firstChild;
15428					node2 = parentNode.lastChild;
15429					if (!node || (node === node2 && node.nodeName === 'BR'))
15430						dom.setHTML(parentNode, value);
15431					else
15432						selection.setContent(value);
15433				} else {
15434					// If the fragment was invalid within that context then we need
15435					// to parse and process the parent it's inserted into
15436
15437					// Insert bookmark node and get the parent
15438					selection.setContent(bookmarkHtml);
15439					parentNode = selection.getNode();
15440					rootNode = editor.getBody();
15441
15442					// Opera will return the document node when selection is in root
15443					if (parentNode.nodeType == 9)
15444						parentNode = node = rootNode;
15445					else
15446						node = parentNode;
15447
15448					// Find the ancestor just before the root element
15449					while (node !== rootNode) {
15450						parentNode = node;
15451						node = node.parentNode;
15452					}
15453
15454					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
15455					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
15456					value = serializer.serialize(
15457						parser.parse(
15458							// Need to replace by using a function since $ in the contents would otherwise be a problem
15459							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
15460								return serializer.serialize(fragment);
15461							})
15462						)
15463					);
15464
15465					// Set the inner/outer HTML depending on if we are in the root or not
15466					if (parentNode == rootNode)
15467						dom.setHTML(rootNode, value);
15468					else
15469						dom.setOuterHTML(parentNode, value);
15470				}
15471
15472				marker = dom.get('mce_marker');
15473
15474				// Scroll range into view scrollIntoView on element can't be used since it will scroll the main view port as well
15475				nodeRect = dom.getRect(marker);
15476				viewPortRect = dom.getViewPort(editor.getWin());
15477
15478				// Check if node is out side the viewport if it is then scroll to it
15479				if ((nodeRect.y + nodeRect.h > viewPortRect.y + viewPortRect.h || nodeRect.y < viewPortRect.y) ||
15480					(nodeRect.x > viewPortRect.x + viewPortRect.w || nodeRect.x < viewPortRect.x)) {
15481					viewportBodyElement = tinymce.isIE ? editor.getDoc().documentElement : editor.getBody();
15482					viewportBodyElement.scrollLeft = nodeRect.x;
15483					viewportBodyElement.scrollTop = nodeRect.y - viewPortRect.h + 25;
15484				}
15485
15486				// Move selection before marker and remove it
15487				rng = dom.createRng();
15488
15489				// If previous sibling is a text node set the selection to the end of that node
15490				node = marker.previousSibling;
15491				if (node && node.nodeType == 3) {
15492					rng.setStart(node, node.nodeValue.length);
15493				} else {
15494					// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
15495					rng.setStartBefore(marker);
15496					rng.setEndBefore(marker);
15497				}
15498
15499				// Remove the marker node and set the new range
15500				dom.remove(marker);
15501				selection.setRng(rng);
15502
15503				// Dispatch after event and add any visual elements needed
15504				selection.onSetContent.dispatch(selection, args);
15505				editor.addVisual();
15506			},
15507
15508			mceInsertRawHTML : function(command, ui, value) {
15509				selection.setContent('tiny_mce_marker');
15510				editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));
15511			},
15512
15513			mceToggleFormat : function(command, ui, value) {
15514				toggleFormat(value);
15515			},
15516
15517			mceSetContent : function(command, ui, value) {
15518				editor.setContent(value);
15519			},
15520
15521			'Indent,Outdent' : function(command) {
15522				var intentValue, indentUnit, value;
15523
15524				// Setup indent level
15525				intentValue = settings.indentation;
15526				indentUnit = /[a-z%]+$/i.exec(intentValue);
15527				intentValue = parseInt(intentValue);
15528
15529				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
15530					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
15531					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
15532						formatter.apply('div');
15533					}
15534
15535					each(selection.getSelectedBlocks(), function(element) {
15536						if (command == 'outdent') {
15537							value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);
15538							dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');
15539						} else
15540							dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);
15541					});
15542				} else
15543					execNativeCommand(command);
15544			},
15545
15546			mceRepaint : function() {
15547				var bookmark;
15548
15549				if (tinymce.isGecko) {
15550					try {
15551						storeSelection(TRUE);
15552
15553						if (selection.getSel())
15554							selection.getSel().selectAllChildren(editor.getBody());
15555
15556						selection.collapse(TRUE);
15557						restoreSelection();
15558					} catch (ex) {
15559						// Ignore
15560					}
15561				}
15562			},
15563
15564			mceToggleFormat : function(command, ui, value) {
15565				formatter.toggle(value);
15566			},
15567
15568			InsertHorizontalRule : function() {
15569				editor.execCommand('mceInsertContent', false, '<hr />');
15570			},
15571
15572			mceToggleVisualAid : function() {
15573				editor.hasVisual = !editor.hasVisual;
15574				editor.addVisual();
15575			},
15576
15577			mceReplaceContent : function(command, ui, value) {
15578				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));
15579			},
15580
15581			mceInsertLink : function(command, ui, value) {
15582				var anchor;
15583
15584				if (typeof(value) == 'string')
15585					value = {href : value};
15586
15587				anchor = dom.getParent(selection.getNode(), 'a');
15588
15589				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
15590				value.href = value.href.replace(' ', '%20');
15591
15592				// Remove existing links if there could be child links or that the href isn't specified
15593				if (!anchor || !value.href) {
15594					formatter.remove('link');
15595				}		
15596
15597				// Apply new link to selection
15598				if (value.href) {
15599					formatter.apply('link', value, anchor);
15600				}
15601			},
15602
15603			selectAll : function() {
15604				var root = dom.getRoot(), rng = dom.createRng();
15605
15606				// Old IE does a better job with selectall than new versions
15607				if (selection.getRng().setStart) {
15608					rng.setStart(root, 0);
15609					rng.setEnd(root, root.childNodes.length);
15610
15611					selection.setRng(rng);
15612				} else {
15613					execNativeCommand('SelectAll');
15614				}
15615			}
15616		});
15617
15618		// Add queryCommandState overrides
15619		addCommands({
15620			// Override justify commands
15621			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {
15622				var name = 'align' + command.substring(7);
15623				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
15624				var matches = tinymce.map(nodes, function(node) {
15625					return !!formatter.matchNode(node, name);
15626				});
15627				return tinymce.inArray(matches, TRUE) !== -1;
15628			},
15629
15630			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {
15631				return isFormatMatch(command);
15632			},
15633
15634			mceBlockQuote : function() {
15635				return isFormatMatch('blockquote');
15636			},
15637
15638			Outdent : function() {
15639				var node;
15640
15641				if (settings.inline_styles) {
15642					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)
15643						return TRUE;
15644
15645					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)
15646						return TRUE;
15647				}
15648
15649				return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));
15650			},
15651
15652			'InsertUnorderedList,InsertOrderedList' : function(command) {
15653				var list = dom.getParent(selection.getNode(), 'ul,ol');
15654				return list && 
15655				     (command === 'insertunorderedlist' && list.tagName === 'UL'
15656				   || command === 'insertorderedlist' && list.tagName === 'OL');
15657			}
15658		}, 'state');
15659
15660		// Add queryCommandValue overrides
15661		addCommands({
15662			'FontSize,FontName' : function(command) {
15663				var value = 0, parent;
15664
15665				if (parent = dom.getParent(selection.getNode(), 'span')) {
15666					if (command == 'fontsize')
15667						value = parent.style.fontSize;
15668					else
15669						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
15670				}
15671
15672				return value;
15673			}
15674		}, 'value');
15675
15676		// Add undo manager logic
15677		addCommands({
15678			Undo : function() {
15679				editor.undoManager.undo();
15680			},
15681
15682			Redo : function() {
15683				editor.undoManager.redo();
15684			}
15685		});
15686	};
15687})(tinymce);
15688
15689(function(tinymce) {
15690	var Dispatcher = tinymce.util.Dispatcher;
15691
15692	tinymce.UndoManager = function(editor) {
15693		var self, index = 0, data = [], beforeBookmark, onAdd, onUndo, onRedo;
15694
15695		function getContent() {
15696			// Remove whitespace before/after and remove pure bogus nodes
15697			return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g, ''));
15698		};
15699
15700		function addNonTypingUndoLevel() {
15701			self.typing = false;
15702			self.add();
15703		};
15704
15705		// Create event instances
15706		onBeforeAdd = new Dispatcher(self);
15707		onAdd       = new Dispatcher(self);
15708		onUndo      = new Dispatcher(self);
15709		onRedo      = new Dispatcher(self);
15710
15711		// Pass though onAdd event from UndoManager to Editor as onChange
15712		onAdd.add(function(undoman, level) {
15713			if (undoman.hasUndo())
15714				return editor.onChange.dispatch(editor, level, undoman);
15715		});
15716
15717		// Pass though onUndo event from UndoManager to Editor
15718		onUndo.add(function(undoman, level) {
15719			return editor.onUndo.dispatch(editor, level, undoman);
15720		});
15721
15722		// Pass though onRedo event from UndoManager to Editor
15723		onRedo.add(function(undoman, level) {
15724			return editor.onRedo.dispatch(editor, level, undoman);
15725		});
15726
15727		// Add initial undo level when the editor is initialized
15728		editor.onInit.add(function() {
15729			self.add();
15730		});
15731
15732		// Get position before an execCommand is processed
15733		editor.onBeforeExecCommand.add(function(ed, cmd, ui, val, args) {
15734			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
15735				self.beforeChange();
15736			}
15737		});
15738
15739		// Add undo level after an execCommand call was made
15740		editor.onExecCommand.add(function(ed, cmd, ui, val, args) {
15741			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
15742				self.add();
15743			}
15744		});
15745
15746		// Add undo level on save contents, drag end and blur/focusout
15747		editor.onSaveContent.add(addNonTypingUndoLevel);
15748		editor.dom.bind(editor.dom.getRoot(), 'dragend', addNonTypingUndoLevel);
15749		editor.dom.bind(editor.getBody(), 'focusout', function(e) {
15750			if (!editor.removed && self.typing) {
15751				addNonTypingUndoLevel();
15752			}
15753		});
15754
15755		editor.onKeyUp.add(function(editor, e) {
15756			var keyCode = e.keyCode;
15757
15758			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
15759				addNonTypingUndoLevel();
15760			}
15761		});
15762
15763		editor.onKeyDown.add(function(editor, e) {
15764			var keyCode = e.keyCode;
15765
15766			// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
15767			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
15768				if (self.typing) {
15769					addNonTypingUndoLevel();
15770				}
15771
15772				return;
15773			}
15774
15775			// If key isn't shift,ctrl,alt,capslock,metakey
15776			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
15777				self.beforeChange();
15778				self.typing = true;
15779				self.add();
15780			}
15781		});
15782
15783		editor.onMouseDown.add(function(editor, e) {
15784			if (self.typing) {
15785				addNonTypingUndoLevel();
15786			}
15787		});
15788
15789		// Add keyboard shortcuts for undo/redo keys
15790		editor.addShortcut('ctrl+z', 'undo_desc', 'Undo');
15791		editor.addShortcut('ctrl+y', 'redo_desc', 'Redo');
15792
15793		self = {
15794			// Explose for debugging reasons
15795			data : data,
15796
15797			typing : false,
15798			
15799			onBeforeAdd: onBeforeAdd,
15800
15801			onAdd : onAdd,
15802
15803			onUndo : onUndo,
15804
15805			onRedo : onRedo,
15806
15807			beforeChange : function() {
15808				beforeBookmark = editor.selection.getBookmark(2, true);
15809			},
15810
15811			add : function(level) {
15812				var i, settings = editor.settings, lastLevel;
15813
15814				level = level || {};
15815				level.content = getContent();
15816				
15817				self.onBeforeAdd.dispatch(self, level);
15818
15819				// Add undo level if needed
15820				lastLevel = data[index];
15821				if (lastLevel && lastLevel.content == level.content)
15822					return null;
15823
15824				// Set before bookmark on previous level
15825				if (data[index])
15826					data[index].beforeBookmark = beforeBookmark;
15827
15828				// Time to compress
15829				if (settings.custom_undo_redo_levels) {
15830					if (data.length > settings.custom_undo_redo_levels) {
15831						for (i = 0; i < data.length - 1; i++)
15832							data[i] = data[i + 1];
15833
15834						data.length--;
15835						index = data.length;
15836					}
15837				}
15838
15839				// Get a non intrusive normalized bookmark
15840				level.bookmark = editor.selection.getBookmark(2, true);
15841
15842				// Crop array if needed
15843				if (index < data.length - 1)
15844					data.length = index + 1;
15845
15846				data.push(level);
15847				index = data.length - 1;
15848
15849				self.onAdd.dispatch(self, level);
15850				editor.isNotDirty = 0;
15851
15852				return level;
15853			},
15854
15855			undo : function() {
15856				var level, i;
15857
15858				if (self.typing) {
15859					self.add();
15860					self.typing = false;
15861				}
15862
15863				if (index > 0) {
15864					level = data[--index];
15865
15866					editor.setContent(level.content, {format : 'raw'});
15867					editor.selection.moveToBookmark(level.beforeBookmark);
15868
15869					self.onUndo.dispatch(self, level);
15870				}
15871
15872				return level;
15873			},
15874
15875			redo : function() {
15876				var level;
15877
15878				if (index < data.length - 1) {
15879					level = data[++index];
15880
15881					editor.setContent(level.content, {format : 'raw'});
15882					editor.selection.moveToBookmark(level.bookmark);
15883
15884					self.onRedo.dispatch(self, level);
15885				}
15886
15887				return level;
15888			},
15889
15890			clear : function() {
15891				data = [];
15892				index = 0;
15893				self.typing = false;
15894			},
15895
15896			hasUndo : function() {
15897				return index > 0 || this.typing;
15898			},
15899
15900			hasRedo : function() {
15901				return index < data.length - 1 && !this.typing;
15902			}
15903		};
15904
15905		return self;
15906	};
15907})(tinymce);
15908
15909tinymce.ForceBlocks = function(editor) {
15910	var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();
15911
15912	function addRootBlocks() {
15913		var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped, isInEditorDocument;
15914
15915		if (!node || node.nodeType !== 1 || !settings.forced_root_block)
15916			return;
15917
15918		// Check if node is wrapped in block
15919		while (node && node != rootNode) {
15920			if (blockElements[node.nodeName])
15921				return;
15922
15923			node = node.parentNode;
15924		}
15925
15926		// Get current selection
15927		rng = selection.getRng();
15928		if (rng.setStart) {
15929			startContainer = rng.startContainer;
15930			startOffset = rng.startOffset;
15931			endContainer = rng.endContainer;
15932			endOffset = rng.endOffset;
15933		} else {
15934			// Force control range into text range
15935			if (rng.item) {
15936				node = rng.item(0);
15937				rng = editor.getDoc().body.createTextRange();
15938				rng.moveToElementText(node);
15939			}
15940
15941			isInEditorDocument = rng.parentElement().ownerDocument === editor.getDoc();
15942			tmpRng = rng.duplicate();
15943			tmpRng.collapse(true);
15944			startOffset = tmpRng.move('character', offset) * -1;
15945
15946			if (!tmpRng.collapsed) {
15947				tmpRng = rng.duplicate();
15948				tmpRng.collapse(false);
15949				endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
15950			}
15951		}
15952
15953		// Wrap non block elements and text nodes
15954		node = rootNode.firstChild;
15955		while (node) {
15956			if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {
15957				// Remove empty text nodes
15958				if (node.nodeType === 3 && node.nodeValue.length == 0) {
15959					tempNode = node;
15960					node = node.nextSibling;
15961					dom.remove(tempNode);
15962					continue;
15963				}
15964
15965				if (!rootBlockNode) {
15966					rootBlockNode = dom.create(settings.forced_root_block);
15967					node.parentNode.insertBefore(rootBlockNode, node);
15968					wrapped = true;
15969				}
15970
15971				tempNode = node;
15972				node = node.nextSibling;
15973				rootBlockNode.appendChild(tempNode);
15974			} else {
15975				rootBlockNode = null;
15976				node = node.nextSibling;
15977			}
15978		}
15979
15980		if (wrapped) {
15981			if (rng.setStart) {
15982				rng.setStart(startContainer, startOffset);
15983				rng.setEnd(endContainer, endOffset);
15984				selection.setRng(rng);
15985			} else {
15986				// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
15987				if (isInEditorDocument) {
15988					try {
15989						rng = editor.getDoc().body.createTextRange();
15990						rng.moveToElementText(rootNode);
15991						rng.collapse(true);
15992						rng.moveStart('character', startOffset);
15993
15994						if (endOffset > 0)
15995							rng.moveEnd('character', endOffset);
15996
15997						rng.select();
15998					} catch (ex) {
15999						// Ignore
16000					}
16001				}
16002			}
16003
16004			editor.nodeChanged();
16005		}
16006	};
16007
16008	// Force root blocks
16009	if (settings.forced_root_block) {
16010		editor.onKeyUp.add(addRootBlocks);
16011		editor.onNodeChange.add(addRootBlocks);
16012	}
16013};
16014
16015(function(tinymce) {
16016	// Shorten names
16017	var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
16018
16019	tinymce.create('tinymce.ControlManager', {
16020		ControlManager : function(ed, s) {
16021			var t = this, i;
16022
16023			s = s || {};
16024			t.editor = ed;
16025			t.controls = {};
16026			t.onAdd = new tinymce.util.Dispatcher(t);
16027			t.onPostRender = new tinymce.util.Dispatcher(t);
16028			t.prefix = s.prefix || ed.id + '_';
16029			t._cls = {};
16030
16031			t.onPostRender.add(function() {
16032				each(t.controls, function(c) {
16033					c.postRender();
16034				});
16035			});
16036		},
16037
16038		get : function(id) {
16039			return this.controls[this.prefix + id] || this.controls[id];
16040		},
16041
16042		setActive : function(id, s) {
16043			var c = null;
16044
16045			if (c = this.get(id))
16046				c.setActive(s);
16047
16048			return c;
16049		},
16050
16051		setDisabled : function(id, s) {
16052			var c = null;
16053
16054			if (c = this.get(id))
16055				c.setDisabled(s);
16056
16057			return c;
16058		},
16059
16060		add : function(c) {
16061			var t = this;
16062
16063			if (c) {
16064				t.controls[c.id] = c;
16065				t.onAdd.dispatch(c, t);
16066			}
16067
16068			return c;
16069		},
16070
16071		createControl : function(name) {
16072			var ctrl, i, l, self = this, editor = self.editor, factories, ctrlName;
16073
16074			// Build control factory cache
16075			if (!self.controlFactories) {
16076				self.controlFactories = [];
16077				each(editor.plugins, function(plugin) {
16078					if (plugin.createControl) {
16079						self.controlFactories.push(plugin);
16080					}
16081				});
16082			}
16083
16084			// Create controls by asking cached factories
16085			factories = self.controlFactories;
16086			for (i = 0, l = factories.length; i < l; i++) {
16087				ctrl = factories[i].createControl(name, self);
16088
16089				if (ctrl) {
16090					return self.add(ctrl);
16091				}
16092			}
16093
16094			// Create sepearator
16095			if (name === "|" || name === "separator") {
16096				return self.createSeparator();
16097			}
16098
16099			// Create control from button collection
16100			if (editor.buttons && (ctrl = editor.buttons[name])) {
16101				return self.createButton(name, ctrl);
16102			}
16103
16104			return self.add(ctrl);
16105		},
16106
16107		createDropMenu : function(id, s, cc) {
16108			var t = this, ed = t.editor, c, bm, v, cls;
16109
16110			s = extend({
16111				'class' : 'mceDropDown',
16112				constrain : ed.settings.constrain_menus
16113			}, s);
16114
16115			s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
16116			if (v = ed.getParam('skin_variant'))
16117				s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
16118
16119			s['class'] += ed.settings.directionality == "rtl" ? ' mceRtl' : '';
16120
16121			id = t.prefix + id;
16122			cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
16123			c = t.controls[id] = new cls(id, s);
16124			c.onAddItem.add(function(c, o) {
16125				var s = o.settings;
16126
16127				s.title = ed.getLang(s.title, s.title);
16128
16129				if (!s.onclick) {
16130					s.onclick = function(v) {
16131						if (s.cmd)
16132							ed.execCommand(s.cmd, s.ui || false, s.value);
16133					};
16134				}
16135			});
16136
16137			ed.onRemove.add(function() {
16138				c.destroy();
16139			});
16140
16141			// Fix for bug #1897785, #1898007
16142			if (tinymce.isIE) {
16143				c.onShowMenu.add(function() {
16144					// IE 8 needs focus in order to store away a range with the current collapsed caret location
16145					ed.focus();
16146
16147					bm = ed.selection.getBookmark(1);
16148				});
16149
16150				c.onHideMenu.add(function() {
16151					if (bm) {
16152						ed.selection.moveToBookmark(bm);
16153						bm = 0;
16154					}
16155				});
16156			}
16157
16158			return t.add(c);
16159		},
16160
16161		createListBox : function(id, s, cc) {
16162			var t = this, ed = t.editor, cmd, c, cls;
16163
16164			if (t.get(id))
16165				return null;
16166
16167			s.title = ed.translate(s.title);
16168			s.scope = s.scope || ed;
16169
16170			if (!s.onselect) {
16171				s.onselect = function(v) {
16172					ed.execCommand(s.cmd, s.ui || false, v || s.value);
16173				};
16174			}
16175
16176			s = extend({
16177				title : s.title,
16178				'class' : 'mce_' + id,
16179				scope : s.scope,
16180				control_manager : t
16181			}, s);
16182
16183			id = t.prefix + id;
16184
16185
16186			function useNativeListForAccessibility(ed) {
16187				return ed.settings.use_accessible_selects && !tinymce.isGecko
16188			}
16189
16190			if (ed.settings.use_native_selects || useNativeListForAccessibility(ed))
16191				c = new tinymce.ui.NativeListBox(id, s);
16192			else {
16193				cls = cc || t._cls.listbox || tinymce.ui.ListBox;
16194				c = new cls(id, s, ed);
16195			}
16196
16197			t.controls[id] = c;
16198
16199			// Fix focus problem in Safari
16200			if (tinymce.isWebKit) {
16201				c.onPostRender.add(function(c, n) {
16202					// Store bookmark on mousedown
16203					Event.add(n, 'mousedown', function() {
16204						ed.bookmark = ed.selection.getBookmark(1);
16205					});
16206
16207					// Restore on focus, since it might be lost
16208					Event.add(n, 'focus', function() {
16209						ed.selection.moveToBookmark(ed.bookmark);
16210						ed.bookmark = null;
16211					});
16212				});
16213			}
16214
16215			if (c.hideMenu)
16216				ed.onMouseDown.add(c.hideMenu, c);
16217
16218			return t.add(c);
16219		},
16220
16221		createButton : function(id, s, cc) {
16222			var t = this, ed = t.editor, o, c, cls;
16223
16224			if (t.get(id))
16225				return null;
16226
16227			s.title = ed.translate(s.title);
16228			s.label = ed.translate(s.label);
16229			s.scope = s.scope || ed;
16230
16231			if (!s.onclick && !s.menu_button) {
16232				s.onclick = function() {
16233					ed.execCommand(s.cmd, s.ui || false, s.value);
16234				};
16235			}
16236
16237			s = extend({
16238				title : s.title,
16239				'class' : 'mce_' + id,
16240				unavailable_prefix : ed.getLang('unavailable', ''),
16241				scope : s.scope,
16242				control_manager : t
16243			}, s);
16244
16245			id = t.prefix + id;
16246
16247			if (s.menu_button) {
16248				cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
16249				c = new cls(id, s, ed);
16250				ed.onMouseDown.add(c.hideMenu, c);
16251			} else {
16252				cls = t._cls.button || tinymce.ui.Button;
16253				c = new cls(id, s, ed);
16254			}
16255
16256			return t.add(c);
16257		},
16258
16259		createMenuButton : function(id, s, cc) {
16260			s = s || {};
16261			s.menu_button = 1;
16262
16263			return this.createButton(id, s, cc);
16264		},
16265
16266		createSplitButton : function(id, s, cc) {
16267			var t = this, ed = t.editor, cmd, c, cls;
16268
16269			if (t.get(id))
16270				return null;
16271
16272			s.title = ed.translate(s.title);
16273			s.scope = s.scope || ed;
16274
16275			if (!s.onclick) {
16276				s.onclick = function(v) {
16277					ed.execCommand(s.cmd, s.ui || false, v || s.value);
16278				};
16279			}
16280
16281			if (!s.onselect) {
16282				s.onselect = function(v) {
16283					ed.execCommand(s.cmd, s.ui || false, v || s.value);
16284				};
16285			}
16286
16287			s = extend({
16288				title : s.title,
16289				'class' : 'mce_' + id,
16290				scope : s.scope,
16291				control_manager : t
16292			}, s);
16293
16294			id = t.prefix + id;
16295			cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
16296			c = t.add(new cls(id, s, ed));
16297			ed.onMouseDown.add(c.hideMenu, c);
16298
16299			return c;
16300		},
16301
16302		createColorSplitButton : function(id, s, cc) {
16303			var t = this, ed = t.editor, cmd, c, cls, bm;
16304
16305			if (t.get(id))
16306				return null;
16307
16308			s.title = ed.translate(s.title);
16309			s.scope = s.scope || ed;
16310
16311			if (!s.onclick) {
16312				s.onclick = function(v) {
16313					if (tinymce.isIE)
16314						bm = ed.selection.getBookmark(1);
16315
16316					ed.execCommand(s.cmd, s.ui || false, v || s.value);
16317				};
16318			}
16319
16320			if (!s.onselect) {
16321				s.onselect = function(v) {
16322					ed.execCommand(s.cmd, s.ui || false, v || s.value);
16323				};
16324			}
16325
16326			s = extend({
16327				title : s.title,
16328				'class' : 'mce_' + id,
16329				'menu_class' : ed.getParam('skin') + 'Skin',
16330				scope : s.scope,
16331				more_colors_title : ed.getLang('more_colors')
16332			}, s);
16333
16334			id = t.prefix + id;
16335			cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
16336			c = new cls(id, s, ed);
16337			ed.onMouseDown.add(c.hideMenu, c);
16338
16339			// Remove the menu element when the editor is removed
16340			ed.onRemove.add(function() {
16341				c.destroy();
16342			});
16343
16344			// Fix for bug #1897785, #1898007
16345			if (tinymce.isIE) {
16346				c.onShowMenu.add(function() {
16347					// IE 8 needs focus in order to store away a range with the current collapsed caret location
16348					ed.focus();
16349					bm = ed.selection.getBookmark(1);
16350				});
16351
16352				c.onHideMenu.add(function() {
16353					if (bm) {
16354						ed.selection.moveToBookmark(bm);
16355						bm = 0;
16356					}
16357				});
16358			}
16359
16360			return t.add(c);
16361		},
16362
16363		createToolbar : function(id, s, cc) {
16364			var c, t = this, cls;
16365
16366			id = t.prefix + id;
16367			cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
16368			c = new cls(id, s, t.editor);
16369
16370			if (t.get(id))
16371				return null;
16372
16373			return t.add(c);
16374		},
16375		
16376		createToolbarGroup : function(id, s, cc) {
16377			var c, t = this, cls;
16378			id = t.prefix + id;
16379			cls = cc || this._cls.toolbarGroup || tinymce.ui.ToolbarGroup;
16380			c = new cls(id, s, t.editor);
16381			
16382			if (t.get(id))
16383				return null;
16384			
16385			return t.add(c);
16386		},
16387
16388		createSeparator : function(cc) {
16389			var cls = cc || this._cls.separator || tinymce.ui.Separator;
16390
16391			return new cls();
16392		},
16393
16394		setControlType : function(n, c) {
16395			return this._cls[n.toLowerCase()] = c;
16396		},
16397	
16398		destroy : function() {
16399			each(this.controls, function(c) {
16400				c.destroy();
16401			});
16402
16403			this.controls = null;
16404		}
16405	});
16406})(tinymce);
16407
16408(function(tinymce) {
16409	var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
16410
16411	tinymce.create('tinymce.WindowManager', {
16412		WindowManager : function(ed) {
16413			var t = this;
16414
16415			t.editor = ed;
16416			t.onOpen = new Dispatcher(t);
16417			t.onClose = new Dispatcher(t);
16418			t.params = {};
16419			t.features = {};
16420		},
16421
16422		open : function(s, p) {
16423			var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;
16424
16425			// Default some options
16426			s = s || {};
16427			p = p || {};
16428			sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
16429			sh = isOpera ? vp.h : screen.height;
16430			s.name = s.name || 'mc_' + new Date().getTime();
16431			s.width = parseInt(s.width || 320);
16432			s.height = parseInt(s.height || 240);
16433			s.resizable = true;
16434			s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
16435			s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
16436			p.inline = false;
16437			p.mce_width = s.width;
16438			p.mce_height = s.height;
16439			p.mce_auto_focus = s.auto_focus;
16440
16441			if (mo) {
16442				if (isIE) {
16443					s.center = true;
16444					s.help = false;
16445					s.dialogWidth = s.width + 'px';
16446					s.dialogHeight = s.height + 'px';
16447					s.scroll = s.scrollbars || false;
16448				}
16449			}
16450
16451			// Build features string
16452			each(s, function(v, k) {
16453				if (tinymce.is(v, 'boolean'))
16454					v = v ? 'yes' : 'no';
16455
16456				if (!/^(name|url)$/.test(k)) {
16457					if (isIE && mo)
16458						f += (f ? ';' : '') + k + ':' + v;
16459					else
16460						f += (f ? ',' : '') + k + '=' + v;
16461				}
16462			});
16463
16464			t.features = s;
16465			t.params = p;
16466			t.onOpen.dispatch(t, s, p);
16467
16468			u = s.url || s.file;
16469			u = tinymce._addVer(u);
16470
16471			try {
16472				if (isIE && mo) {
16473					w = 1;
16474					window.showModalDialog(u, window, f);
16475				} else
16476					w = window.open(u, s.name, f);
16477			} catch (ex) {
16478				// Ignore
16479			}
16480
16481			if (!w)
16482				alert(t.editor.getLang('popup_blocked'));
16483		},
16484
16485		close : function(w) {
16486			w.close();
16487			this.onClose.dispatch(this);
16488		},
16489
16490		createInstance : function(cl, a, b, c, d, e) {
16491			var f = tinymce.resolve(cl);
16492
16493			return new f(a, b, c, d, e);
16494		},
16495
16496		confirm : function(t, cb, s, w) {
16497			w = w || window;
16498
16499			cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
16500		},
16501
16502		alert : function(tx, cb, s, w) {
16503			var t = this;
16504
16505			w = w || window;
16506			w.alert(t._decode(t.editor.getLang(tx, tx)));
16507
16508			if (cb)
16509				cb.call(s || t);
16510		},
16511
16512		resizeBy : function(dw, dh, win) {
16513			win.resizeBy(dw, dh);
16514		},
16515
16516		// Internal functions
16517
16518		_decode : function(s) {
16519			return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
16520		}
16521	});
16522}(tinymce));
16523(function(tinymce) {
16524	tinymce.Formatter = function(ed) {
16525		var formats = {},
16526			each = tinymce.each,
16527			dom = ed.dom,
16528			selection = ed.selection,
16529			TreeWalker = tinymce.dom.TreeWalker,
16530			rangeUtils = new tinymce.dom.RangeUtils(dom),
16531			isValid = ed.schema.isValidChild,
16532			isArray = tinymce.isArray,
16533			isBlock = dom.isBlock,
16534			forcedRootBlock = ed.settings.forced_root_block,
16535			nodeIndex = dom.nodeIndex,
16536			INVISIBLE_CHAR = '\uFEFF',
16537			MCE_ATTR_RE = /^(src|href|style)$/,
16538			FALSE = false,
16539			TRUE = true,
16540			formatChangeData,
16541			undef,
16542			getContentEditable = dom.getContentEditable;
16543
16544		function isTextBlock(name) {
16545			if (name.nodeType) {
16546				name = name.nodeName;
16547			}
16548
16549			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
16550		}
16551
16552		function getParents(node, selector) {
16553			return dom.getParents(node, selector, dom.getRoot());
16554		};
16555
16556		function isCaretNode(node) {
16557			return node.nodeType === 1 && node.id === '_mce_caret';
16558		};
16559
16560		function defaultFormats() {
16561			register({
16562				alignleft : [
16563					{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}, defaultBlock: 'div'},
16564					{selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
16565				],
16566
16567				aligncenter : [
16568					{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}, defaultBlock: 'div'},
16569					{selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
16570					{selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
16571				],
16572
16573				alignright : [
16574					{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}, defaultBlock: 'div'},
16575					{selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
16576				],
16577
16578				alignfull : [
16579					{selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}, defaultBlock: 'div'}
16580				],
16581
16582				bold : [
16583					{inline : 'strong', remove : 'all'},
16584					{inline : 'span', styles : {fontWeight : 'bold'}},
16585					{inline : 'b', remove : 'all'}
16586				],
16587
16588				italic : [
16589					{inline : 'em', remove : 'all'},
16590					{inline : 'span', styles : {fontStyle : 'italic'}},
16591					{inline : 'i', remove : 'all'}
16592				],
16593
16594				underline : [
16595					{inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
16596					{inline : 'u', remove : 'all'}
16597				],
16598
16599				strikethrough : [
16600					{inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
16601					{inline : 'strike', remove : 'all'}
16602				],
16603
16604				forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
16605				hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
16606				fontname : {inline : 'span', styles : {fontFamily : '%value'}},
16607				fontsize : {inline : 'span', styles : {fontSize : '%value'}},
16608				fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
16609				blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
16610				subscript : {inline : 'sub'},
16611				superscript : {inline : 'sup'},
16612
16613				link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
16614					onmatch : function(node) {
16615						return true;
16616					},
16617
16618					onformat : function(elm, fmt, vars) {
16619						each(vars, function(value, key) {
16620							dom.setAttrib(elm, key, value);
16621						});
16622					}
16623				},
16624
16625				removeformat : [
16626					{selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
16627					{selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
16628					{selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
16629				]
16630			});
16631
16632			// Register default block formats
16633			each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
16634				register(name, {block : name, remove : 'all'});
16635			});
16636
16637			// Register user defined formats
16638			register(ed.settings.formats);
16639		};
16640
16641		function addKeyboardShortcuts() {
16642			// Add some inline shortcuts
16643			ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');
16644			ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');
16645			ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');
16646
16647			// BlockFormat shortcuts keys
16648			for (var i = 1; i <= 6; i++) {
16649				ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
16650			}
16651
16652			ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
16653			ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
16654			ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
16655		};
16656
16657		// Public functions
16658
16659		function get(name) {
16660			return name ? formats[name] : formats;
16661		};
16662
16663		function register(name, format) {
16664			if (name) {
16665				if (typeof(name) !== 'string') {
16666					each(name, function(format, name) {
16667						register(name, format);
16668					});
16669				} else {
16670					// Force format into array and add it to internal collection
16671					format = format.length ? format : [format];
16672
16673					each(format, function(format) {
16674						// Set deep to false by default on selector formats this to avoid removing
16675						// alignment on images inside paragraphs when alignment is changed on paragraphs
16676						if (format.deep === undef)
16677							format.deep = !format.selector;
16678
16679						// Default to true
16680						if (format.split === undef)
16681							format.split = !format.selector || format.inline;
16682
16683						// Default to true
16684						if (format.remove === undef && format.selector && !format.inline)
16685							format.remove = 'none';
16686
16687						// Mark format as a mixed format inline + block level
16688						if (format.selector && format.inline) {
16689							format.mixed = true;
16690							format.block_expand = true;
16691						}
16692
16693						// Split classes if needed
16694						if (typeof(format.classes) === 'string')
16695							format.classes = format.classes.split(/\s+/);
16696					});
16697
16698					formats[name] = format;
16699				}
16700			}
16701		};
16702
16703		var getTextDecoration = function(node) {
16704			var decoration;
16705
16706			ed.dom.getParent(node, function(n) {
16707				decoration = ed.dom.getStyle(n, 'text-decoration');
16708				return decoration && decoration !== 'none';
16709			});
16710
16711			return decoration;
16712		};
16713
16714		var processUnderlineAndColor = function(node) {
16715			var textDecoration;
16716			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
16717				textDecoration = getTextDecoration(node.parentNode);
16718				if (ed.dom.getStyle(node, 'color') && textDecoration) {
16719					ed.dom.setStyle(node, 'text-decoration', textDecoration);
16720				} else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {
16721					ed.dom.setStyle(node, 'text-decoration', null);
16722				}
16723			}
16724		};
16725
16726		function apply(name, vars, node) {
16727			var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();
16728
16729			function setElementFormat(elm, fmt) {
16730				fmt = fmt || format;
16731
16732				if (elm) {
16733					if (fmt.onformat) {
16734						fmt.onformat(elm, fmt, vars, node);
16735					}
16736
16737					each(fmt.styles, function(value, name) {
16738						dom.setStyle(elm, name, replaceVars(value, vars));
16739					});
16740
16741					each(fmt.attributes, function(value, name) {
16742						dom.setAttrib(elm, name, replaceVars(value, vars));
16743					});
16744
16745					each(fmt.classes, function(value) {
16746						value = replaceVars(value, vars);
16747						if (!dom.hasClass(elm, value))
16748							dom.addClass(elm, value);
16749					});
16750				}
16751			};
16752			function adjustSelectionToVisibleSelection() {
16753				function findSelectionEnd(start, end) {
16754					var walker = new TreeWalker(end);
16755					for (node = walker.current(); node; node = walker.prev()) {
16756						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
16757							return node;
16758						}
16759					}
16760				};
16761
16762				// Adjust selection so that a end container with a end offset of zero is not included in the selection
16763				// as this isn't visible to the user.
16764				var rng = ed.selection.getRng();
16765				var start = rng.startContainer;
16766				var end = rng.endContainer;
16767
16768				if (start != end && rng.endOffset === 0) {
16769					var newEnd = findSelectionEnd(start, end);
16770					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
16771
16772					rng.setEnd(newEnd, endOffset);
16773				}
16774
16775				return rng;
16776			}
16777			
16778			function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){
16779				var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;
16780				
16781				// find the index of the first child list.
16782				each(node.childNodes, function(n, index) {
16783					if (n.nodeName === "UL" || n.nodeName === "OL") {
16784						listIndex = index;
16785						list = n;
16786						return false;
16787					}
16788				});
16789				
16790				// get the index of the bookmarks
16791				each(node.childNodes, function(n, index) {
16792					if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
16793						if (n.id == bookmark.id + "_start") {
16794							startIndex = index;
16795						} else if (n.id == bookmark.id + "_end") {
16796							endIndex = index;
16797						}
16798					}
16799				});
16800				
16801				// if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally
16802				if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {
16803					each(tinymce.grep(node.childNodes), process);
16804					return 0;
16805				} else {
16806					currentWrapElm = dom.clone(wrapElm, FALSE);
16807
16808					// create a list of the nodes on the same side of the list as the selection
16809					each(tinymce.grep(node.childNodes), function(n, index) {
16810						if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
16811							nodes.push(n); 
16812							n.parentNode.removeChild(n);
16813						}
16814					});
16815
16816					// insert the wrapping element either before or after the list.
16817					if (startIndex < listIndex) {
16818						node.insertBefore(currentWrapElm, list);
16819					} else if (startIndex > listIndex) {
16820						node.insertBefore(currentWrapElm, list.nextSibling);
16821					}
16822					
16823					// add the new nodes to the list.
16824					newWrappers.push(currentWrapElm);
16825
16826					each(nodes, function(node) {
16827						currentWrapElm.appendChild(node);
16828					});
16829
16830					return currentWrapElm;
16831				}
16832			};
16833
16834			function applyRngStyle(rng, bookmark, node_specific) {
16835				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
16836
16837				// Setup wrapper element
16838				wrapName = format.inline || format.block;
16839				wrapElm = dom.create(wrapName);
16840				setElementFormat(wrapElm);
16841
16842				rangeUtils.walk(rng, function(nodes) {
16843					var currentWrapElm;
16844
16845					function process(node) {
16846						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
16847
16848						lastContentEditable = contentEditable;
16849						nodeName = node.nodeName.toLowerCase();
16850						parentName = node.parentNode.nodeName.toLowerCase();
16851
16852						// Node has a contentEditable value
16853						if (node.nodeType === 1 && getContentEditable(node)) {
16854							lastContentEditable = contentEditable;
16855							contentEditable = getContentEditable(node) === "true";
16856							hasContentEditableState = true; // We don't want to wrap the container only it's children
16857						}
16858
16859						// Stop wrapping on br elements
16860						if (isEq(nodeName, 'br')) {
16861							currentWrapElm = 0;
16862
16863							// Remove any br elements when we wrap things
16864							if (format.block)
16865								dom.remove(node);
16866
16867							return;
16868						}
16869
16870						// If node is wrapper type
16871						if (format.wrapper && matchNode(node, name, vars)) {
16872							currentWrapElm = 0;
16873							return;
16874						}
16875
16876						// Can we rename the block
16877						if (contentEditable && !hasContentEditableState && format.block && !format.wrapper && isTextBlock(nodeName)) {
16878							node = dom.rename(node, wrapName);
16879							setElementFormat(node);
16880							newWrappers.push(node);
16881							currentWrapElm = 0;
16882							return;
16883						}
16884
16885						// Handle selector patterns
16886						if (format.selector) {
16887							// Look for matching formats
16888							each(formatList, function(format) {
16889								// Check collapsed state if it exists
16890								if ('collapsed' in format && format.collapsed !== isCollapsed) {
16891									return;
16892								}
16893
16894								if (dom.is(node, format.selector) && !isCaretNode(node)) {
16895									setElementFormat(node, format);
16896									found = true;
16897								}
16898							});
16899
16900							// Continue processing if a selector match wasn't found and a inline element is defined
16901							if (!format.inline || found) {
16902								currentWrapElm = 0;
16903								return;
16904							}
16905						}
16906
16907						// Is it valid to wrap this item
16908						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
16909								!(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && !isCaretNode(node) && (!format.inline || !isBlock(node))) {
16910							// Start wrapping
16911							if (!currentWrapElm) {
16912								// Wrap the node
16913								currentWrapElm = dom.clone(wrapElm, FALSE);
16914								node.parentNode.insertBefore(currentWrapElm, node);
16915								newWrappers.push(currentWrapElm);
16916							}
16917
16918							currentWrapElm.appendChild(node);
16919						} else if (nodeName == 'li' && bookmark) {
16920							// Start wrapping - if we are in a list node and have a bookmark, then we will always begin by wrapping in a new element.
16921							currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);
16922						} else {
16923							// Start a new wrapper for possible children
16924							currentWrapElm = 0;
16925							
16926							each(tinymce.grep(node.childNodes), process);
16927
16928							if (hasContentEditableState) {
16929								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
16930							}
16931
16932							// End the last wrapper
16933							currentWrapElm = 0;
16934						}
16935					};
16936
16937					// Process siblings from range
16938					each(nodes, process);
16939				});
16940
16941				// Wrap links inside as well, for example color inside a link when the wrapper is around the link
16942				if (format.wrap_links === false) {
16943					each(newWrappers, function(node) {
16944						function process(node) {
16945							var i, currentWrapElm, children;
16946
16947							if (node.nodeName === 'A') {
16948								currentWrapElm = dom.clone(wrapElm, FALSE);
16949								newWrappers.push(currentWrapElm);
16950
16951								children = tinymce.grep(node.childNodes);
16952								for (i = 0; i < children.length; i++)
16953									currentWrapElm.appendChild(children[i]);
16954
16955								node.appendChild(currentWrapElm);
16956							}
16957
16958							each(tinymce.grep(node.childNodes), process);
16959						};
16960
16961						process(node);
16962					});
16963				}
16964
16965				// Cleanup
16966				
16967				each(newWrappers, function(node) {
16968					var childCount;
16969
16970					function getChildCount(node) {
16971						var count = 0;
16972
16973						each(node.childNodes, function(node) {
16974							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))
16975								count++;
16976						});
16977
16978						return count;
16979					};
16980
16981					function mergeStyles(node) {
16982						var child, clone;
16983
16984						each(node.childNodes, function(node) {
16985							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
16986								child = node;
16987								return FALSE; // break loop
16988							}
16989						});
16990
16991						// If child was found and of the same type as the current node
16992						if (child && matchName(child, format)) {
16993							clone = dom.clone(child, FALSE);
16994							setElementFormat(clone);
16995
16996							dom.replace(clone, node, TRUE);
16997							dom.remove(child, 1);
16998						}
16999
17000						return clone || node;
17001					};
17002
17003					childCount = getChildCount(node);
17004
17005					// Remove empty nodes but only if there is multiple wrappers and they are not block
17006					// elements so never remove single <h1></h1> since that would remove the currrent empty block element where the caret is at
17007					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
17008						dom.remove(node, 1);
17009						return;
17010					}
17011
17012					if (format.inline || format.wrapper) {
17013						// Merges the current node with it's children of similar type to reduce the number of elements
17014						if (!format.exact && childCount === 1)
17015							node = mergeStyles(node);
17016
17017						// Remove/merge children
17018						each(formatList, function(format) {
17019							// Merge all children of similar type will move styles from child to parent
17020							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
17021							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
17022							each(dom.select(format.inline, node), function(child) {
17023								var parent;
17024
17025								// When wrap_links is set to false we don't want
17026								// to remove the format on children within links
17027								if (format.wrap_links === false) {
17028									parent = child.parentNode;
17029
17030									do {
17031										if (parent.nodeName === 'A')
17032											return;
17033									} while (parent = parent.parentNode);
17034								}
17035
17036								removeFormat(format, vars, child, format.exact ? child : null);
17037							});
17038						});
17039
17040						// Remove child if direct parent is of same type
17041						if (matchNode(node.parentNode, name, vars)) {
17042							dom.remove(node, 1);
17043							node = 0;
17044							return TRUE;
17045						}
17046
17047						// Look for parent with similar style format
17048						if (format.merge_with_parents) {
17049							dom.getParent(node.parentNode, function(parent) {
17050								if (matchNode(parent, name, vars)) {
17051									dom.remove(node, 1);
17052									node = 0;
17053									return TRUE;
17054								}
17055							});
17056						}
17057
17058						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
17059						if (node && format.merge_siblings !== false) {
17060							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
17061							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
17062						}
17063					}
17064				});
17065			};
17066
17067			if (format) {
17068				if (node) {
17069					if (node.nodeType) {
17070						rng = dom.createRng();
17071						rng.setStartBefore(node);
17072						rng.setEndAfter(node);
17073						applyRngStyle(expandRng(rng, formatList), null, true);
17074					} else {
17075						applyRngStyle(node, null, true);
17076					}
17077				} else {
17078					if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {
17079						// Obtain selection node before selection is unselected by applyRngStyle()
17080						var curSelNode = ed.selection.getNode();
17081
17082						// If the formats have a default block and we can't find a parent block then start wrapping it with a DIV this is for forced_root_blocks: false
17083						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
17084						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
17085							apply(formatList[0].defaultBlock);
17086						}
17087
17088						// Apply formatting to selection
17089						ed.selection.setRng(adjustSelectionToVisibleSelection());
17090						bookmark = selection.getBookmark();
17091						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
17092
17093						// Colored nodes should be underlined so that the color of the underline matches the text color.
17094						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
17095							tinymce.walk(curSelNode, processUnderlineAndColor, 'childNodes');
17096							processUnderlineAndColor(curSelNode);
17097						}
17098
17099						selection.moveToBookmark(bookmark);
17100						moveStart(selection.getRng(TRUE));
17101						ed.nodeChanged();
17102					} else
17103						performCaretAction('apply', name, vars);
17104				}
17105			}
17106		};
17107
17108		function remove(name, vars, node) {
17109			var formatList = get(name), format = formatList[0], bookmark, i, rng, contentEditable = true;
17110
17111			// Merges the styles for each node
17112			function process(node) {
17113				var children, i, l, localContentEditable, lastContentEditable, hasContentEditableState;
17114
17115				// Skip on text nodes as they have neither format to remove nor children
17116				if (node.nodeType === 3) {
17117					return;
17118				}
17119
17120				// Node has a contentEditable value
17121				if (node.nodeType === 1 && getContentEditable(node)) {
17122					lastContentEditable = contentEditable;
17123					contentEditable = getContentEditable(node) === "true";
17124					hasContentEditableState = true; // We don't want to wrap the container only it's children
17125				}
17126
17127				// Grab the children first since the nodelist might be changed
17128				children = tinymce.grep(node.childNodes);
17129				// Process current node
17130				if (contentEditable && !hasContentEditableState) {
17131					for (i = 0, l = formatList.length; i < l; i++) {
17132						if (removeFormat(formatList[i], vars, node, node))
17133							break;
17134					}
17135				}
17136
17137				// Process the children
17138				if (format.deep) {
17139					if (children.length) {					
17140						for (i = 0, l = children.length; i < l; i++)
17141							process(children[i]);
17142
17143						if (hasContentEditableState) {
17144							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
17145						}
17146					}
17147				}
17148			};
17149
17150			function findFormatRoot(container) {
17151				var formatRoot;
17152
17153				// Find format root
17154				each(getParents(container.parentNode).reverse(), function(parent) {
17155					var format;
17156
17157					// Find format root element
17158					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
17159						// Is the node matching the format we are looking for
17160						format = matchNode(parent, name, vars);
17161						if (format && format.split !== false)
17162							formatRoot = parent;
17163					}
17164				});
17165
17166				return formatRoot;
17167			};
17168
17169			function wrapAndSplit(format_root, container, target, split) {
17170				var parent, clone, lastClone, firstClone, i, formatRootParent;
17171
17172				// Format root found then clone formats and split it
17173				if (format_root) {
17174					formatRootParent = format_root.parentNode;
17175
17176					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
17177						clone = dom.clone(parent, FALSE);
17178
17179						for (i = 0; i < formatList.length; i++) {
17180							if (removeFormat(formatList[i], vars, clone, clone)) {
17181								clone = 0;
17182								break;
17183							}
17184						}
17185
17186						// Build wrapper node
17187						if (clone) {
17188							if (lastClone)
17189								clone.appendChild(lastClone);
17190
17191							if (!firstClone)
17192								firstClone = clone;
17193
17194							lastClone = clone;
17195						}
17196					}
17197
17198					// Never split block elements if the format is mixed
17199					if (split && (!format.mixed || !isBlock(format_root)))
17200						container = dom.split(format_root, container);
17201
17202					// Wrap container in cloned formats
17203					if (lastClone) {
17204						target.parentNode.insertBefore(lastClone, target);
17205						firstClone.appendChild(target);
17206					}
17207				}
17208
17209				return container;
17210			};
17211
17212			function splitToFormatRoot(container) {
17213				return wrapAndSplit(findFormatRoot(container), container, container, true);
17214			};
17215
17216			function unwrap(start) {
17217				var node = dom.get(start ? '_start' : '_end'),
17218					out = node[start ? 'firstChild' : 'lastChild'];
17219
17220				// If the end is placed within the start the result will be removed
17221				// So this checks if the out node is a bookmark node if it is it
17222				// checks for another more suitable node
17223				if (isBookmarkNode(out))
17224					out = out[start ? 'firstChild' : 'lastChild'];
17225
17226				dom.remove(node, true);
17227
17228				return out;
17229			};
17230
17231			function removeRngStyle(rng) {
17232				var startContainer, endContainer, node;
17233
17234				rng = expandRng(rng, formatList, TRUE);
17235
17236				if (format.split) {
17237					startContainer = getContainer(rng, TRUE);
17238					endContainer = getContainer(rng);
17239
17240					if (startContainer != endContainer) {
17241						// WebKit will render the table incorrectly if we wrap a TD in a SPAN so lets see if the can use the first child instead
17242						// This will happen if you tripple click a table cell and use remove formatting
17243						if (/^(TR|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
17244							startContainer = (startContainer.nodeName == "TD" ? startContainer.firstChild : startContainer.firstChild.firstChild) || startContainer;
17245						}
17246
17247						// Wrap start/end nodes in span element since these might be cloned/moved
17248						startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});
17249						endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});
17250
17251						// Split start/end
17252						splitToFormatRoot(startContainer);
17253						splitToFormatRoot(endContainer);
17254
17255						// Unwrap start/end to get real elements again
17256						startContainer = unwrap(TRUE);
17257						endContainer = unwrap();
17258					} else
17259						startContainer = endContainer = splitToFormatRoot(startContainer);
17260
17261					// Update range positions since they might have changed after the split operations
17262					rng.startContainer = startContainer.parentNode;
17263					rng.startOffset = nodeIndex(startContainer);
17264					rng.endContainer = endContainer.parentNode;
17265					rng.endOffset = nodeIndex(endContainer) + 1;
17266				}
17267
17268				// Remove items between start/end
17269				rangeUtils.walk(rng, function(nodes) {
17270					each(nodes, function(node) {
17271						process(node);
17272
17273						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
17274						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
17275							removeFormat({'deep': false, 'exact': true, 'inline': 'span', 'styles': {'textDecoration' : 'underline'}}, null, node);
17276						}
17277					});
17278				});
17279			};
17280
17281			// Handle node
17282			if (node) {
17283				if (node.nodeType) {
17284					rng = dom.createRng();
17285					rng.setStartBefore(node);
17286					rng.setEndAfter(node);
17287					removeRngStyle(rng);
17288				} else {
17289					removeRngStyle(node);
17290				}
17291
17292				return;
17293			}
17294
17295			if (!selection.isCollapsed() || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {
17296				bookmark = selection.getBookmark();
17297				removeRngStyle(selection.getRng(TRUE));
17298				selection.moveToBookmark(bookmark);
17299
17300				// Check if start element still has formatting then we are at: "<b>text|</b>text" and need to move the start into the next text node
17301				if (format.inline && match(name, vars, selection.getStart())) {
17302					moveStart(selection.getRng(true));
17303				}
17304
17305				ed.nodeChanged();
17306			} else
17307				performCaretAction('remove', name, vars);
17308		};
17309
17310		function toggle(name, vars, node) {
17311			var fmt = get(name);
17312
17313			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle))
17314				remove(name, vars, node);
17315			else
17316				apply(name, vars, node);
17317		};
17318
17319		function matchNode(node, name, vars, similar) {
17320			var formatList = get(name), format, i, classes;
17321
17322			function matchItems(node, format, item_name) {
17323				var key, value, items = format[item_name], i;
17324
17325				// Custom match
17326				if (format.onmatch) {
17327					return format.onmatch(node, format, item_name);
17328				}
17329
17330				// Check all items
17331				if (items) {
17332					// Non indexed object
17333					if (items.length === undef) {
17334						for (key in items) {
17335							if (items.hasOwnProperty(key)) {
17336								if (item_name === 'attributes')
17337									value = dom.getAttrib(node, key);
17338								else
17339									value = getStyle(node, key);
17340
17341								if (similar && !value && !format.exact)
17342									return;
17343
17344								if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))
17345									return;
17346							}
17347						}
17348					} else {
17349						// Only one match needed for indexed arrays
17350						for (i = 0; i < items.length; i++) {
17351							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))
17352								return format;
17353						}
17354					}
17355				}
17356
17357				return format;
17358			};
17359
17360			if (formatList && node) {
17361				// Check each format in list
17362				for (i = 0; i < formatList.length; i++) {
17363					format = formatList[i];
17364
17365					// Name name, attributes, styles and classes
17366					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
17367						// Match classes
17368						if (classes = format.classes) {
17369							for (i = 0; i < classes.length; i++) {
17370								if (!dom.hasClass(node, classes[i]))
17371									return;
17372							}
17373						}
17374
17375						return format;
17376					}
17377				}
17378			}
17379		};
17380
17381		function match(name, vars, node) {
17382			var startNode;
17383
17384			function matchParents(node) {
17385				// Find first node with similar format settings
17386				node = dom.getParent(node, function(node) {
17387					return !!matchNode(node, name, vars, true);
17388				});
17389
17390				// Do an exact check on the similar format element
17391				return matchNode(node, name, vars);
17392			};
17393
17394			// Check specified node
17395			if (node)
17396				return matchParents(node);
17397
17398			// Check selected node
17399			node = selection.getNode();
17400			if (matchParents(node))
17401				return TRUE;
17402
17403			// Check start node if it's different
17404			startNode = selection.getStart();
17405			if (startNode != node) {
17406				if (matchParents(startNode))
17407					return TRUE;
17408			}
17409
17410			return FALSE;
17411		};
17412
17413		function matchAll(names, vars) {
17414			var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;
17415
17416			// Check start of selection for formats
17417			startElement = selection.getStart();
17418			dom.getParent(startElement, function(node) {
17419				var i, name;
17420
17421				for (i = 0; i < names.length; i++) {
17422					name = names[i];
17423
17424					if (!checkedMap[name] && matchNode(node, name, vars)) {
17425						checkedMap[name] = true;
17426						matchedFormatNames.push(name);
17427					}
17428				}
17429			}, dom.getRoot());
17430
17431			return matchedFormatNames;
17432		};
17433
17434		function canApply(name) {
17435			var formatList = get(name), startNode, parents, i, x, selector;
17436
17437			if (formatList) {
17438				startNode = selection.getStart();
17439				parents = getParents(startNode);
17440
17441				for (x = formatList.length - 1; x >= 0; x--) {
17442					selector = formatList[x].selector;
17443
17444					// Format is not selector based, then always return TRUE
17445					if (!selector)
17446						return TRUE;
17447
17448					for (i = parents.length - 1; i >= 0; i--) {
17449						if (dom.is(parents[i], selector))
17450							return TRUE;
17451					}
17452				}
17453			}
17454
17455			return FALSE;
17456		};
17457
17458		function formatChanged(formats, callback, similar) {
17459			var currentFormats;
17460
17461			// Setup format node change logic
17462			if (!formatChangeData) {
17463				formatChangeData = {};
17464				currentFormats = {};
17465
17466				ed.onNodeChange.addToTop(function(ed, cm, node) {
17467					var parents = getParents(node), matchedFormats = {};
17468
17469					// Check for new formats
17470					each(formatChangeData, function(callbacks, format) {
17471						each(parents, function(node) {
17472							if (matchNode(node, format, {}, callbacks.similar)) {
17473								if (!currentFormats[format]) {
17474									// Execute callbacks
17475									each(callbacks, function(callback) {
17476										callback(true, {node: node, format: format, parents: parents});
17477									});
17478
17479									currentFormats[format] = callbacks;
17480								}
17481
17482								matchedFormats[format] = callbacks;
17483								return false;
17484							}
17485						});
17486					});
17487
17488					// Check if current formats still match
17489					each(currentFormats, function(callbacks, format) {
17490						if (!matchedFormats[format]) {
17491							delete currentFormats[format];
17492
17493							each(callbacks, function(callback) {
17494								callback(false, {node: node, format: format, parents: parents});
17495							});
17496						}
17497					});
17498				});
17499			}
17500
17501			// Add format listeners
17502			each(formats.split(','), function(format) {
17503				if (!formatChangeData[format]) {
17504					formatChangeData[format] = [];
17505					formatChangeData[format].similar = similar;
17506				}
17507
17508				formatChangeData[format].push(callback);
17509			});
17510
17511			return this;
17512		};
17513
17514		// Expose to public
17515		tinymce.extend(this, {
17516			get : get,
17517			register : register,
17518			apply : apply,
17519			remove : remove,
17520			toggle : toggle,
17521			match : match,
17522			matchAll : matchAll,
17523			matchNode : matchNode,
17524			canApply : canApply,
17525			formatChanged: formatChanged
17526		});
17527
17528		// Initialize
17529		defaultFormats();
17530		addKeyboardShortcuts();
17531
17532		// Private functions
17533
17534		function matchName(node, format) {
17535			// Check for inline match
17536			if (isEq(node, format.inline))
17537				return TRUE;
17538
17539			// Check for block match
17540			if (isEq(node, format.block))
17541				return TRUE;
17542
17543			// Check for selector match
17544			if (format.selector)
17545				return dom.is(node, format.selector);
17546		};
17547
17548		function isEq(str1, str2) {
17549			str1 = str1 || '';
17550			str2 = str2 || '';
17551
17552			str1 = '' + (str1.nodeName || str1);
17553			str2 = '' + (str2.nodeName || str2);
17554
17555			return str1.toLowerCase() == str2.toLowerCase();
17556		};
17557
17558		function getStyle(node, name) {
17559			var styleVal = dom.getStyle(node, name);
17560
17561			// Force the format to hex
17562			if (name == 'color' || name == 'backgroundColor')
17563				styleVal = dom.toHex(styleVal);
17564
17565			// Opera will return bold as 700
17566			if (name == 'fontWeight' && styleVal == 700)
17567				styleVal = 'bold';
17568
17569			return '' + styleVal;
17570		};
17571
17572		function replaceVars(value, vars) {
17573			if (typeof(value) != "string")
17574				value = value(vars);
17575			else if (vars) {
17576				value = value.replace(/%(\w+)/g, function(str, name) {
17577					return vars[name] || str;
17578				});
17579			}
17580
17581			return value;
17582		};
17583
17584		function isWhiteSpaceNode(node) {
17585			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
17586		};
17587
17588		function wrap(node, name, attrs) {
17589			var wrapper = dom.create(name, attrs);
17590
17591			node.parentNode.insertBefore(wrapper, node);
17592			wrapper.appendChild(node);
17593
17594			return wrapper;
17595		};
17596
17597		function expandRng(rng, format, remove) {
17598			var sibling, lastIdx, leaf, endPoint,
17599				startContainer = rng.startContainer,
17600				startOffset = rng.startOffset,
17601				endContainer = rng.endContainer,
17602				endOffset = rng.endOffset;
17603
17604			// This function walks up the tree if there is no siblings before/after the node
17605			function findParentContainer(start) {
17606				var container, parent, child, sibling, siblingName, root;
17607
17608				container = parent = start ? startContainer : endContainer;
17609				siblingName = start ? 'previousSibling' : 'nextSibling';
17610				root = dom.getRoot();
17611
17612				function isBogusBr(node) {
17613					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
17614				};
17615
17616				// If it's a text node and the offset is inside the text
17617				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
17618					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
17619						return container;
17620					}
17621				}
17622
17623				for (;;) {
17624					// Stop expanding on block elements
17625					if (!format[0].block_expand && isBlock(parent))
17626						return parent;
17627
17628					// Walk left/right
17629					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
17630						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
17631							return parent;
17632						}
17633					}
17634
17635					// Check if we can move up are we at root level or body level
17636					if (parent.parentNode == root) {
17637						container = parent;
17638						break;
17639					}
17640
17641					parent = parent.parentNode;
17642				}
17643
17644				return container;
17645			};
17646
17647			// This function walks down the tree to find the leaf at the selection.
17648			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
17649			function findLeaf(node, offset) {
17650				if (offset === undef)
17651					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
17652				while (node && node.hasChildNodes()) {
17653					node = node.childNodes[offset];
17654					if (node)
17655						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
17656				}
17657				return { node: node, offset: offset };
17658			}
17659
17660			// If index based start position then resolve it
17661			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
17662				lastIdx = startContainer.childNodes.length - 1;
17663				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
17664
17665				if (startContainer.nodeType == 3)
17666					startOffset = 0;
17667			}
17668
17669			// If index based end position then resolve it
17670			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
17671				lastIdx = endContainer.childNodes.length - 1;
17672				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
17673
17674				if (endContainer.nodeType == 3)
17675					endOffset = endContainer.nodeValue.length;
17676			}
17677
17678			// Expands the node to the closes contentEditable false element if it exists
17679			function findParentContentEditable(node) {
17680				var parent = node;
17681
17682				while (parent) {
17683					if (parent.nodeType === 1 && getContentEditable(parent)) {
17684						return getContentEditable(parent) === "false" ? parent : node;
17685					}
17686
17687					parent = parent.parentNode;
17688				}
17689
17690				return node;
17691			};
17692
17693			function findWordEndPoint(container, offset, start) {
17694				var walker, node, pos, lastTextNode;
17695
17696				function findSpace(node, offset) {
17697					var pos, pos2, str = node.nodeValue;
17698
17699					if (typeof(offset) == "undefined") {
17700						offset = start ? str.length : 0;
17701					}
17702
17703					if (start) {
17704						pos = str.lastIndexOf(' ', offset);
17705						pos2 = str.lastIndexOf('\u00a0', offset);
17706						pos = pos > pos2 ? pos : pos2;
17707
17708						// Include the space on remove to avoid tag soup
17709						if (pos !== -1 && !remove) {
17710							pos++;
17711						}
17712					} else {
17713						pos = str.indexOf(' ', offset);
17714						pos2 = str.indexOf('\u00a0', offset);
17715						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
17716					}
17717
17718					return pos;
17719				};
17720
17721				if (container.nodeType === 3) {
17722					pos = findSpace(container, offset);
17723
17724					if (pos !== -1) {
17725						return {container : container, offset : pos};
17726					}
17727
17728					lastTextNode = container;
17729				}
17730
17731				// Walk the nodes inside the block
17732				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
17733				while (node = walker[start ? 'prev' : 'next']()) {
17734					if (node.nodeType === 3) {
17735						lastTextNode = node;
17736						pos = findSpace(node);
17737
17738						if (pos !== -1) {
17739							return {container : node, offset : pos};
17740						}
17741					} else if (isBlock(node)) {
17742						break;
17743					}
17744				}
17745
17746				if (lastTextNode) {
17747					if (start) {
17748						offset = 0;
17749					} else {
17750						offset = lastTextNode.length;
17751					}
17752
17753					return {container: lastTextNode, offset: offset};
17754				}
17755			};
17756
17757			function findSelectorEndPoint(container, sibling_name) {
17758				var parents, i, y, curFormat;
17759
17760				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name])
17761					container = container[sibling_name];
17762
17763				parents = getParents(container);
17764				for (i = 0; i < parents.length; i++) {
17765					for (y = 0; y < format.length; y++) {
17766						curFormat = format[y];
17767
17768						// If collapsed state is set then skip formats that doesn't match that
17769						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)
17770							continue;
17771
17772						if (dom.is(parents[i], curFormat.selector))
17773							return parents[i];
17774					}
17775				}
17776
17777				return container;
17778			};
17779
17780			function findBlockEndPoint(container, sibling_name, sibling_name2) {
17781				var node;
17782
17783				// Expand to block of similar type
17784				if (!format[0].wrapper)
17785					node = dom.getParent(container, format[0].block);
17786
17787				// Expand to first wrappable block element or any block element
17788				if (!node)
17789					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isTextBlock);
17790
17791				// Exclude inner lists from wrapping
17792				if (node && format[0].wrapper)
17793					node = getParents(node, 'ul,ol').reverse()[0] || node;
17794
17795				// Didn't find a block element look for first/last wrappable element
17796				if (!node) {
17797					node = container;
17798
17799					while (node[sibling_name] && !isBlock(node[sibling_name])) {
17800						node = node[sibling_name];
17801
17802						// Break on BR but include it will be removed later on
17803						// we can't remove it now since we need to check if it can be wrapped
17804						if (isEq(node, 'br'))
17805							break;
17806					}
17807				}
17808
17809				return node || container;
17810			};
17811
17812			// Expand to closest contentEditable element
17813			startContainer = findParentContentEditable(startContainer);
17814			endContainer = findParentContentEditable(endContainer);
17815
17816			// Exclude bookmark nodes if possible
17817			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
17818				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
17819				startContainer = startContainer.nextSibling || startContainer;
17820
17821				if (startContainer.nodeType == 3)
17822					startOffset = 0;
17823			}
17824
17825			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
17826				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
17827				endContainer = endContainer.previousSibling || endContainer;
17828
17829				if (endContainer.nodeType == 3)
17830					endOffset = endContainer.length;
17831			}
17832
17833			if (format[0].inline) {
17834				if (rng.collapsed) {
17835					// Expand left to closest word boundery
17836					endPoint = findWordEndPoint(startContainer, startOffset, true);
17837					if (endPoint) {
17838						startContainer = endPoint.container;
17839						startOffset = endPoint.offset;
17840					}
17841
17842					// Expand right to closest word boundery
17843					endPoint = findWordEndPoint(endContainer, endOffset);
17844					if (endPoint) {
17845						endContainer = endPoint.container;
17846						endOffset = endPoint.offset;
17847					}
17848				}
17849
17850				// Avoid applying formatting to a trailing space.
17851				leaf = findLeaf(endContainer, endOffset);
17852				if (leaf.node) {
17853					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)
17854						leaf = findLeaf(leaf.node.previousSibling);
17855
17856					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
17857							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
17858
17859						if (leaf.offset > 1) {
17860							endContainer = leaf.node;
17861							endContainer.splitText(leaf.offset - 1);
17862						}
17863					}
17864				}
17865			}
17866
17867			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
17868			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
17869			// This will reduce the number of wrapper elements that needs to be created
17870			// Move start point up the tree
17871			if (format[0].inline || format[0].block_expand) {
17872				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
17873					startContainer = findParentContainer(true);
17874				}
17875
17876				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
17877					endContainer = findParentContainer();
17878				}
17879			}
17880
17881			// Expand start/end container to matching selector
17882			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
17883				// Find new startContainer/endContainer if there is better one
17884				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
17885				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
17886			}
17887
17888			// Expand start/end container to matching block element or text node
17889			if (format[0].block || format[0].selector) {
17890				// Find new startContainer/endContainer if there is better one
17891				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
17892				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
17893
17894				// Non block element then try to expand up the leaf
17895				if (format[0].block) {
17896					if (!isBlock(startContainer))
17897						startContainer = findParentContainer(true);
17898
17899					if (!isBlock(endContainer))
17900						endContainer = findParentContainer();
17901				}
17902			}
17903
17904			// Setup index for startContainer
17905			if (startContainer.nodeType == 1) {
17906				startOffset = nodeIndex(startContainer);
17907				startContainer = startContainer.parentNode;
17908			}
17909
17910			// Setup index for endContainer
17911			if (endContainer.nodeType == 1) {
17912				endOffset = nodeIndex(endContainer) + 1;
17913				endContainer = endContainer.parentNode;
17914			}
17915
17916			// Return new range like object
17917			return {
17918				startContainer : startContainer,
17919				startOffset : startOffset,
17920				endContainer : endContainer,
17921				endOffset : endOffset
17922			};
17923		}
17924
17925		function removeFormat(format, vars, node, compare_node) {
17926			var i, attrs, stylesModified;
17927
17928			// Check if node matches format
17929			if (!matchName(node, format))
17930				return FALSE;
17931
17932			// Should we compare with format attribs and styles
17933			if (format.remove != 'all') {
17934				// Remove styles
17935				each(format.styles, function(value, name) {
17936					value = replaceVars(value, vars);
17937
17938					// Indexed array
17939					if (typeof(name) === 'number') {
17940						name = value;
17941						compare_node = 0;
17942					}
17943
17944					if (!compare_node || isEq(getStyle(compare_node, name), value))
17945						dom.setStyle(node, name, '');
17946
17947					stylesModified = 1;
17948				});
17949
17950				// Remove style attribute if it's empty
17951				if (stylesModified && dom.getAttrib(node, 'style') == '') {
17952					node.removeAttribute('style');
17953					node.removeAttribute('data-mce-style');
17954				}
17955
17956				// Remove attributes
17957				each(format.attributes, function(value, name) {
17958					var valueOut;
17959
17960					value = replaceVars(value, vars);
17961
17962					// Indexed array
17963					if (typeof(name) === 'number') {
17964						name = value;
17965						compare_node = 0;
17966					}
17967
17968					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
17969						// Keep internal classes
17970						if (name == 'class') {
17971							value = dom.getAttrib(node, name);
17972							if (value) {
17973								// Build new class value where everything is removed except the internal prefixed classes
17974								valueOut = '';
17975								each(value.split(/\s+/), function(cls) {
17976									if (/mce\w+/.test(cls))
17977										valueOut += (valueOut ? ' ' : '') + cls;
17978								});
17979
17980								// We got some internal classes left
17981								if (valueOut) {
17982									dom.setAttrib(node, name, valueOut);
17983									return;
17984								}
17985							}
17986						}
17987
17988						// IE6 has a bug where the attribute doesn't get removed correctly
17989						if (name == "class")
17990							node.removeAttribute('className');
17991
17992						// Remove mce prefixed attributes
17993						if (MCE_ATTR_RE.test(name))
17994							node.removeAttribute('data-mce-' + name);
17995
17996						node.removeAttribute(name);
17997					}
17998				});
17999
18000				// Remove classes
18001				each(format.classes, function(value) {
18002					value = replaceVars(value, vars);
18003
18004					if (!compare_node || dom.hasClass(compare_node, value))
18005						dom.removeClass(node, value);
18006				});
18007
18008				// Check for non internal attributes
18009				attrs = dom.getAttribs(node);
18010				for (i = 0; i < attrs.length; i++) {
18011					if (attrs[i].nodeName.indexOf('_') !== 0)
18012						return FALSE;
18013				}
18014			}
18015
18016			// Remove the inline child if it's empty for example <b> or <span>
18017			if (format.remove != 'none') {
18018				removeNode(node, format);
18019				return TRUE;
18020			}
18021		};
18022
18023		function removeNode(node, format) {
18024			var parentNode = node.parentNode, rootBlockElm;
18025
18026			function find(node, next, inc) {
18027				node = getNonWhiteSpaceSibling(node, next, inc);
18028
18029				return !node || (node.nodeName == 'BR' || isBlock(node));
18030			};
18031
18032			if (format.block) {
18033				if (!forcedRootBlock) {
18034					// Append BR elements if needed before we remove the block
18035					if (isBlock(node) && !isBlock(parentNode)) {
18036						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))
18037							node.insertBefore(dom.create('br'), node.firstChild);
18038
18039						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))
18040							node.appendChild(dom.create('br'));
18041					}
18042				} else {
18043					// Wrap the block in a forcedRootBlock if we are at the root of document
18044					if (parentNode == dom.getRoot()) {
18045						if (!format.list_block || !isEq(node, format.list_block)) {
18046							each(tinymce.grep(node.childNodes), function(node) {
18047								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
18048									if (!rootBlockElm)
18049										rootBlockElm = wrap(node, forcedRootBlock);
18050									else
18051										rootBlockElm.appendChild(node);
18052								} else
18053									rootBlockElm = 0;
18054							});
18055						}
18056					}
18057				}
18058			}
18059
18060			// Never remove nodes that isn't the specified inline element if a selector is specified too
18061			if (format.selector && format.inline && !isEq(format.inline, node))
18062				return;
18063
18064			dom.remove(node, 1);
18065		};
18066
18067		function getNonWhiteSpaceSibling(node, next, inc) {
18068			if (node) {
18069				next = next ? 'nextSibling' : 'previousSibling';
18070
18071				for (node = inc ? node : node[next]; node; node = node[next]) {
18072					if (node.nodeType == 1 || !isWhiteSpaceNode(node))
18073						return node;
18074				}
18075			}
18076		};
18077
18078		function isBookmarkNode(node) {
18079			return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';
18080		};
18081
18082		function mergeSiblings(prev, next) {
18083			var marker, sibling, tmpSibling;
18084
18085			function compareElements(node1, node2) {
18086				// Not the same name
18087				if (node1.nodeName != node2.nodeName)
18088					return FALSE;
18089
18090				function getAttribs(node) {
18091					var attribs = {};
18092
18093					each(dom.getAttribs(node), function(attr) {
18094						var name = attr.nodeName.toLowerCase();
18095
18096						// Don't compare internal attributes or style
18097						if (name.indexOf('_') !== 0 && name !== 'style')
18098							attribs[name] = dom.getAttrib(node, name);
18099					});
18100
18101					return attribs;
18102				};
18103
18104				function compareObjects(obj1, obj2) {
18105					var value, name;
18106
18107					for (name in obj1) {
18108						// Obj1 has item obj2 doesn't have
18109						if (obj1.hasOwnProperty(name)) {
18110							value = obj2[name];
18111
18112							// Obj2 doesn't have obj1 item
18113							if (value === undef)
18114								return FALSE;
18115
18116							// Obj2 item has a different value
18117							if (obj1[name] != value)
18118								return FALSE;
18119
18120							// Delete similar value
18121							delete obj2[name];
18122						}
18123					}
18124
18125					// Check if obj 2 has something obj 1 doesn't have
18126					for (name in obj2) {
18127						// Obj2 has item obj1 doesn't have
18128						if (obj2.hasOwnProperty(name))
18129							return FALSE;
18130					}
18131
18132					return TRUE;
18133				};
18134
18135				// Attribs are not the same
18136				if (!compareObjects(getAttribs(node1), getAttribs(node2)))
18137					return FALSE;
18138
18139				// Styles are not the same
18140				if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))
18141					return FALSE;
18142
18143				return TRUE;
18144			};
18145
18146			function findElementSibling(node, sibling_name) {
18147				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
18148					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)
18149						return node;
18150
18151					if (sibling.nodeType == 1 && !isBookmarkNode(sibling))
18152						return sibling;
18153				}
18154
18155				return node;
18156			};
18157
18158			// Check if next/prev exists and that they are elements
18159			if (prev && next) {
18160				// If previous sibling is empty then jump over it
18161				prev = findElementSibling(prev, 'previousSibling');
18162				next = findElementSibling(next, 'nextSibling');
18163
18164				// Compare next and previous nodes
18165				if (compareElements(prev, next)) {
18166					// Append nodes between
18167					for (sibling = prev.nextSibling; sibling && sibling != next;) {
18168						tmpSibling = sibling;
18169						sibling = sibling.nextSibling;
18170						prev.appendChild(tmpSibling);
18171					}
18172
18173					// Remove next node
18174					dom.remove(next);
18175
18176					// Move children into prev node
18177					each(tinymce.grep(next.childNodes), function(node) {
18178						prev.appendChild(node);
18179					});
18180
18181					return prev;
18182				}
18183			}
18184
18185			return next;
18186		};
18187
18188		function getContainer(rng, start) {
18189			var container, offset, lastIdx, walker;
18190
18191			container = rng[start ? 'startContainer' : 'endContainer'];
18192			offset = rng[start ? 'startOffset' : 'endOffset'];
18193
18194			if (container.nodeType == 1) {
18195				lastIdx = container.childNodes.length - 1;
18196
18197				if (!start && offset)
18198					offset--;
18199
18200				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
18201			}
18202
18203			// If start text node is excluded then walk to the next node
18204			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
18205				container = new TreeWalker(container, ed.getBody()).next() || container;
18206			}
18207
18208			// If end text node is excluded then walk to the previous node
18209			if (container.nodeType === 3 && !start && offset === 0) {
18210				container = new TreeWalker(container, ed.getBody()).prev() || container;
18211			}
18212
18213			return container;
18214		};
18215
18216		function performCaretAction(type, name, vars) {
18217			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
18218
18219			// Creates a caret container bogus element
18220			function createCaretContainer(fill) {
18221				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
18222
18223				if (fill) {
18224					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
18225				}
18226
18227				return caretContainer;
18228			};
18229
18230			function isCaretContainerEmpty(node, nodes) {
18231				while (node) {
18232					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
18233						return false;
18234					}
18235
18236					// Collect nodes
18237					if (nodes && node.nodeType === 1) {
18238						nodes.push(node);
18239					}
18240
18241					node = node.firstChild;
18242				}
18243
18244				return true;
18245			};
18246			
18247			// Returns any parent caret container element
18248			function getParentCaretContainer(node) {
18249				while (node) {
18250					if (node.id === caretContainerId) {
18251						return node;
18252					}
18253
18254					node = node.parentNode;
18255				}
18256			};
18257
18258			// Finds the first text node in the specified node
18259			function findFirstTextNode(node) {
18260				var walker;
18261
18262				if (node) {
18263					walker = new TreeWalker(node, node);
18264
18265					for (node = walker.current(); node; node = walker.next()) {
18266						if (node.nodeType === 3) {
18267							return node;
18268						}
18269					}
18270				}
18271			};
18272
18273			// Removes the caret container for the specified node or all on the current document
18274			function removeCaretContainer(node, move_caret) {
18275				var child, rng;
18276
18277				if (!node) {
18278					node = getParentCaretContainer(selection.getStart());
18279
18280					if (!node) {
18281						while (node = dom.get(caretContainerId)) {
18282							removeCaretContainer(node, false);
18283						}
18284					}
18285				} else {
18286					rng = selection.getRng(true);
18287
18288					if (isCaretContainerEmpty(node)) {
18289						if (move_caret !== false) {
18290							rng.setStartBefore(node);
18291							rng.setEndBefore(node);
18292						}
18293
18294						dom.remove(node);
18295					} else {
18296						child = findFirstTextNode(node);
18297
18298						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
18299							child = child.deleteData(0, 1);
18300						}
18301
18302						dom.remove(node, 1);
18303					}
18304
18305					selection.setRng(rng);
18306				}
18307			};
18308			
18309			// Applies formatting to the caret postion
18310			function applyCaretFormat() {
18311				var rng, caretContainer, textNode, offset, bookmark, container, text;
18312
18313				rng = selection.getRng(true);
18314				offset = rng.startOffset;
18315				container = rng.startContainer;
18316				text = container.nodeValue;
18317
18318				caretContainer = getParentCaretContainer(selection.getStart());
18319				if (caretContainer) {
18320					textNode = findFirstTextNode(caretContainer);
18321				}
18322
18323				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
18324				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
18325					// Get bookmark of caret position
18326					bookmark = selection.getBookmark();
18327
18328					// Collapse bookmark range (WebKit)
18329					rng.collapse(true);
18330
18331					// Expand the range to the closest word and split it at those points
18332					rng = expandRng(rng, get(name));
18333					rng = rangeUtils.split(rng);
18334
18335					// Apply the format to the range
18336					apply(name, vars, rng);
18337
18338					// Move selection back to caret position
18339					selection.moveToBookmark(bookmark);
18340				} else {
18341					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
18342						caretContainer = createCaretContainer(true);
18343						textNode = caretContainer.firstChild;
18344
18345						rng.insertNode(caretContainer);
18346						offset = 1;
18347
18348						apply(name, vars, caretContainer);
18349					} else {
18350						apply(name, vars, caretContainer);
18351					}
18352
18353					// Move selection to text node
18354					selection.setCursorLocation(textNode, offset);
18355				}
18356			};
18357
18358			function removeCaretFormat() {
18359				var rng = selection.getRng(true), container, offset, bookmark,
18360					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
18361
18362				container = rng.startContainer;
18363				offset = rng.startOffset;
18364				node = container;
18365
18366				if (container.nodeType == 3) {
18367					if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
18368						hasContentAfter = true;
18369					}
18370
18371					node = node.parentNode;
18372				}
18373
18374				while (node) {
18375					if (matchNode(node, name, vars)) {
18376						formatNode = node;
18377						break;
18378					}
18379
18380					if (node.nextSibling) {
18381						hasContentAfter = true;
18382					}
18383
18384					parents.push(node);
18385					node = node.parentNode;
18386				}
18387
18388				// Node doesn't have the specified format
18389				if (!formatNode) {
18390					return;
18391				}
18392
18393				// Is there contents after the caret then remove the format on the element
18394				if (hasContentAfter) {
18395					// Get bookmark of caret position
18396					bookmark = selection.getBookmark();
18397
18398					// Collapse bookmark range (WebKit)
18399					rng.collapse(true);
18400
18401					// Expand the range to the closest word and split it at those points
18402					rng = expandRng(rng, get(name), true);
18403					rng = rangeUtils.split(rng);
18404
18405					// Remove the format from the range
18406					remove(name, vars, rng);
18407
18408					// Move selection back to caret position
18409					selection.moveToBookmark(bookmark);
18410				} else {
18411					caretContainer = createCaretContainer();
18412
18413					node = caretContainer;
18414					for (i = parents.length - 1; i >= 0; i--) {
18415						node.appendChild(dom.clone(parents[i], false));
18416						node = node.firstChild;
18417					}
18418
18419					// Insert invisible character into inner most format element
18420					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
18421					node = node.firstChild;
18422
18423					var block = dom.getParent(formatNode, isTextBlock);
18424
18425					if (block && dom.isEmpty(block)) {
18426						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
18427						formatNode.parentNode.replaceChild(caretContainer, formatNode);
18428					} else {
18429						// Insert caret container after the formated node
18430						dom.insertAfter(caretContainer, formatNode);
18431					}
18432
18433					// Move selection to text node
18434					selection.setCursorLocation(node, 1);
18435
18436					// If the formatNode is empty, we can remove it safely. 
18437					if (dom.isEmpty(formatNode)) {
18438						dom.remove(formatNode);
18439					}
18440				}
18441			};
18442
18443			// Checks if the parent caret container node isn't empty if that is the case it
18444			// will remove the bogus state on all children that isn't empty
18445			function unmarkBogusCaretParents() {
18446				var i, caretContainer, node;
18447
18448				caretContainer = getParentCaretContainer(selection.getStart());
18449				if (caretContainer && !dom.isEmpty(caretContainer)) {
18450					tinymce.walk(caretContainer, function(node) {
18451						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
18452							dom.setAttrib(node, 'data-mce-bogus', null);
18453						}
18454					}, 'childNodes');
18455				}
18456			};
18457
18458			// Only bind the caret events once
18459			if (!self._hasCaretEvents) {
18460				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
18461				ed.onBeforeGetContent.addToTop(function() {
18462					var nodes = [], i;
18463
18464					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
18465						// Mark children
18466						i = nodes.length;
18467						while (i--) {
18468							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
18469						}
18470					}
18471				});
18472
18473				// Remove caret container on mouse up and on key up
18474				tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
18475					ed[name].addToTop(function() {
18476						removeCaretContainer();
18477						unmarkBogusCaretParents();
18478					});
18479				});
18480
18481				// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
18482				ed.onKeyDown.addToTop(function(ed, e) {
18483					var keyCode = e.keyCode;
18484
18485					if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
18486						removeCaretContainer(getParentCaretContainer(selection.getStart()));
18487					}
18488
18489					unmarkBogusCaretParents();
18490				});
18491
18492				// Remove bogus state if they got filled by contents using editor.selection.setContent
18493				selection.onSetContent.add(unmarkBogusCaretParents);
18494
18495				self._hasCaretEvents = true;
18496			}
18497
18498			// Do apply or remove caret format
18499			if (type == "apply") {
18500				applyCaretFormat();
18501			} else {
18502				removeCaretFormat();
18503			}
18504		};
18505
18506		function moveStart(rng) {
18507			var container = rng.startContainer,
18508					offset = rng.startOffset, isAtEndOfText,
18509					walker, node, nodes, tmpNode;
18510
18511			// Convert text node into index if possible
18512			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
18513				// Get the parent container location and walk from there
18514				offset = nodeIndex(container);
18515				container = container.parentNode;
18516				isAtEndOfText = true;
18517			}
18518
18519			// Move startContainer/startOffset in to a suitable node
18520			if (container.nodeType == 1) {
18521				nodes = container.childNodes;
18522				container = nodes[Math.min(offset, nodes.length - 1)];
18523				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
18524
18525				// If offset is at end of the parent node walk to the next one
18526				if (offset >
18526 nodes.length - 1 || isAtEndOfText)
18527					walker.next();
18528
18529				for (node = walker.current(); node; node = walker.next()) {
18530					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
18531						// IE has a "neat" feature where it moves the start node into the closest element
18532						// we can avoid this by inserting an element before it and then remove it after we set the selection
18533						tmpNode = dom.create('a', null, INVISIBLE_CHAR);
18534						node.parentNode.insertBefore(tmpNode, node);
18535
18536						// Set selection and remove tmpNode
18537						rng.setStart(node, 0);
18538						selection.setRng(rng);
18539						dom.remove(tmpNode);
18540
18541						return;
18542					}
18543				}
18544			}
18545		};
18546	};
18547})(tinymce);
18548
18549tinymce.onAddEditor.add(function(tinymce, ed) {
18550	var filters, fontSizes, dom, settings = ed.settings;
18551
18552	function replaceWithSpan(node, styles) {
18553		tinymce.each(styles, function(value, name) {
18554			if (value)
18555				dom.setStyle(node, name, value);
18556		});
18557
18558		dom.rename(node, 'span');
18559	};
18560
18561	function convert(editor, params) {
18562		dom = editor.dom;
18563
18564		if (settings.convert_fonts_to_spans) {
18565			tinymce.each(dom.select('font,u,strike', params.node), function(node) {
18566				filters[node.nodeName.toLowerCase()](ed.dom, node);
18567			});
18568		}
18569	};
18570
18571	if (settings.inline_styles) {
18572		fontSizes = tinymce.explode(settings.font_size_legacy_values);
18573
18574		filters = {
18575			font : function(dom, node) {
18576				replaceWithSpan(node, {
18577					backgroundColor : node.style.backgroundColor,
18578					color : node.color,
18579					fontFamily : node.face,
18580					fontSize : fontSizes[parseInt(node.size, 10) - 1]
18581				});
18582			},
18583
18584			u : function(dom, node) {
18585				replaceWithSpan(node, {
18586					textDecoration : 'underline'
18587				});
18588			},
18589
18590			strike : function(dom, node) {
18591				replaceWithSpan(node, {
18592					textDecoration : 'line-through'
18593				});
18594			}
18595		};
18596
18597		ed.onPreProcess.add(convert);
18598		ed.onSetContent.add(convert);
18599
18600		ed.onInit.add(function() {
18601			ed.selection.onSetContent.add(convert);
18602		});
18603	}
18604});
18605
18606(function(tinymce) {
18607	var TreeWalker = tinymce.dom.TreeWalker;
18608
18609	tinymce.EnterKey = function(editor) {
18610		var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager, nonEmptyElementsMap = editor.schema.getNonEmptyElements();
18611
18612		function handleEnterKey(evt) {
18613			var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
18614				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
18615
18616			// Returns true if the block can be split into two blocks or not
18617			function canSplitBlock(node) {
18618				return node &&
18619					dom.isBlock(node) &&
18620					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
18621					!/^(fixed|absolute)/i.test(node.style.position) && 
18622					dom.getContentEditable(node) !== "true";
18623			};
18624
18625			// Renders empty block on IE
18626			function renderBlockOnIE(block) {
18627				var oldRng;
18628
18629				if (tinymce.isIE && !tinymce.isIE11 && dom.isBlock(block)) {
18630					oldRng = selection.getRng();
18631					block.appendChild(dom.create('span', null, '\u00a0'));
18632					selection.select(block);
18633					block.lastChild.outerHTML = '';
18634					selection.setRng(oldRng);
18635				}
18636			};
18637
18638			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
18639			function trimInlineElementsOnLeftSideOfBlock(block) {
18640				var node = block, firstChilds = [], i;
18641
18642				// Find inner most first child ex: <p><i><b>*</b></i></p>
18643				while (node = node.firstChild) {
18644					if (dom.isBlock(node)) {
18645						return;
18646					}
18647
18648					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
18649						firstChilds.push(node);
18650					}
18651				}
18652
18653				i = firstChilds.length;
18654				while (i--) {
18655					node = firstChilds[i];
18656					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
18657						dom.remove(node);
18658					} else {
18659						// Remove <a> </a> see #5381
18660						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
18661							dom.remove(node);
18662						}
18663					}
18664				}
18665			};
18666			
18667			// Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
18668			function moveToCaretPosition(root) {
18669				var walker, node, rng, y, viewPort, lastNode = root, tempElm;
18670
18671				rng = dom.createRng();
18672
18673				if (root.hasChildNodes()) {
18674					walker = new TreeWalker(root, root);
18675
18676					while (node = walker.current()) {
18677						if (node.nodeType == 3) {
18678							rng.setStart(node, 0);
18679							rng.setEnd(node, 0);
18680							break;
18681						}
18682
18683						if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
18684							rng.setStartBefore(node);
18685							rng.setEndBefore(node);
18686							break;
18687						}
18688
18689						lastNode = node;
18690						node = walker.next();
18691					}
18692
18693					if (!node) {
18694						rng.setStart(lastNode, 0);
18695						rng.setEnd(lastNode, 0);
18696					}
18697				} else {
18698					if (root.nodeName == 'BR') {
18699						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
18700							// Trick on older IE versions to render the caret before the BR between two lists
18701							if (!documentMode || documentMode < 9) {
18702								tempElm = dom.create('br');
18703								root.parentNode.insertBefore(tempElm, root);
18704							}
18705
18706							rng.setStartBefore(root);
18707							rng.setEndBefore(root);
18708						} else {
18709							rng.setStartAfter(root);
18710							rng.setEndAfter(root);
18711						}
18712					} else {
18713						rng.setStart(root, 0);
18714						rng.setEnd(root, 0);
18715					}
18716				}
18717
18718				selection.setRng(rng);
18719
18720				// Remove tempElm created for old IE:s
18721				dom.remove(tempElm);
18722
18723				viewPort = dom.getViewPort(editor.getWin());
18724
18725				// scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
18726				y = dom.getPos(root).y;
18727				if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
18728					editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
18729				}
18730			};
18731
18732			// Creates a new block element by cloning the current one or creating a new one if the name is specified
18733			// This function will also copy any text formatting from the parent block and add it to the new one
18734			function createNewBlock(name) {
18735				var node = container, block, clonedNode, caretNode;
18736
18737				block = name || parentBlockName == "TABLE" ? dom.create(name || newBlockName) : parentBlock.cloneNode(false);
18738				caretNode = block;
18739
18740				// Clone any parent styles
18741				if (settings.keep_styles !== false) {
18742					do {
18743						if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
18744							// Never clone a caret containers
18745							if (node.id == '_mce_caret') {
18746								continue;
18747							}
18748
18749							clonedNode = node.cloneNode(false);
18750							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
18751
18752							if (block.hasChildNodes()) {
18753								clonedNode.appendChild(block.firstChild);
18754								block.appendChild(clonedNode);
18755							} else {
18756								caretNode = clonedNode;
18757								block.appendChild(clonedNode);
18758							}
18759						}
18760					} while (node = node.parentNode);
18761				}
18762
18763				// BR is needed in empty blocks on non IE browsers
18764				if (!tinymce.isIE || tinymce.isIE11) {
18765					caretNode.innerHTML = '<br data-mce-bogus="1">';
18766				}
18767
18768				return block;
18769			};
18770
18771			// Returns true/false if the caret is at the start/end of the parent block element
18772			function isCaretAtStartOrEndOfBlock(start) {
18773				var walker, node, name;
18774
18775				// Caret is in the middle of a text node like "a|b"
18776				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
18777					return false;
18778				}
18779
18780				// If after the last element in block node edge case for #5091
18781				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
18782					return true;
18783				}
18784
18785				// If the caret if before the first element in parentBlock
18786				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
18787					return true;
18788				}
18789
18790				// Caret can be before/after a table
18791				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
18792					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
18793				}
18794
18795				// Walk the DOM and look for text nodes or non empty elements
18796				walker = new TreeWalker(container, parentBlock);
18797	
18798				// If caret is in beginning or end of a text block then jump to the next/previous node
18799				if (container.nodeType == 3) {
18800					if (start && offset == 0) {
18801						walker.prev();
18802					} else if (!start && offset == container.nodeValue.length) {
18803						walker.next();
18804					}
18805				}
18806
18807				while (node = walker.current()) {
18808					if (node.nodeType === 1) {
18809						// Ignore bogus elements
18810						if (!node.getAttribute('data-mce-bogus')) {
18811							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
18812							name = node.nodeName.toLowerCase();
18813							if (nonEmptyElementsMap[name] && name !== 'br') {
18814								return false;
18815							}
18816						}
18817					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
18818						return false;
18819					}
18820
18821					if (start) {
18822						walker.prev();
18823					} else {
18824						walker.next();
18825					}
18826				}
18827
18828				return true;
18829			};
18830
18831			// Wraps any text nodes or inline elements in the specified forced root block name
18832			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
18833				var newBlock, parentBlock, startNode, node, next, blockName = newBlockName || 'P';
18834
18835				// Not in a block element or in a table cell or caption
18836				parentBlock = dom.getParent(container, dom.isBlock);
18837				if (!parentBlock || !canSplitBlock(parentBlock)) {
18838					parentBlock = parentBlock || editableRoot;
18839
18840					if (!parentBlock.hasChildNodes()) {
18841						newBlock = dom.create(blockName);
18842						parentBlock.appendChild(newBlock);
18843						rng.setStart(newBlock, 0);
18844						rng.setEnd(newBlock, 0);
18845						return newBlock;
18846					}
18847
18848					// Find parent that is the first child of parentBlock
18849					node = container;
18850					while (node.parentNode != parentBlock) {
18851						node = node.parentNode;
18852					}
18853
18854					// Loop left to find start node start wrapping at
18855					while (node && !dom.isBlock(node)) {
18856						startNode = node;
18857						node = node.previousSibling;
18858					}
18859
18860					if (startNode) {
18861						newBlock = dom.create(blockName);
18862						startNode.parentNode.insertBefore(newBlock, startNode);
18863
18864						// Start wrapping until we hit a block
18865						node = startNode;
18866						while (node && !dom.isBlock(node)) {
18867							next = node.nextSibling;
18868							newBlock.appendChild(node);
18869							node = next;
18870						}
18871
18872						// Restore range to it's past location
18873						rng.setStart(container, offset);
18874						rng.setEnd(container, offset);
18875					}
18876				}
18877
18878				return container;
18879			};
18880
18881			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
18882			function handleEmptyListItem() {
18883				function isFirstOrLastLi(first) {
18884					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
18885
18886					// Find first/last element since there might be whitespace there
18887					while (node) {
18888						if (node.nodeType == 1) {
18889							break;
18890						}
18891
18892						node = node[first ? 'nextSibling' : 'previousSibling'];
18893					}
18894
18895					return node === parentBlock;
18896				};
18897
18898				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
18899
18900				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
18901					// Is first and last list item then replace the OL/UL with a text block
18902					dom.replace(newBlock, containerBlock);
18903				} else if (isFirstOrLastLi(true)) {
18904					// First LI in list then remove LI and add text block before list
18905					containerBlock.parentNode.insertBefore(newBlock, containerBlock);
18906				} else if (isFirstOrLastLi()) {
18907					// Last LI in list then temove LI and add text block after list
18908					dom.insertAfter(newBlock, containerBlock);
18909					renderBlockOnIE(newBlock);
18910				} else {
18911					// Middle LI in list the split the list and insert a text block in the middle
18912					// Extract after fragment and insert it after the current block
18913					tmpRng = rng.cloneRange();
18914					tmpRng.setStartAfter(parentBlock);
18915					tmpRng.setEndAfter(containerBlock);
18916					fragment = tmpRng.extractContents();
18917					dom.insertAfter(fragment, containerBlock);
18918					dom.insertAfter(newBlock, containerBlock);
18919				}
18920
18921				dom.remove(parentBlock);
18922				moveToCaretPosition(newBlock);
18923				undoManager.add();
18924			};
18925
18926			// Walks the parent block to the right and look for any contents
18927			function hasRightSideContent() {
18928				var walker = new TreeWalker(container, parentBlock), node;
18929
18930				while (node = walker.next()) {
18931					if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
18932						return true;
18933					}
18934				}
18935			}
18936
18937			// Inserts a BR element if the forced_root_block option is set to false or empty string
18938			function insertBr() {
18939				var brElm, extraBr, marker;
18940
18941				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
18942					// Insert extra BR element at the end block elements
18943					if ((!tinymce.isIE || tinymce.isIE11) && !hasRightSideContent()) {
18944						brElm = dom.create('br');
18945						rng.insertNode(brElm);
18946						rng.setStartAfter(brElm);
18947						rng.setEndAfter(brElm);
18948						extraBr = true;
18949					}
18950				}
18951
18952				brElm = dom.create('br');
18953				rng.insertNode(brElm);
18954
18955				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
18956				if ((tinymce.isIE && !tinymce.isIE11) && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
18957					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
18958				}
18959
18960				// Insert temp marker and scroll to that
18961				marker = dom.create('span', {}, '&nbsp;');
18962				brElm.parentNode.insertBefore(marker, brElm);
18963				selection.scrollIntoView(marker);
18964				dom.remove(marker);
18965
18966				if (!extraBr) {
18967					rng.setStartAfter(brElm);
18968					rng.setEndAfter(brElm);
18969				} else {
18970					rng.setStartBefore(brElm);
18971					rng.setEndBefore(brElm);
18972				}
18973
18974				selection.setRng(rng);
18975				undoManager.add();
18976			};
18977
18978			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
18979			function trimLeadingLineBreaks(node) {
18980				do {
18981					if (node.nodeType === 3) {
18982						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
18983					}
18984
18985					node = node.firstChild;
18986				} while (node);
18987			};
18988
18989			function getEditableRoot(node) {
18990				var root = dom.getRoot(), parent, editableRoot;
18991
18992				// Get all parents until we hit a non editable parent or the root
18993				parent = node;
18994				while (parent !== root && dom.getContentEditable(parent) !== "false") {
18995					if (dom.getContentEditable(parent) === "true") {
18996						editableRoot = parent;
18997					}
18998
18999					parent = parent.parentNode;
19000				}
19001				
19002				return parent !== root ? editableRoot : root;
19003			};
19004
19005			// Adds a BR at the end of blocks that only contains an IMG or INPUT since these might be floated and then they won't expand the block
19006			function addBrToBlockIfNeeded(block) {
19007				var lastChild;
19008
19009				// IE will render the blocks correctly other browsers needs a BR
19010				if (!tinymce.isIE || tinymce.isIE11) {
19011					block.normalize(); // Remove empty text nodes that got left behind by the extract
19012
19013					// Check if the block is empty or contains a floated last child
19014					lastChild = block.lastChild;
19015					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
19016						dom.add(block, 'br');
19017					}
19018				}
19019			};
19020
19021			// Delete any selected contents
19022			if (!rng.collapsed) {
19023				editor.execCommand('Delete');
19024				return;
19025			}
19026
19027			// Event is blocked by some other handler for example the lists plugin
19028			if (evt.isDefaultPrevented()) {
19029				return;
19030			}
19031
19032			// Setup range items and newBlockName
19033			container = rng.startContainer;
19034			offset = rng.startOffset;
19035			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
19036			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
19037			documentMode = dom.doc.documentMode;
19038			shiftKey = evt.shiftKey;
19039
19040			// Resolve node index
19041			if (container.nodeType == 1 && container.hasChildNodes()) {
19042				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
19043				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
19044				if (isAfterLastNodeInContainer && container.nodeType == 3) {
19045					offset = container.nodeValue.length;
19046				} else {
19047					offset = 0;
19048				}
19049			}
19050
19051			// Get editable root node normaly the body element but sometimes a div or span
19052			editableRoot = getEditableRoot(container);
19053
19054			// If there is no editable root then enter is done inside a contentEditable false element
19055			if (!editableRoot) {
19056				return;
19057			}
19058
19059			undoManager.beforeChange();
19060
19061			// If editable root isn't block nor the root of the editor
19062			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
19063				if (!newBlockName || shiftKey) {
19064					insertBr();
19065				}
19066
19067				return;
19068			}
19069
19070			// Wrap the current node and it's sibling in a default block if it's needed.
19071			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
19072			// This won't happen if root blocks are disabled or the shiftKey is pressed
19073			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
19074				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
19075			}
19076
19077			// Find parent block and setup empty block paddings
19078			parentBlock = dom.getParent(container, dom.isBlock);
19079			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
19080
19081			// Setup block names
19082			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
19083			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
19084
19085			// Enter inside block contained within a LI then split or insert before/after LI
19086			if (containerBlockName == 'LI' && !evt.ctrlKey) {
19087				parentBlock = containerBlock;
19088				parentBlockName = containerBlockName;
19089			}
19090
19091			// Handle enter in LI
19092			if (parentBlockName == 'LI') {
19093				if (!newBlockName && shiftKey) {
19094					insertBr();
19095					return;
19096				}
19097
19098				// Handle enter inside an empty list item
19099				if (dom.isEmpty(parentBlock)) {
19100					// Let the list plugin or browser handle nested lists for now
19101					if (/^(UL|OL|LI)$/.test(containerBlock.parentNode.nodeName)) {
19102						return false;
19103					}
19104
19105					handleEmptyListItem();
19106					return;
19107				}
19108			}
19109
19110			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
19111			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
19112				if (!shiftKey) {
19113					insertBr();
19114					return;
19115				}
19116			} else {
19117				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
19118				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
19119					insertBr();
19120					return;
19121				}
19122			}
19123
19124			// Default block name if it's not configured
19125			newBlockName = newBlockName || 'P';
19126
19127			// Insert new block before/after the parent block depending on caret location
19128			if (isCaretAtStartOrEndOfBlock()) {
19129				// 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
19130				if (/^(H[1-6]|PRE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
19131					newBlock = createNewBlock(newBlockName);
19132				} else {
19133					newBlock = createNewBlock();
19134				}
19135
19136				// Split the current container block element if enter is pressed inside an empty inner block element
19137				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
19138					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
19139					newBlock = dom.split(containerBlock, parentBlock);
19140				} else {
19141					dom.insertAfter(newBlock, parentBlock);
19142				}
19143
19144				moveToCaretPosition(newBlock);
19145			} else if (isCaretAtStartOrEndOfBlock(true)) {
19146				// Insert new block before
19147				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
19148				renderBlockOnIE(newBlock);
19149			} else {
19150				// Extract after fragment and insert it after the current block
19151				tmpRng = rng.cloneRange();
19152				tmpRng.setEndAfter(parentBlock);
19153				fragment = tmpRng.extractContents();
19154				trimLeadingLineBreaks(fragment);
19155				newBlock = fragment.firstChild;
19156				dom.insertAfter(fragment, parentBlock);
19157				trimInlineElementsOnLeftSideOfBlock(newBlock);
19158				addBrToBlockIfNeeded(parentBlock);
19159				moveToCaretPosition(newBlock);
19160			}
19161
19162			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
19163			undoManager.add();
19164		}
19165
19166		editor.onKeyDown.add(function(ed, evt) {
19167			if (evt.keyCode == 13) {
19168				if (handleEnterKey(evt) !== false) {
19169					evt.preventDefault();
19170				}
19171			}
19172		});
19173	};
19174})(tinymce);
19175

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.