includes/clientside/tinymce/tiny_mce_src.js
changeset 588 20484deb89cd
parent 543 dffcbfbc4e59
child 778 57ce13805b6f
--- a/includes/clientside/tinymce/tiny_mce_src.js	Thu Jun 26 18:05:42 2008 -0400
+++ b/includes/clientside/tinymce/tiny_mce_src.js	Thu Jun 26 20:55:13 2008 -0400
@@ -3,11 +3,11 @@
 
 var tinymce = {
 	majorVersion : '3',
-	minorVersion : '0.8',
-	releaseDate : '2008-04-30',
+	minorVersion : '1.0.1',
+	releaseDate : '2008-06-18',
 
 	_init : function() {
-		var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p;
+		var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
 
 		// Browser checks
 		t.isOpera = w.opera && opera.buildNumber;
@@ -32,8 +32,13 @@
 		// If base element found, add that infront of baseURL
 		nl = d.getElementsByTagName('base');
 		for (i=0; i<nl.length; i++) {
-			if (nl[i].href)
-				base = nl[i].href;
+			if (v = nl[i].href) {
+				// Host only value like http://site.com or http://site.com:8008
+				if (/^https?:\/\/[^\/]+$/.test(v))
+					v += '/';
+
+				base = v ? v.match(/.*\//)[0] : ''; // Get only directory
+			}
 		}
 
 		function getBase(n) {
@@ -876,19 +881,24 @@
 
 			x.send(o.data);
 
-			// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
-			t = w.setInterval(function() {
-				if (x.readyState == 4 || c++ > 10000) {
-					w.clearInterval(t);
-
+			function ready() {
+				if (!o.async || x.readyState == 4 || c++ > 10000) {
 					if (o.success && c < 10000 && x.status == 200)
 						o.success.call(o.success_scope, '' + x.responseText, x, o);
 					else if (o.error)
 						o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
 
 					x = null;
-				}
-			}, 10);
+				} else
+					w.setTimeout(ready, 10);
+			};
+
+			// Syncronous request
+			if (!o.async)
+				return ready();
+
+			// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
+			t = w.setTimeout(ready, 10);
 		}
 
 		}
@@ -1065,7 +1075,7 @@
 					}
 
 					each(na.split(','), function(v) {
-						if (n.nodeType == 1 && ((se.strict && n.nodeName.toUpperCase() == v) || n.nodeName == v)) {
+						if (n.nodeType == 1 && ((se.strict && n.nodeName.toUpperCase() == v) || n.nodeName.toUpperCase() == v)) {
 							s = true;
 							return false; // Break loop
 						}
@@ -1482,6 +1492,10 @@
 						}
 
 						break;
+					
+					case "shape":
+						e.setAttribute('mce_style', v);
+						break;
 				}
 
 				if (is(v) && v !== null && v.length !== 0)
@@ -1515,7 +1529,7 @@
 				dv = "";
 
 			// Try the mce variant for these
-			if (/^(src|href|style|coords)$/.test(n)) {
+			if (/^(src|href|style|coords|shape)$/.test(n)) {
 				v = e.getAttribute("mce_" + n);
 
 				if (v)
@@ -1586,12 +1600,19 @@
 						break;
 
 					case 'tabindex':
-						// IE returns 32768 as default value
+						// IE returns default value
 						if (v === 32768)
 							v = '';
 
 						break;
 
+					case 'maxlength':
+						// IE returns  default value
+						if (v === 2147483647)
+							v = '';
+
+						break;
+
 					case 'shape':
 						v = v.toLowerCase();
 						break;
@@ -1702,10 +1723,13 @@
 				delete o[c];
 			};
 
+			st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
+
 			each(st.split(';'), function(v) {
 				var sv, ur = [];
 
 				if (v) {
+					v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities
 					v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
 					v = v.split(':');
 					sv = tinymce.trim(v[1]);
@@ -1717,7 +1741,7 @@
 
 					if (s.url_converter) {
 						sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
-							return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null)) + ')';
+							return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';
 						});
 					}
 
@@ -1747,6 +1771,9 @@
 
 			each(o, function(v, k) {
 				if (k && v) {
+					if (tinymce.isGecko && k.indexOf('-moz-') === 0)
+						return;
+
 					switch (k) {
 						case 'color':
 						case 'background-color':
@@ -1975,16 +2002,43 @@
 
 			// Store away src and href in mce_src and mce_href since browsers mess them up
 			if (s.keep_values) {
-				// Wrap scripts in comments for serialization purposes
-				if (h.indexOf('<script') !== -1) {
-					h = h.replace(/<script>/g, '<script type="text/javascript">');
-					h = h.replace(/<script(|[^>]+)>(\s*<!--|\/\/\s*<\[CDATA\[)?[\r\n]*/g, '<mce:script$1><!--\n');
-					h = h.replace(/\s*(\/\/\s*-->|\/\/\s*]]>)?<\/script>/g, '\n// --></mce:script>');
-					h = h.replace(/<mce:script(|[^>]+)><!--\n\/\/ --><\/mce:script>/g, '<mce:script$1></mce:script>');
+				// Wrap scripts and styles in comments for serialization purposes
+				if (/<script|style/.test(h)) {
+					function trim(s) {
+						// Remove prefix and suffix code for element
+						s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');
+						s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<\[CDATA\[|<!--|<\[CDATA\[)[\r\n]*/g, '');
+						s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->)\s*$/g, '');
+
+						return s;
+					};
+
+					// Preserve script elements
+					h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/g, function(v, a, b) {
+						// Remove prefix and suffix code for script element
+						b = trim(b);
+
+						// Force type attribute
+						if (!a)
+							a = ' type="text/javascript"';
+
+						// Wrap contents in a comment
+						if (b)
+							b = '<!--\n' + b + '\n// -->';
+
+						// Output fake element
+						return '<mce:script' + a + '>' + b + '</mce:script>';
+					});
+
+					// Preserve style elements
+					h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/g, function(v, a, b) {
+						b = trim(b);
+						return '<mce:style' + a + '><!--\n' + b + '\n--></mce:style><style' + a + ' mce_bogus="1">' + b + '</style>';
+					});
 				}
 
 				// Process all tags with src, href or style
-				h = h.replace(/<([\w:]+) [^>]*(src|href|style|coords)[^>]*>/gi, function(a, n) {
+				h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) {
 					function handle(m, b, c) {
 						var u = c;
 
@@ -2012,7 +2066,7 @@
 									return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')';
 								});
 							}
-						} else if (b != 'coords') {
+						} else if (b != 'coords' && b != 'shape') {
 							if (s.url_converter)
 								u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));
 						}
@@ -2020,10 +2074,10 @@
 						return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';
 					};
 
-					a = a.replace(/ (src|href|style|coords)=[\"]([^\"]+)[\"]/gi, handle); // W3C
-					a = a.replace(/ (src|href|style|coords)=[\']([^\']+)[\']/gi, handle); // W3C
-
-					return a.replace(/ (src|href|style|coords)=([^\s\"\'>]+)/gi, handle); // IE
+					a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C
+					a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C
+
+					return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE
 				});
 			}
 
@@ -2138,7 +2192,7 @@
 
 			n = n.nodeName || n;
 
-			return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);
+			return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);
 		},
 
 		// #if !jquery
@@ -2313,8 +2367,7 @@
 
 		_isRes : function(c) {
 			// Is live resizble element
-
-			return /^(top|left|bottom|right|width|height)/i.test(c) || /^[;\s](top|left|bottom|right|width|height)/i.test(c);
+			return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
 		}
 
 		/*
@@ -2555,8 +2608,10 @@
 			var t;
 
 			// No need since the document is already loaded
-			if (window.tinyMCE_GZ && tinyMCE_GZ.loaded)
+			if (window.tinyMCE_GZ && tinyMCE_GZ.loaded) {
+				Event.domLoaded = 1;
 				return;
+			}
 
 			if (isIE && document.location.protocol != 'https:') {
 				// Fake DOMContentLoaded on IE
@@ -2784,15 +2839,13 @@
 		},
 
 		setContent : function(h, s) {
-			var t = this, r = t.getRng(), d;
+			var t = this, r = t.getRng(), d = t.win.document;
 
 			s = s || {format : 'html'};
 			s.set = true;
 			h = t.dom.processHTML(h);
 
 			if (r.insertNode) {
-				d = t.win.document;
-
 				// Gecko has a bug where if you insert &nbsp; using InsertHTML it will insert a space instead
 				// So we simply check if the input is HTML or text and then insert text using the insertNode method
 				if (tinymce.isGecko && h.indexOf('<') == -1) {
@@ -2814,10 +2867,13 @@
 					r.insertNode(t.getRng().createContextualFragment(h));
 				}
 			} else {
-				if (r.item)
-					r.item(0).outerHTML = h;
-				else
-					r.pasteHTML(h);
+				if (r.item) {
+					// Delete content and get caret text selection
+					d.execCommand('Delete', false, null);
+					r = t.getRng();
+				}
+
+				r.pasteHTML(h);
 			}
 		},
 
@@ -2952,7 +3008,7 @@
 						return d;
 					}
 
-					p += (n.nodeValue || '').length;
+					p += trimNl(n.nodeValue || '').length;
 				}
 
 				return null;
@@ -4327,7 +4383,7 @@
 			t.addValidChildRules(s.valid_child_elements);
 
 			if (s.invalid_elements)
-				t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(',', '|').toLowerCase()) + ')$');
+				t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');
 
 			if (s.attrib_value_filter)
 				t.attribValueFilter = s.attribValueFilter;
@@ -4925,9 +4981,6 @@
 			this.onShowMenu = new tinymce.util.Dispatcher(this);
 			this.onHideMenu = new tinymce.util.Dispatcher(this);
 			this.classPrefix = 'mceMenu';
-
-			// Fix for odd IE bug: #1903622 (Frames selection)
-			this.fixIE = tinymce.isIE && (DOM.win.top != DOM.win);
 		},
 
 		createMenu : function(s) {
@@ -5017,7 +5070,7 @@
 			t.element.update();
 
 			t.isMenuVisible = 1;
-			t.mouseClickFunc = Event.add(co, t.fixIE ? 'mousedown' : 'click', function(e) {
+			t.mouseClickFunc = Event.add(co, 'click', function(e) {
 				var m;
 
 				e = e.target;
@@ -5030,22 +5083,15 @@
 
 					dm = t;
 
-					// Wait a while to fix IE bug where it looses the selection if the user clicks on a menu
-					// item when the editor is placed within an frame or iframe
-					DOM.win.setTimeout(function() {
-						while (dm) {
-							if (dm.hideMenu)
-								dm.hideMenu();
-
-							dm = dm.settings.parent;
-						}
-					}, 0);
-
-					// Yield on IE to prevent loosing image focus when context menu is used
-					window.setTimeout(function() {
-						if (m.settings.onclick)
-							m.settings.onclick(e);
-					}, 0);
+					while (dm) {
+						if (dm.hideMenu)
+							dm.hideMenu();
+
+						dm = dm.settings.parent;
+					}
+
+					if (m.settings.onclick)
+						m.settings.onclick(e);
 
 					return Event.cancel(e); // Cancel to fix onbeforeunload problem
 				}
@@ -5081,6 +5127,7 @@
 			if (s.keyboard_focus) {
 				Event.add(co, 'keydown', t._keyHandler, t);
 				DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link
+				t._focusIdx = 0;
 			}
 		},
 
@@ -5091,7 +5138,7 @@
 				return;
 
 			Event.remove(co, 'mouseover', t.mouseOverFunc);
-			Event.remove(co, t.fixIE ? 'mousedown' : 'click', t.mouseClickFunc);
+			Event.remove(co, 'click', t.mouseClickFunc);
 			Event.remove(co, 'keydown', t._keyHandler);
 			DOM.hide(co);
 			t.isMenuVisible = 0;
@@ -5169,9 +5216,32 @@
 		// Internal functions
 
 		_keyHandler : function(e) {
-			// Accessibility feature
-			if (e.keyCode == 27)
-				this.hideMenu();
+			var t = this, kc = e.keyCode;
+
+			function focus(d) {
+				var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];
+
+				if (e) {
+					t._focusIdx = i;
+					e.focus();
+				}
+			};
+
+			switch (kc) {
+				case 38:
+					focus(-1); // Select first link
+					return;
+
+				case 40:
+					focus(1);
+					return;
+
+				case 13:
+					return;
+
+				case 27:
+					return this.hideMenu();
+			}
 		},
 
 		_add : function(tb, o) {
@@ -5349,7 +5419,7 @@
 			m = t.menu;
 			m.settings.offset_x = p2.x;
 			m.settings.offset_y = p2.y;
-			m.settings.keyboard_focus = t._focused;
+			m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
 
 			// Select in menu
 			if (t.oldID)
@@ -5366,6 +5436,8 @@
 
 			Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
 			DOM.addClass(t.id, t.classPrefix + 'Selected');
+
+			//DOM.get(t.id + '_text').focus();
 		},
 
 		hideMenu : function(e) {
@@ -5398,8 +5470,12 @@
 
 			m.add({
 				title : t.settings.title,
-				'class' : 'mceMenuItemTitle'
-			}).setDisabled(1);
+				'class' : 'mceMenuItemTitle',
+				onclick : function() {
+					if (t.settings.onselect('') !== false)
+						t.select(''); // Must be runned after
+				}
+			});
 
 			each(t.items, function(o) {
 				o.id = DOM.uniqueId();
@@ -5419,8 +5495,40 @@
 			var t = this, cp = t.classPrefix;
 
 			Event.add(t.id, 'click', t.showMenu, t);
-			Event.add(t.id + '_text', 'focus', function() {t._focused = 1;});
-			Event.add(t.id + '_text', 'blur', function() {t._focused = 0;});
+			Event.add(t.id + '_text', 'focus', function(e) {
+				if (!t._focused) {
+					t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {
+						var idx = -1, v, kc = e.keyCode;
+
+						// Find current index
+						each(t.items, function(v, i) {
+							if (t.selectedValue == v.value)
+								idx = i;
+						});
+
+						// Move up/down
+						if (kc == 38)
+							v = t.items[idx - 1];
+						else if (kc == 40)
+							v = t.items[idx + 1];
+						else if (kc == 13) {
+							// Fake select on enter
+							v = t.selectedValue;
+							t.selectedValue = null; // Needs to be null to fake change
+							t.settings.onselect(v);
+							return Event.cancel(e);
+						}
+
+						if (v) {
+							t.hideMenu();
+							t.select(v.value);
+						}
+					});
+				}
+
+				t._focused = 1;
+			});
+			Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});
 
 			// Old IE doesn't have hover on all elements
 			if (tinymce.isIE6 || !DOM.boxModel) {
@@ -5731,6 +5839,9 @@
 				default_color : '#888888'
 			}, t.settings);
 
+			t.onShowMenu = new tinymce.util.Dispatcher(t);
+			t.onHideMenu = new tinymce.util.Dispatcher(t);
+
 			t.value = s.default_color;
 		},
 
@@ -5770,6 +5881,8 @@
 				DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
 			}
 
+			t.onShowMenu.dispatch(t);
+
 			t.isMenuVisible = 1;
 		},
 
@@ -5787,6 +5900,8 @@
 				DOM.hide(t.id + '_menu');
 			}
 
+			t.onHideMenu.dispatch(t);
+
 			t.isMenuVisible = 0;
 		},
 
@@ -6467,7 +6582,7 @@
 				apply_source_formatting : 1,
 				directionality : 'ltr',
 				forced_root_block : 'p',
-				valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote,-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value|_value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',
+				valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',
 				hidden_input : 1,
 				padd_empty_editor : 1,
 				render_ui : 1,
@@ -6520,7 +6635,7 @@
 
 			if (s.encoding == 'xml') {
 				t.onGetContent.add(function(ed, o) {
-					if (o.get)
+					if (o.save)
 						o.content = DOM.encode(o.content);
 				});
 			}
@@ -6534,7 +6649,7 @@
 				});
 			}
 
-			if (s.add_unload_trigger) {
+			if (s.add_unload_trigger && !s.ask) {
 				t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
 					if (t.initialized && !t.destroyed && !t.isHidden())
 						t.save({format : 'raw', no_events : true});
@@ -6593,12 +6708,15 @@
 				sl.loadQueue(function() {
 					if (s.ask) {
 						function ask() {
-							t.windowManager.confirm(t.getLang('edit_confirm'), function(s) {
-								if (s)
-									t.init();
-								else
-									Event.remove(t.id, 'focus', ask);
-							});
+							// Yield for awhile to avoid focus bug on FF 3 when cancel is pressed
+							window.setTimeout(function() {
+								Event.remove(t.id, 'focus', ask);
+
+								t.windowManager.confirm(t.getLang('edit_confirm'), function(s) {
+									if (s)
+										t.init();
+								});
+							}, 0);
 						};
 
 						Event.add(t.id, 'focus', ask);
@@ -6736,7 +6854,7 @@
 			if (h < 100)
 				h = 100;
 
-			t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '"></base>';
+			t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '" />';
 			t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
 
 			if (tinymce.relaxedDomain)
@@ -7087,10 +7205,14 @@
 
 		
 		focus : function(sf) {
-			var oed, t = this;
+			var oed, t = this, ce = t.settings.content_editable;
 
 			if (!sf) {
-				t.getWin().focus();
+				// Is not content editable or the selection is outside the area in IE
+				// the IE statement is needed to avoid bluring if element selections inside layers since
+				// the layer is like it's own document in IE
+				if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))
+					t.getWin().focus();
 
 							}
 
@@ -7265,7 +7387,7 @@
 			if (o.terminate)
 				return false;
 
-			// Comamnd callback
+			// Command callback
 			if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
 				t.onExecCommand.dispatch(t, cmd, ui, val, a);
 				return true;
@@ -7432,6 +7554,12 @@
 			o = o || {};
 			o.save = true;
 
+			// Add undo level will trigger onchange event
+			if (!o.no_events) {
+				t.undoManager.typing = 0;
+				t.undoManager.add();
+			}
+
 			o.element = e;
 			h = o.content = t.getContent(o);
 
@@ -7474,7 +7602,7 @@
 			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
 			// It will also be impossible to place the caret in the editor unless there is a BR element present
 			if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
-				o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />', 1);
+				o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');
 				o.format = 'raw';
 			}
 
@@ -7508,8 +7636,10 @@
 				h = t.getBody().innerHTML;
 
 			h = h.replace(/^\s*|\s*$/g, '');
-			o = {content : h};
-			t.onGetContent.dispatch(t, o);
+			o.content = h;
+
+			if (!o.no_events)
+				t.onGetContent.dispatch(t, o);
 
 			return o.content;
 		},
@@ -7729,14 +7859,14 @@
 					case 'contextmenu':
 						if (tinymce.isOpera) {
 							// Fake contextmenu on Opera
-							Event.add(t.getDoc(), 'mousedown', function(e) {
+							Event.add(t.getBody(), 'mousedown', function(e) {
 								if (e.ctrlKey) {
 									e.fakeType = 'contextmenu';
 									eventHandler(e);
 								}
 							});
 						} else
-							Event.add(t.getDoc(), k, eventHandler);
+							Event.add(t.getBody(), k, eventHandler);
 						break;
 
 					case 'paste':
@@ -7822,7 +7952,7 @@
 						} catch (ex) {
 							// Use old method
 							if (!t._isHidden())
-								d.execCommand("useCSS", 0, true);
+								try {d.execCommand("useCSS", 0, true);} catch (ex) {}
 						}
 
 						if (!s.table_inline_editing)
@@ -8551,14 +8681,30 @@
 		},
 
 		FontSize : function(u, v) {
-			var ed = this.editor, s = ed.settings, fz = tinymce.explode(s.font_size_style_values), fzc = tinymce.explode(s.font_size_classes);
-
+			var ed = this.editor, s = ed.settings, fz = tinymce.explode(s.font_size_style_values), fzc = tinymce.explode(s.font_size_classes), h, bm;
+
+			// Remove style sizes
+			each(ed.dom.select('font'), function(e) {
+				e.style.fontSize = '';
+			});
+
+			// Let the browser add new size it will remove unneded ones in some browsers
 			ed.getDoc().execCommand('FontSize', false, v);
 
 			// Add style values
 			if (s.inline_styles) {
 				each(ed.dom.select('font'), function(e) {
-					if (e.size === v) {
+					// Try remove redundant font elements
+					if (e.parentNode.nodeName == 'FONT' && e.size == e.parentNode.size) {
+						if (!bm)
+							bm = ed.selection.getBookmark();
+
+						ed.dom.remove(e, 1);
+						return;
+					}
+
+					// Setup font size based on font size value
+					if (v = e.size) {
 						if (fzc && fzc.length > 0)
 							ed.dom.setAttrib(e, 'class', fzc[parseInt(v) - 1]);
 						else
@@ -8566,6 +8712,8 @@
 					}
 				});
 			}
+
+			ed.selection.moveToBookmark(bm);
 		},
 
 		queryCommandValue : function(c) {
@@ -9004,13 +9152,37 @@
 		},
 
 		FormatBlock : function(ui, val) {
-			var t = this, ed = t.editor;
+			var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;
+
+			function isBlock(n) {
+				return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);
+			};
+
+			bl = dom.getParent(s.getNode(), function(n) {
+				return isBlock(n);
+			});
+
+			// IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>
+			// FF and Opera doesn't change parent DIV elements if you switch format
+			if (bl) {
+				if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {
+					// Rename block element
+					nb = ed.dom.create(val);
+
+					each(dom.getAttribs(bl), function(v) {
+						dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));
+					});
+
+					b = s.getBookmark();
+					dom.replace(nb, bl, 1);
+					s.moveToBookmark(b);
+					ed.nodeChanged();
+					return;
+				}
+			}
 
 			val = ed.settings.forced_root_block ? (val || '<p>') : val;
 
-			if (/^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(ed.selection.getNode().nodeName))
-				t.mceRemoveNode();
-
 			if (val.indexOf('<') == -1)
 				val = '<' + val + '>';
 
@@ -9656,10 +9828,13 @@
 							// Store selection
 							if (si == -2 && r) {
 								if (!isIE) {
-									so = r.startOffset;
-									eo = r.endOffset;
-									si = t.find(b, 0, r.startContainer);
-									ei = t.find(b, 0, r.endContainer);
+									// If element is inside body, might not be the case in contentEdiable mode
+									if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {
+										so = r.startOffset;
+										eo = r.endOffset;
+										si = t.find(b, 0, r.startContainer);
+										ei = t.find(b, 0, r.endContainer);
+									}
 								} else {
 									tr = d.body.createTextRange();
 									tr.moveToElementText(b);
@@ -9696,7 +9871,7 @@
 			// Restore selection
 			if (si != -2) {
 				if (!isIE) {
-					bl = d.getElementsByTagName(ed.settings.element)[0];
+					bl = b.getElementsByTagName(ed.settings.element)[0];
 					r = d.createRange();
 
 					// Select last location or generated block
@@ -9918,8 +10093,8 @@
 			if (isEmpty(aft))
 				aft.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
 
-			// Opera needs this one backwards
-			if (isOpera) {
+			// Opera needs this one backwards for older versions
+			if (isOpera && parseFloat(opera.version()) < 9.5) {
 				r.insertNode(bef);
 				r.insertNode(aft);
 			} else {
@@ -10129,12 +10304,7 @@
 			// Fix for bug #1897785, #1898007
 			if (tinymce.isIE) {
 				c.onShowMenu.add(function() {
-					var s = ed.selection, n = s.getNode();
-
-					if (n.nodeName == 'IMG')
-						bm = s.getBookmark();
-					else
-						bm = 0;
+					bm = ed.selection.getBookmark(1);
 				});
 
 				c.onHideMenu.add(function() {
@@ -10283,7 +10453,7 @@
 		},
 
 		createColorSplitButton : function(id, s, cc) {
-			var t = this, ed = t.editor, cmd, c, cls;
+			var t = this, ed = t.editor, cmd, c, cls, bm;
 
 			if (t.get(id))
 				return null;
@@ -10321,6 +10491,20 @@
 				c.destroy();
 			});
 
+			// Fix for bug #1897785, #1898007
+			if (tinymce.isIE) {
+				c.onShowMenu.add(function() {
+					bm = ed.selection.getBookmark(1);
+				});
+
+				c.onHideMenu.add(function() {
+					if (bm) {
+						ed.selection.moveToBookmark(bm);
+						bm = 0;
+					}
+				});
+			}
+
 			return t.add(c);
 		},
 
@@ -10400,8 +10584,7 @@
 					s.dialogWidth = s.width + 'px';
 					s.dialogHeight = s.height + 'px';
 					s.scroll = s.scrollbars || false;
-				} else
-					s.modal = s.alwaysRaised = s.dialog = s.centerscreen = s.dependent = true;
+				}
 			}
 
 			// Build features string