tinymce-rails 3.5 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. data/lib/tasks/{assets.rake → tinymce-assets.rake} +0 -0
  2. data/lib/tinymce/rails/configuration.rb +8 -0
  3. data/lib/tinymce/rails/engine.rb +3 -1
  4. data/lib/tinymce/rails/version.rb +2 -2
  5. data/vendor/assets/javascripts/tinymce/plugins/autolink/editor_plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/autolink/editor_plugin_src.js +10 -3
  7. data/vendor/assets/javascripts/tinymce/plugins/legacyoutput/editor_plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/legacyoutput/editor_plugin_src.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/searchreplace.htm +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/table/editor_plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/table/editor_plugin_src.js +20 -2
  12. data/vendor/assets/javascripts/tinymce/plugins/table/js/table.js +9 -0
  13. data/vendor/assets/javascripts/tinymce/themes/advanced/editor_template.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/themes/advanced/editor_template_src.js +18 -12
  15. data/vendor/assets/javascripts/tinymce/themes/advanced/js/anchor.js +18 -6
  16. data/vendor/assets/javascripts/tinymce/themes/advanced/js/link.js +9 -3
  17. data/vendor/assets/javascripts/tinymce/tiny_mce.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/tiny_mce_jquery.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/tiny_mce_jquery_src.js +414 -126
  20. data/vendor/assets/javascripts/tinymce/tiny_mce_popup.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/tiny_mce_src.js +414 -126
  22. metadata +5 -4
@@ -2,4 +2,4 @@
2
2
  // Uncomment and change this document.domain value if you are loading the script cross subdomains
3
3
  // document.domain = 'moxiecode.com';
4
4
 
5
- var tinymce=null,tinyMCEPopup,tinyMCE;tinyMCEPopup={init:function(){var b=this,a,c;a=b.getWin();tinymce=a.tinymce;tinyMCE=a.tinyMCE;b.editor=tinymce.EditorManager.activeEditor;b.params=b.editor.windowManager.params;b.features=b.editor.windowManager.features;b.dom=b.editor.windowManager.createInstance("tinymce.dom.DOMUtils",document,{ownEvents:true,proxy:tinyMCEPopup._eventProxy});b.dom.bind(window,"ready",b._onDOMLoaded,b);if(b.features.popup_css!==false){b.dom.loadCSS(b.features.popup_css||b.editor.settings.popup_css)}b.listeners=[];b.onInit={add:function(e,d){b.listeners.push({func:e,scope:d})}};b.isWindow=!b.getWindowArg("mce_inline");b.id=b.getWindowArg("mce_window_id");b.editor.windowManager.onOpen.dispatch(b.editor.windowManager,window)},getWin:function(){return(!window.frameElement&&window.dialogArguments)||opener||parent||top},getWindowArg:function(c,b){var a=this.params[c];return tinymce.is(a)?a:b},getParam:function(b,a){return this.editor.getParam(b,a)},getLang:function(b,a){return this.editor.getLang(b,a)},execCommand:function(d,c,e,b){b=b||{};b.skip_focus=1;this.restoreSelection();return this.editor.execCommand(d,c,e,b)},resizeToInnerSize:function(){var a=this;setTimeout(function(){var b=a.dom.getViewPort(window);a.editor.windowManager.resizeBy(a.getWindowArg("mce_width")-b.w,a.getWindowArg("mce_height")-b.h,a.id||window)},10)},executeOnLoad:function(s){this.onInit.add(function(){eval(s)})},storeSelection:function(){this.editor.windowManager.bookmark=tinyMCEPopup.editor.selection.getBookmark(1)},restoreSelection:function(){var a=tinyMCEPopup;if(!a.isWindow&&tinymce.isIE){a.editor.selection.moveToBookmark(a.editor.windowManager.bookmark)}},requireLangPack:function(){var b=this,a=b.getWindowArg("plugin_url")||b.getWindowArg("theme_url");if(a&&b.editor.settings.language&&b.features.translate_i18n!==false&&b.editor.settings.language_load!==false){a+="/langs/"+b.editor.settings.language+"_dlg.js";if(!tinymce.ScriptLoader.isDone(a)){document.write('<script type="text/javascript" src="'+tinymce._addVer(a)+'"><\/script>');tinymce.ScriptLoader.markDone(a)}}},pickColor:function(b,a){this.execCommand("mceColorPicker",true,{color:document.getElementById(a).value,func:function(e){document.getElementById(a).value=e;try{document.getElementById(a).onchange()}catch(d){}}})},openBrowser:function(a,c,b){tinyMCEPopup.restoreSelection();this.editor.execCallback("file_browser_callback",a,document.getElementById(a).value,c,window)},confirm:function(b,a,c){this.editor.windowManager.confirm(b,a,c,window)},alert:function(b,a,c){this.editor.windowManager.alert(b,a,c,window)},close:function(){var a=this;function b(){a.editor.windowManager.close(window);tinymce=tinyMCE=a.editor=a.params=a.dom=a.dom.doc=null}if(tinymce.isOpera){a.getWin().setTimeout(b,0)}else{b()}},_restoreSelection:function(){var a=window.event.srcElement;if(a.nodeName=="INPUT"&&(a.type=="submit"||a.type=="button")){tinyMCEPopup.restoreSelection()}},_onDOMLoaded:function(){var b=tinyMCEPopup,d=document.title,e,c,a;if(b.features.translate_i18n!==false){c=document.body.innerHTML;if(tinymce.isIE){c=c.replace(/ (value|title|alt)=([^"][^\s>]+)/gi,' $1="$2"')}document.dir=b.editor.getParam("directionality","");if((a=b.editor.translate(c))&&a!=c){document.body.innerHTML=a}if((a=b.editor.translate(d))&&a!=d){document.title=d=a}}if(!b.editor.getParam("browser_preferred_colors",false)||!b.isWindow){b.dom.addClass(document.body,"forceColors")}document.body.style.display="";if(tinymce.isIE){document.attachEvent("onmouseup",tinyMCEPopup._restoreSelection);b.dom.add(b.dom.select("head")[0],"base",{target:"_self"})}b.restoreSelection();b.resizeToInnerSize();if(!b.isWindow){b.editor.windowManager.setTitle(window,d)}else{window.focus()}if(!tinymce.isIE&&!b.isWindow){b.dom.bind(document,"focus",function(){b.editor.windowManager.focus(b.id)})}tinymce.each(b.dom.select("select"),function(f){f.onkeydown=tinyMCEPopup._accessHandler});tinymce.each(b.listeners,function(f){f.func.call(f.scope,b.editor)});if(b.getWindowArg("mce_auto_focus",true)){window.focus();tinymce.each(document.forms,function(g){tinymce.each(g.elements,function(f){if(b.dom.hasClass(f,"mceFocus")&&!f.disabled){f.focus();return false}})})}document.onkeyup=tinyMCEPopup._closeWinKeyHandler},_accessHandler:function(a){a=a||window.event;if(a.keyCode==13||a.keyCode==32){a=a.target||a.srcElement;if(a.onchange){a.onchange()}return tinymce.dom.Event.cancel(a)}},_closeWinKeyHandler:function(a){a=a||window.event;if(a.keyCode==27){tinyMCEPopup.close()}},_eventProxy:function(a){return function(b){tinyMCEPopup.dom.events.callNativeHandler(a,b)}}};tinyMCEPopup.init();
5
+ var tinymce=null,tinyMCEPopup,tinyMCE;tinyMCEPopup={init:function(){var b=this,a,c;a=b.getWin();tinymce=a.tinymce;tinyMCE=a.tinyMCE;b.editor=tinymce.EditorManager.activeEditor;b.params=b.editor.windowManager.params;b.features=b.editor.windowManager.features;b.dom=b.editor.windowManager.createInstance("tinymce.dom.DOMUtils",document,{ownEvents:true,proxy:tinyMCEPopup._eventProxy});b.dom.bind(window,"ready",b._onDOMLoaded,b);if(b.features.popup_css!==false){b.dom.loadCSS(b.features.popup_css||b.editor.settings.popup_css)}b.listeners=[];b.onInit={add:function(e,d){b.listeners.push({func:e,scope:d})}};b.isWindow=!b.getWindowArg("mce_inline");b.id=b.getWindowArg("mce_window_id");b.editor.windowManager.onOpen.dispatch(b.editor.windowManager,window)},getWin:function(){return(!window.frameElement&&window.dialogArguments)||opener||parent||top},getWindowArg:function(c,b){var a=this.params[c];return tinymce.is(a)?a:b},getParam:function(b,a){return this.editor.getParam(b,a)},getLang:function(b,a){return this.editor.getLang(b,a)},execCommand:function(d,c,e,b){b=b||{};b.skip_focus=1;this.restoreSelection();return this.editor.execCommand(d,c,e,b)},resizeToInnerSize:function(){var a=this;setTimeout(function(){var b=a.dom.getViewPort(window);a.editor.windowManager.resizeBy(a.getWindowArg("mce_width")-b.w,a.getWindowArg("mce_height")-b.h,a.id||window)},10)},executeOnLoad:function(s){this.onInit.add(function(){eval(s)})},storeSelection:function(){this.editor.windowManager.bookmark=tinyMCEPopup.editor.selection.getBookmark(1)},restoreSelection:function(){var a=tinyMCEPopup;if(!a.isWindow&&tinymce.isIE){a.editor.selection.moveToBookmark(a.editor.windowManager.bookmark)}},requireLangPack:function(){var b=this,a=b.getWindowArg("plugin_url")||b.getWindowArg("theme_url");if(a&&b.editor.settings.language&&b.features.translate_i18n!==false&&b.editor.settings.language_load!==false){a+="/langs/"+b.editor.settings.language+"_dlg.js";if(!tinymce.ScriptLoader.isDone(a)){document.write('<script type="text/javascript" src="'+tinymce._addVer(a)+'"><\/script>');tinymce.ScriptLoader.markDone(a)}}},pickColor:function(b,a){this.execCommand("mceColorPicker",true,{color:document.getElementById(a).value,func:function(e){document.getElementById(a).value=e;try{document.getElementById(a).onchange()}catch(d){}}})},openBrowser:function(a,c,b){tinyMCEPopup.restoreSelection();this.editor.execCallback("file_browser_callback",a,document.getElementById(a).value,c,window)},confirm:function(b,a,c){this.editor.windowManager.confirm(b,a,c,window)},alert:function(b,a,c){this.editor.windowManager.alert(b,a,c,window)},close:function(){var a=this;function b(){a.editor.windowManager.close(window);tinymce=tinyMCE=a.editor=a.params=a.dom=a.dom.doc=null}if(tinymce.isOpera){a.getWin().setTimeout(b,0)}else{b()}},_restoreSelection:function(){var a=window.event.srcElement;if(a.nodeName=="INPUT"&&(a.type=="submit"||a.type=="button")){tinyMCEPopup.restoreSelection()}},_onDOMLoaded:function(){var b=tinyMCEPopup,d=document.title,e,c,a;if(b.features.translate_i18n!==false){c=document.body.innerHTML;if(tinymce.isIE){c=c.replace(/ (value|title|alt)=([^"][^\s>]+)/gi,' $1="$2"')}document.dir=b.editor.getParam("directionality","");if((a=b.editor.translate(c))&&a!=c){document.body.innerHTML=a}if((a=b.editor.translate(d))&&a!=d){document.title=d=a}}if(!b.editor.getParam("browser_preferred_colors",false)||!b.isWindow){b.dom.addClass(document.body,"forceColors")}document.body.style.display="";if(tinymce.isIE){document.attachEvent("onmouseup",tinyMCEPopup._restoreSelection);b.dom.add(b.dom.select("head")[0],"base",{target:"_self"})}b.restoreSelection();b.resizeToInnerSize();if(!b.isWindow){b.editor.windowManager.setTitle(window,d)}else{window.focus()}if(!tinymce.isIE&&!b.isWindow){b.dom.bind(document,"focus",function(){b.editor.windowManager.focus(b.id)})}tinymce.each(b.dom.select("select"),function(f){f.onkeydown=tinyMCEPopup._accessHandler});tinymce.each(b.listeners,function(f){f.func.call(f.scope,b.editor)});if(b.getWindowArg("mce_auto_focus",true)){window.focus();tinymce.each(document.forms,function(g){tinymce.each(g.elements,function(f){if(b.dom.hasClass(f,"mceFocus")&&!f.disabled){f.focus();return false}})})}document.onkeyup=tinyMCEPopup._closeWinKeyHandler},_accessHandler:function(a){a=a||window.event;if(a.keyCode==13||a.keyCode==32){var b=a.target||a.srcElement;if(b.onchange){b.onchange()}return tinymce.dom.Event.cancel(a)}},_closeWinKeyHandler:function(a){a=a||window.event;if(a.keyCode==27){tinyMCEPopup.close()}},_eventProxy:function(a){return function(b){tinyMCEPopup.dom.events.callNativeHandler(a,b)}}};tinyMCEPopup.init();
@@ -6,9 +6,9 @@
6
6
  var tinymce = {
7
7
  majorVersion : '3',
8
8
 
9
- minorVersion : '5',
9
+ minorVersion : '5.2',
10
10
 
11
- releaseDate : '2012-05-03',
11
+ releaseDate : '2012-05-31',
12
12
 
13
13
  _init : function() {
14
14
  var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -880,12 +880,12 @@ tinymce.create('tinymce.util.Dispatcher', {
880
880
  ((s) ? "; secure" : "");
881
881
  },
882
882
 
883
- remove : function(n, p) {
884
- var d = new Date();
883
+ remove : function(name, path, domain) {
884
+ var date = new Date();
885
885
 
886
- d.setTime(d.getTime() - 1000);
886
+ date.setTime(date.getTime() - 1000);
887
887
 
888
- this.set(n, '', d, p, d);
888
+ this.set(name, '', date, path, domain);
889
889
  }
890
890
  });
891
891
  })();
@@ -1588,6 +1588,14 @@ tinymce.util.Quirks = function(editor) {
1588
1588
  editor.onSetContent.add(selection.onSetContent.add(fixLinks));
1589
1589
  };
1590
1590
 
1591
+ function setDefaultBlockType() {
1592
+ if (settings.forced_root_block) {
1593
+ editor.onInit.add(function() {
1594
+ setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
1595
+ });
1596
+ }
1597
+ }
1598
+
1591
1599
  function removeGhostSelection() {
1592
1600
  function repaint(sender, args) {
1593
1601
  if (!sender || !args.initial) {
@@ -1622,6 +1630,7 @@ tinymce.util.Quirks = function(editor) {
1622
1630
  cleanupStylesWhenDeleting();
1623
1631
  inputMethodFocus();
1624
1632
  selectControlElements();
1633
+ setDefaultBlockType();
1625
1634
 
1626
1635
  // iOS
1627
1636
  if (tinymce.isIDevice) {
@@ -2099,8 +2108,11 @@ tinymce.html.Styles = function(settings, schema) {
2099
2108
  if (!html5) {
2100
2109
  html5 = mapCache.html5 = unpack({
2101
2110
  A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title',
2102
- 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|meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video',
2103
- 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|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|p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
2111
+ 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|' +
2112
+ 'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video',
2113
+ 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|' +
2114
+ '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|' +
2115
+ 'p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
2104
2116
  }, 'html[A|manifest][body|head]' +
2105
2117
  'head[A][base|command|link|meta|noscript|script|style|title]' +
2106
2118
  'title[A][#]' +
@@ -2136,7 +2148,7 @@ tinymce.html.Styles = function(settings, schema) {
2136
2148
  'dl[A][dd|dt]' +
2137
2149
  'dt[A][B]' +
2138
2150
  'dd[A][C]' +
2139
- 'a[A|href|target|ping|rel|media|type][C]' +
2151
+ 'a[A|href|target|ping|rel|media|type][B]' +
2140
2152
  'em[A][B]' +
2141
2153
  'strong[A][B]' +
2142
2154
  'small[A][B]' +
@@ -2182,7 +2194,8 @@ tinymce.html.Styles = function(settings, schema) {
2182
2194
  'form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]' +
2183
2195
  'fieldset[A|disabled|form|name][C|legend]' +
2184
2196
  'label[A|form|for][B]' +
2185
- 'input[A|type|accept|alt|autocomplete|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value][]' +
2197
+ 'input[A|type|accept|alt|autocomplete|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|' +
2198
+ 'multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]' +
2186
2199
  'button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]' +
2187
2200
  'select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]' +
2188
2201
  'datalist[A][B|option]' +
@@ -2196,7 +2209,7 @@ tinymce.html.Styles = function(settings, schema) {
2196
2209
  'area[A|shape|coords|href|alt|target|media|rel|ping|type][]' +
2197
2210
  'mathml[A][]' +
2198
2211
  'svg[A][]' +
2199
- 'table[A|summary][caption|colgroup|thead|tfoot|tbody|tr]' +
2212
+ 'table[A|border][caption|colgroup|thead|tfoot|tbody|tr]' +
2200
2213
  'caption[A][C]' +
2201
2214
  'colgroup[A|span][col]' +
2202
2215
  'col[A|span][]' +
@@ -2385,13 +2398,13 @@ tinymce.html.Styles = function(settings, schema) {
2385
2398
 
2386
2399
  // Setup map objects
2387
2400
  whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script style textarea');
2388
- selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li options p td tfoot th thead tr');
2401
+ selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
2389
2402
  shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source');
2390
2403
  boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls');
2391
2404
  nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object', shortEndedElementsMap);
2392
2405
  blockElementsMap = createLookupTable('block_elements', 'h1 h2 h3 h4 h5 h6 hr p div address pre form table tbody thead tfoot ' +
2393
2406
  'th tr td li ol ul caption blockquote center dl dt dd dir fieldset ' +
2394
- 'noscript menu isindex samp header footer article section hgroup aside nav figure');
2407
+ 'noscript menu isindex samp header footer article section hgroup aside nav figure option datalist select optgroup');
2395
2408
 
2396
2409
  // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
2397
2410
  function patternToRegExp(str) {
@@ -2713,6 +2726,36 @@ tinymce.html.Styles = function(settings, schema) {
2713
2726
  return !!(parent && parent[child]);
2714
2727
  };
2715
2728
 
2729
+ self.isValid = function(name, attr) {
2730
+ var attrPatterns, i, rule = getElementRule(name);
2731
+
2732
+ // Check if it's a valid element
2733
+ if (rule) {
2734
+ if (attr) {
2735
+ // Check if attribute name exists
2736
+ if (rule.attributes[attr]) {
2737
+ return true;
2738
+ }
2739
+
2740
+ // Check if attribute matches a regexp pattern
2741
+ attrPatterns = rule.attributePatterns;
2742
+ if (attrPatterns) {
2743
+ i = attrPatterns.length;
2744
+ while (i--) {
2745
+ if (attrPatterns[i].pattern.test(name)) {
2746
+ return true;
2747
+ }
2748
+ }
2749
+ }
2750
+ } else {
2751
+ return true;
2752
+ }
2753
+ }
2754
+
2755
+ // No match
2756
+ return false;
2757
+ };
2758
+
2716
2759
  self.getElementRule = getElementRule;
2717
2760
 
2718
2761
  self.getCustomElements = function() {
@@ -2836,7 +2879,7 @@ tinymce.html.Styles = function(settings, schema) {
2836
2879
 
2837
2880
  // Setup lookup tables for empty elements and boolean attributes
2838
2881
  shortEndedElements = schema.getShortEndedElements();
2839
- selfClosing = schema.getSelfClosingElements();
2882
+ selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
2840
2883
  fillAttrsMap = schema.getBoolAttrs();
2841
2884
  validate = settings.validate;
2842
2885
  removeInternalElements = settings.remove_internals;
@@ -3584,9 +3627,23 @@ tinymce.html.Styles = function(settings, schema) {
3584
3627
  }
3585
3628
  };
3586
3629
 
3630
+ function cloneAndExcludeBlocks(input) {
3631
+ var name, output = {};
3632
+
3633
+ for (name in input) {
3634
+ if (name !== 'li' && name != 'p') {
3635
+ output[name] = input[name];
3636
+ }
3637
+ }
3638
+
3639
+ return output;
3640
+ };
3641
+
3587
3642
  parser = new tinymce.html.SaxParser({
3588
3643
  validate : validate,
3589
- fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results
3644
+
3645
+ // Exclude P and LI from DOM parsing since it's treated better by the DOM parser
3646
+ self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
3590
3647
 
3591
3648
  cdata: function(text) {
3592
3649
  node.append(createNode('#cdata', 4)).value = text;
@@ -3764,7 +3821,7 @@ tinymce.html.Styles = function(settings, schema) {
3764
3821
  node.empty().append(new Node('#text', '3')).value = '\u00a0';
3765
3822
  else {
3766
3823
  // Leave nodes that have a name like <a name="name">
3767
- if (!node.attributes.map.name) {
3824
+ if (!node.attributes.map.name && !node.attributes.map.id) {
3768
3825
  tempNode = node.parent;
3769
3826
  node.empty().remove();
3770
3827
  node = tempNode;
@@ -3916,12 +3973,12 @@ tinymce.html.Styles = function(settings, schema) {
3916
3973
 
3917
3974
  // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
3918
3975
  if (!settings.allow_html_in_named_anchor) {
3919
- self.addAttributeFilter('name', function(nodes, name) {
3976
+ self.addAttributeFilter('id,name', function(nodes, name) {
3920
3977
  var i = nodes.length, sibling, prevSibling, parent, node;
3921
3978
 
3922
3979
  while (i--) {
3923
3980
  node = nodes[i];
3924
- if (node.name === 'a' && node.firstChild) {
3981
+ if (node.name === 'a' && node.firstChild && !node.attr('href')) {
3925
3982
  parent = node.parent;
3926
3983
 
3927
3984
  // Move children after current node
@@ -4660,12 +4717,20 @@ tinymce.dom = {};
4660
4717
  };
4661
4718
 
4662
4719
  self.prevent = function(e) {
4720
+ if (!e.preventDefault) {
4721
+ e = fix(e);
4722
+ }
4723
+
4663
4724
  e.preventDefault();
4664
4725
 
4665
4726
  return false;
4666
4727
  };
4667
4728
 
4668
4729
  self.stop = function(e) {
4730
+ if (!e.stopPropagation) {
4731
+ e = fix(e);
4732
+ }
4733
+
4669
4734
  e.stopPropagation();
4670
4735
 
4671
4736
  return false;
@@ -6158,7 +6223,8 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
6158
6223
  cloneContents : cloneContents,
6159
6224
  insertNode : insertNode,
6160
6225
  surroundContents : surroundContents,
6161
- cloneRange : cloneRange
6226
+ cloneRange : cloneRange,
6227
+ toStringIE : toStringIE
6162
6228
  });
6163
6229
 
6164
6230
  function createDocumentFragment() {
@@ -6798,9 +6864,20 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
6798
6864
 
6799
6865
  n.parentNode.removeChild(n);
6800
6866
  };
6867
+
6868
+ function toStringIE() {
6869
+ return dom.create('body', null, cloneContents()).outerText;
6870
+ }
6871
+
6872
+ return t;
6801
6873
  };
6802
6874
 
6803
6875
  ns.Range = Range;
6876
+
6877
+ // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
6878
+ Range.prototype.toString = function() {
6879
+ return this.toStringIE();
6880
+ };
6804
6881
  })(tinymce.dom);
6805
6882
 
6806
6883
  (function() {
@@ -7156,7 +7233,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
7156
7233
  };
7157
7234
 
7158
7235
  this.addRange = function(rng) {
7159
- var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;
7236
+ var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling, doc = selection.dom.doc, body = doc.body;
7160
7237
 
7161
7238
  function setEndPoint(start) {
7162
7239
  var container, offset, marker, tmpRng, nodes;
@@ -7214,11 +7291,25 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
7214
7291
  // Trick to place the caret inside an empty block element like <p></p>
7215
7292
  if (startOffset == endOffset && !startContainer.hasChildNodes()) {
7216
7293
  if (startContainer.canHaveHTML) {
7294
+ // Check if previous sibling is an empty block if it is then we need to render it
7295
+ // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
7296
+ // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
7297
+ sibling = startContainer.previousSibling;
7298
+ if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
7299
+ sibling.innerHTML = '\uFEFF';
7300
+ } else {
7301
+ sibling = null;
7302
+ }
7303
+
7217
7304
  startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';
7218
7305
  ieRng.moveToElementText(startContainer.lastChild);
7219
7306
  ieRng.select();
7220
7307
  dom.doc.selection.clear();
7221
7308
  startContainer.innerHTML = '';
7309
+
7310
+ if (sibling) {
7311
+ sibling.innerHTML = '';
7312
+ }
7222
7313
  return;
7223
7314
  } else {
7224
7315
  startOffset = dom.nodeIndex(startContainer);
@@ -8816,12 +8907,13 @@ window.tinymce.dom.Sizzle = Sizzle;
8816
8907
  var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each, TreeWalker = tinymce.dom.TreeWalker;
8817
8908
 
8818
8909
  tinymce.create('tinymce.dom.Selection', {
8819
- Selection : function(dom, win, serializer) {
8910
+ Selection : function(dom, win, serializer, editor) {
8820
8911
  var t = this;
8821
8912
 
8822
8913
  t.dom = dom;
8823
8914
  t.win = win;
8824
8915
  t.serializer = serializer;
8916
+ t.editor = editor;
8825
8917
 
8826
8918
  // Add events
8827
8919
  each([
@@ -9784,14 +9876,66 @@ window.tinymce.dom.Sizzle = Sizzle;
9784
9876
  }
9785
9877
  },
9786
9878
 
9787
- destroy : function(s) {
9788
- var t = this;
9879
+ selectorChanged: function(selector, callback) {
9880
+ var self = this, currentSelectors;
9789
9881
 
9790
- t.win = null;
9882
+ if (!self.selectorChangedData) {
9883
+ self.selectorChangedData = {};
9884
+ currentSelectors = {};
9885
+
9886
+ self.editor.onNodeChange.addToTop(function(ed, cm, node) {
9887
+ var dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
9888
+
9889
+ // Check for new matching selectors
9890
+ each(self.selectorChangedData, function(callbacks, selector) {
9891
+ each(parents, function(node) {
9892
+ if (dom.is(node, selector)) {
9893
+ if (!currentSelectors[selector]) {
9894
+ // Execute callbacks
9895
+ each(callbacks, function(callback) {
9896
+ callback(true, {node: node, selector: selector, parents: parents});
9897
+ });
9898
+
9899
+ currentSelectors[selector] = callbacks;
9900
+ }
9901
+
9902
+ matchedSelectors[selector] = callbacks;
9903
+ return false;
9904
+ }
9905
+ });
9906
+ });
9907
+
9908
+ // Check if current selectors still match
9909
+ each(currentSelectors, function(callbacks, selector) {
9910
+ if (!matchedSelectors[selector]) {
9911
+ delete currentSelectors[selector];
9912
+
9913
+ each(callbacks, function(callback) {
9914
+ callback(false, {node: node, selector: selector, parents: parents});
9915
+ });
9916
+ }
9917
+ });
9918
+ });
9919
+ }
9920
+
9921
+ // Add selector listeners
9922
+ if (!self.selectorChangedData[selector]) {
9923
+ self.selectorChangedData[selector] = [];
9924
+ }
9925
+
9926
+ self.selectorChangedData[selector].push(callback);
9927
+
9928
+ return self;
9929
+ },
9930
+
9931
+ destroy : function(manual) {
9932
+ var self = this;
9933
+
9934
+ self.win = null;
9791
9935
 
9792
9936
  // Manual destroy then remove unload handler
9793
- if (!s)
9794
- tinymce.removeUnload(t.destroy);
9937
+ if (!manual)
9938
+ tinymce.removeUnload(self.destroy);
9795
9939
  },
9796
9940
 
9797
9941
  // IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode
@@ -10207,11 +10351,10 @@ window.tinymce.dom.Sizzle = Sizzle;
10207
10351
  }
10208
10352
 
10209
10353
  // Create new script element
10210
- elm = dom.create('script', {
10211
- id : id,
10212
- type : 'text/javascript',
10213
- src : tinymce._addVer(url)
10214
- });
10354
+ elm = document.createElement('script');
10355
+ elm.id = id;
10356
+ elm.type = 'text/javascript';
10357
+ elm.src = tinymce._addVer(url);
10215
10358
 
10216
10359
  // Add onload listener for non IE browsers since IE9
10217
10360
  // fires onload event before the script is parsed and executed
@@ -10575,12 +10718,15 @@ window.tinymce.dom.Sizzle = Sizzle;
10575
10718
 
10576
10719
  t.destroy = function() {
10577
10720
  each(items, function(item) {
10578
- dom.unbind(dom.get(item.id), 'focus', itemFocussed);
10579
- dom.unbind(dom.get(item.id), 'blur', itemBlurred);
10721
+ var elm = dom.get(item.id);
10722
+
10723
+ dom.unbind(elm, 'focus', itemFocussed);
10724
+ dom.unbind(elm, 'blur', itemBlurred);
10580
10725
  });
10581
10726
 
10582
- dom.unbind(dom.get(root), 'focus', rootFocussed);
10583
- dom.unbind(dom.get(root), 'keydown', rootKeydown);
10727
+ var rootElm = dom.get(root);
10728
+ dom.unbind(rootElm, 'focus', rootFocussed);
10729
+ dom.unbind(rootElm, 'keydown', rootKeydown);
10584
10730
 
10585
10731
  items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;
10586
10732
  t.destroy = function() {};
@@ -10659,21 +10805,23 @@ window.tinymce.dom.Sizzle = Sizzle;
10659
10805
 
10660
10806
  // Set up state and listeners for each item.
10661
10807
  each(items, function(item, idx) {
10662
- var tabindex;
10808
+ var tabindex, elm;
10663
10809
 
10664
10810
  if (!item.id) {
10665
10811
  item.id = dom.uniqueId('_mce_item_');
10666
10812
  }
10667
10813
 
10814
+ elm = dom.get(item.id);
10815
+
10668
10816
  if (excludeFromTabOrder) {
10669
- dom.bind(item.id, 'blur', itemBlurred);
10817
+ dom.bind(elm, 'blur', itemBlurred);
10670
10818
  tabindex = '-1';
10671
10819
  } else {
10672
10820
  tabindex = (idx === 0 ? '0' : '-1');
10673
10821
  }
10674
10822
 
10675
- dom.setAttrib(item.id, 'tabindex', tabindex);
10676
- dom.bind(dom.get(item.id), 'focus', itemFocussed);
10823
+ elm.setAttribute('tabindex', tabindex);
10824
+ dom.bind(elm, 'focus', itemFocussed);
10677
10825
  });
10678
10826
 
10679
10827
  // Setup initial state for root element.
@@ -10682,10 +10830,11 @@ window.tinymce.dom.Sizzle = Sizzle;
10682
10830
  }
10683
10831
 
10684
10832
  dom.setAttrib(root, 'tabindex', '-1');
10685
-
10833
+
10686
10834
  // Setup listeners for root element.
10687
- dom.bind(dom.get(root), 'focus', rootFocussed);
10688
- dom.bind(dom.get(root), 'keydown', rootKeydown);
10835
+ var rootElm = dom.get(root);
10836
+ dom.bind(rootElm, 'focus', rootFocussed);
10837
+ dom.bind(rootElm, 'keydown', rootKeydown);
10689
10838
  }
10690
10839
  });
10691
10840
  })(tinymce);
@@ -12483,11 +12632,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12483
12632
  return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
12484
12633
  };
12485
12634
 
12486
- s = extend({
12487
- theme : "simple",
12488
- language : "en"
12489
- }, s);
12490
-
12491
12635
  t.settings = s;
12492
12636
 
12493
12637
  // Legacy call
@@ -12767,7 +12911,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12767
12911
  self.settings = settings = extend({
12768
12912
  id : id,
12769
12913
  language : 'en',
12770
- theme : 'simple',
12914
+ theme : 'advanced',
12771
12915
  skin : 'default',
12772
12916
  delta_width : 0,
12773
12917
  delta_height : 0,
@@ -12798,8 +12942,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12798
12942
  inline_styles : TRUE,
12799
12943
  convert_fonts_to_spans : TRUE,
12800
12944
  indent : 'simple',
12801
- 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',
12802
- 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',
12945
+ 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',
12946
+ 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',
12803
12947
  validate : TRUE,
12804
12948
  entity_encoding : 'named',
12805
12949
  url_converter : self.convertURL,
@@ -12859,6 +13003,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12859
13003
  if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
12860
13004
  DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
12861
13005
 
13006
+ // Hide target element early to prevent content flashing
13007
+ if (!s.content_editable) {
13008
+ t.orgVisibility = t.getElement().style.visibility;
13009
+ t.getElement().style.visibility = 'hidden';
13010
+ }
13011
+
12862
13012
  if (tinymce.WindowManager)
12863
13013
  t.windowManager = new tinymce.WindowManager(t);
12864
13014
 
@@ -12920,7 +13070,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12920
13070
  if (s.language && s.language_load !== false)
12921
13071
  sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
12922
13072
 
12923
- if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
13073
+ if (s.theme && typeof s.theme != "function" && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
12924
13074
  ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
12925
13075
 
12926
13076
  each(explode(s.plugins), function(p) {
@@ -12954,20 +13104,25 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12954
13104
  },
12955
13105
 
12956
13106
  init : function() {
12957
- var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
13107
+ var n, t = this, s = t.settings, w, h, mh, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
12958
13108
 
12959
13109
  tinymce.add(t);
12960
13110
 
12961
13111
  s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));
12962
13112
 
12963
13113
  if (s.theme) {
12964
- s.theme = s.theme.replace(/-/, '');
12965
- o = ThemeManager.get(s.theme);
12966
- t.theme = new o();
13114
+ if (typeof s.theme != "function") {
13115
+ s.theme = s.theme.replace(/-/, '');
13116
+ o = ThemeManager.get(s.theme);
13117
+ t.theme = new o();
12967
13118
 
12968
- if (t.theme.init)
12969
- t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
13119
+ if (t.theme.init)
13120
+ t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
13121
+ } else {
13122
+ t.theme = s.theme;
13123
+ }
12970
13124
  }
13125
+
12971
13126
  function initPlugin(p) {
12972
13127
  var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
12973
13128
  if (c && tinymce.inArray(initializedPlugins,p) === -1) {
@@ -13012,25 +13167,63 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13012
13167
 
13013
13168
  // Measure box
13014
13169
  if (s.render_ui && t.theme) {
13015
- w = s.width || e.style.width || e.offsetWidth;
13016
- h = s.height || e.style.height || e.offsetHeight;
13017
13170
  t.orgDisplay = e.style.display;
13018
- re = /^[0-9\.]+(|px)$/i;
13019
13171
 
13020
- if (re.test('' + w))
13021
- w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
13172
+ if (typeof s.theme != "function") {
13173
+ w = s.width || e.style.width || e.offsetWidth;
13174
+ h = s.height || e.style.height || e.offsetHeight;
13175
+ mh = s.min_height || 100;
13176
+ re = /^[0-9\.]+(|px)$/i;
13177
+
13178
+ if (re.test('' + w))
13179
+ w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
13180
+
13181
+ if (re.test('' + h))
13182
+ h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), mh);
13183
+
13184
+ // Render UI
13185
+ o = t.theme.renderUI({
13186
+ targetNode : e,
13187
+ width : w,
13188
+ height : h,
13189
+ deltaWidth : s.delta_width,
13190
+ deltaHeight : s.delta_height
13191
+ });
13022
13192
 
13023
- if (re.test('' + h))
13024
- h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), 100);
13193
+ // Resize editor
13194
+ DOM.setStyles(o.sizeContainer || o.editorContainer, {
13195
+ width : w,
13196
+ height : h
13197
+ });
13025
13198
 
13026
- // Render UI
13027
- o = t.theme.renderUI({
13028
- targetNode : e,
13029
- width : w,
13030
- height : h,
13031
- deltaWidth : s.delta_width,
13032
- deltaHeight : s.delta_height
13033
- });
13199
+ h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
13200
+ if (h < mh)
13201
+ h = mh;
13202
+ } else {
13203
+ o = s.theme(t, e);
13204
+
13205
+ // Convert element type to id:s
13206
+ if (o.editorContainer.nodeType) {
13207
+ o.editorContainer = o.editorContainer.id = o.editorContainer.id || t.id + "_parent";
13208
+ }
13209
+
13210
+ // Convert element type to id:s
13211
+ if (o.iframeContainer.nodeType) {
13212
+ o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || t.id + "_iframecontainer";
13213
+ }
13214
+
13215
+ // Use specified iframe height or the targets offsetHeight
13216
+ h = o.iframeHeight || e.offsetHeight;
13217
+
13218
+ // Store away the selection when it's changed to it can be restored later with a editor.focus() call
13219
+ if (isIE) {
13220
+ t.onInit.add(function(ed) {
13221
+ ed.dom.bind(ed.getBody(), 'beforedeactivate keydown', function() {
13222
+ ed.lastIERng = ed.selection.getRng();
13223
+ });
13224
+ });
13225
+ }
13226
+ }
13034
13227
 
13035
13228
  t.editorContainer = o.editorContainer;
13036
13229
  }
@@ -13052,16 +13245,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13052
13245
  if (document.domain && location.hostname != document.domain)
13053
13246
  tinymce.relaxedDomain = document.domain;
13054
13247
 
13055
- // Resize editor
13056
- DOM.setStyles(o.sizeContainer || o.editorContainer, {
13057
- width : w,
13058
- height : h
13059
- });
13060
-
13061
- h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
13062
- if (h < 100)
13063
- h = 100;
13064
-
13065
13248
  t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
13066
13249
 
13067
13250
  // We only need to override paths if we have to
@@ -13120,7 +13303,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13120
13303
  });
13121
13304
 
13122
13305
  t.contentAreaContainer = o.iframeContainer;
13123
- DOM.get(o.editorContainer).style.display = t.orgDisplay;
13306
+
13307
+ if (o.editorContainer) {
13308
+ DOM.get(o.editorContainer).style.display = t.orgDisplay;
13309
+ }
13310
+
13311
+ // Restore visibility on target element
13312
+ e.style.visibility = t.orgVisibility;
13313
+
13124
13314
  DOM.get(t.id).style.display = 'none';
13125
13315
  DOM.setAttrib(t.id, 'aria-hidden', true);
13126
13316
 
@@ -13230,7 +13420,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13230
13420
 
13231
13421
  self.serializer = new tinymce.dom.Serializer(settings, self.dom, self.schema);
13232
13422
 
13233
- self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer);
13423
+ self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer, self);
13234
13424
 
13235
13425
  self.formatter = new tinymce.Formatter(self);
13236
13426
 
@@ -13251,7 +13441,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13251
13441
 
13252
13442
  self.onPreInit.dispatch(self);
13253
13443
 
13254
- if (!settings.gecko_spellcheck)
13444
+ if (!settings.browser_spellcheck && !settings.gecko_spellcheck)
13255
13445
  doc.body.spellcheck = false;
13256
13446
 
13257
13447
  if (!settings.readonly) {
@@ -13327,6 +13517,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13327
13517
  var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, ieRng, controlElm, doc = self.getDoc(), body;
13328
13518
 
13329
13519
  if (!skip_focus) {
13520
+ if (self.lastIERng) {
13521
+ selection.setRng(self.lastIERng);
13522
+ }
13523
+
13330
13524
  // Get selected control element
13331
13525
  ieRng = selection.getRng();
13332
13526
  if (ieRng.item) {
@@ -13445,9 +13639,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13445
13639
  if (self.initialized) {
13446
13640
  o = o || {};
13447
13641
 
13448
- // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i>
13449
- selection.normalize();
13450
-
13451
13642
  // Get start node
13452
13643
  node = selection.getStart() || self.getBody();
13453
13644
  node = isIE && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
@@ -13792,7 +13983,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13792
13983
  if (!args.no_events)
13793
13984
  self.onSetContent.dispatch(self, args);
13794
13985
 
13795
- self.selection.normalize();
13986
+ // Don't normalize selection if the focused element isn't the body in content editable mode since it will steal focus otherwise
13987
+ if (!self.settings.content_editable || document.activeElement === self.getBody()) {
13988
+ self.selection.normalize();
13989
+ }
13796
13990
 
13797
13991
  return args.content;
13798
13992
  },
@@ -13925,14 +14119,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13925
14119
  return;
13926
14120
 
13927
14121
  case 'A':
13928
- value = dom.getAttrib(elm, 'name');
13929
- cls = 'mceItemAnchor';
14122
+ if (!elm.href) {
14123
+ value = dom.getAttrib(elm, 'name') || elm.id;
14124
+ cls = 'mceItemAnchor';
13930
14125
 
13931
- if (value) {
13932
- if (self.hasVisual)
13933
- dom.addClass(elm, cls);
13934
- else
13935
- dom.removeClass(elm, cls);
14126
+ if (value) {
14127
+ if (self.hasVisual)
14128
+ dom.addClass(elm, cls);
14129
+ else
14130
+ dom.removeClass(elm, cls);
14131
+ }
13936
14132
  }
13937
14133
 
13938
14134
  return;
@@ -14227,6 +14423,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14227
14423
  self.focus(true);
14228
14424
  };
14229
14425
 
14426
+ function nodeChanged() {
14427
+ // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i>
14428
+ self.selection.normalize();
14429
+ self.nodeChanged();
14430
+ }
14431
+
14230
14432
  // Add DOM events
14231
14433
  each(nativeToDispatcherMap, function(dispatcherName, nativeName) {
14232
14434
  var root = settings.content_editable ? self.getBody() : self.getDoc();
@@ -14261,13 +14463,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14261
14463
  }
14262
14464
 
14263
14465
  // Add node change handler
14264
- self.onMouseUp.add(self.nodeChanged);
14466
+ self.onMouseUp.add(nodeChanged);
14265
14467
 
14266
14468
  self.onKeyUp.add(function(ed, e) {
14267
14469
  var keyCode = e.keyCode;
14268
14470
 
14269
14471
  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)
14270
- self.nodeChanged();
14472
+ nodeChanged();
14271
14473
  });
14272
14474
 
14273
14475
  // Add reset handler
@@ -14875,9 +15077,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14875
15077
  };
14876
15078
 
14877
15079
  // Create event instances
14878
- onAdd = new Dispatcher(self);
14879
- onUndo = new Dispatcher(self);
14880
- onRedo = new Dispatcher(self);
15080
+ onBeforeAdd = new Dispatcher(self);
15081
+ onAdd = new Dispatcher(self);
15082
+ onUndo = new Dispatcher(self);
15083
+ onRedo = new Dispatcher(self);
14881
15084
 
14882
15085
  // Pass though onAdd event from UndoManager to Editor as onChange
14883
15086
  onAdd.add(function(undoman, level) {
@@ -14966,6 +15169,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14966
15169
  data : data,
14967
15170
 
14968
15171
  typing : false,
15172
+
15173
+ onBeforeAdd: onBeforeAdd,
14969
15174
 
14970
15175
  onAdd : onAdd,
14971
15176
 
@@ -14982,6 +15187,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14982
15187
 
14983
15188
  level = level || {};
14984
15189
  level.content = getContent();
15190
+
15191
+ self.onBeforeAdd.dispatch(self, level);
14985
15192
 
14986
15193
  // Add undo level if needed
14987
15194
  lastLevel = data[index];
@@ -15083,7 +15290,7 @@ tinymce.ForceBlocks = function(editor) {
15083
15290
  return;
15084
15291
 
15085
15292
  // Check if node is wrapped in block
15086
- while (node != rootNode) {
15293
+ while (node && node != rootNode) {
15087
15294
  if (blockElements[node.nodeName])
15088
15295
  return;
15089
15296
 
@@ -15224,28 +15431,40 @@ tinymce.ForceBlocks = function(editor) {
15224
15431
  return c;
15225
15432
  },
15226
15433
 
15227
- createControl : function(n) {
15228
- var c, t = this, ed = t.editor;
15434
+ createControl : function(name) {
15435
+ var ctrl, i, l, self = this, editor = self.editor, factories, ctrlName;
15229
15436
 
15230
- each(ed.plugins, function(p) {
15231
- if (p.createControl) {
15232
- c = p.createControl(n, t);
15437
+ // Build control factory cache
15438
+ if (!self.controlFactories) {
15439
+ self.controlFactories = [];
15440
+ each(editor.plugins, function(plugin) {
15441
+ if (plugin.createControl) {
15442
+ self.controlFactories.push(plugin);
15443
+ }
15444
+ });
15445
+ }
15233
15446
 
15234
- if (c)
15235
- return false;
15447
+ // Create controls by asking cached factories
15448
+ factories = self.controlFactories;
15449
+ for (i = 0, l = factories.length; i < l; i++) {
15450
+ ctrl = factories[i].createControl(name, self);
15451
+
15452
+ if (ctrl) {
15453
+ return self.add(ctrl);
15236
15454
  }
15237
- });
15455
+ }
15238
15456
 
15239
- switch (n) {
15240
- case "|":
15241
- case "separator":
15242
- return t.createSeparator();
15457
+ // Create sepearator
15458
+ if (name === "|" || name === "separator") {
15459
+ return self.createSeparator();
15243
15460
  }
15244
15461
 
15245
- if (!c && ed.buttons && (c = ed.buttons[n]))
15246
- return t.createButton(n, c);
15462
+ // Create control from button collection
15463
+ if (editor.buttons && (ctrl = editor.buttons[name])) {
15464
+ return self.createButton(name, ctrl);
15465
+ }
15247
15466
 
15248
- return t.add(c);
15467
+ return self.add(ctrl);
15249
15468
  },
15250
15469
 
15251
15470
  createDropMenu : function(id, s, cc) {
@@ -15680,6 +15899,7 @@ tinymce.ForceBlocks = function(editor) {
15680
15899
  MCE_ATTR_RE = /^(src|href|style)$/,
15681
15900
  FALSE = false,
15682
15901
  TRUE = true,
15902
+ formatChangeData,
15683
15903
  undef,
15684
15904
  getContentEditable = dom.getContentEditable;
15685
15905
 
@@ -16561,7 +16781,7 @@ tinymce.ForceBlocks = function(editor) {
16561
16781
  matchedFormatNames.push(name);
16562
16782
  }
16563
16783
  }
16564
- });
16784
+ }, dom.getRoot());
16565
16785
 
16566
16786
  return matchedFormatNames;
16567
16787
  };
@@ -16590,6 +16810,61 @@ tinymce.ForceBlocks = function(editor) {
16590
16810
  return FALSE;
16591
16811
  };
16592
16812
 
16813
+ function formatChanged(formats, callback) {
16814
+ var currentFormats;
16815
+
16816
+ // Setup format node change logic
16817
+ if (!formatChangeData) {
16818
+ formatChangeData = {};
16819
+ currentFormats = {};
16820
+
16821
+ ed.onNodeChange.addToTop(function(ed, cm, node) {
16822
+ var parents = getParents(node), matchedFormats = {};
16823
+
16824
+ // Check for new formats
16825
+ each(formatChangeData, function(callbacks, format) {
16826
+ each(parents, function(node) {
16827
+ if (matchNode(node, format, {}, true)) {
16828
+ if (!currentFormats[format]) {
16829
+ // Execute callbacks
16830
+ each(callbacks, function(callback) {
16831
+ callback(true, {node: node, format: format, parents: parents});
16832
+ });
16833
+
16834
+ currentFormats[format] = callbacks;
16835
+ }
16836
+
16837
+ matchedFormats[format] = callbacks;
16838
+ return false;
16839
+ }
16840
+ });
16841
+ });
16842
+
16843
+ // Check if current formats still match
16844
+ each(currentFormats, function(callbacks, format) {
16845
+ if (!matchedFormats[format]) {
16846
+ delete currentFormats[format];
16847
+
16848
+ each(callbacks, function(callback) {
16849
+ callback(false, {node: node, format: format, parents: parents});
16850
+ });
16851
+ }
16852
+ });
16853
+ });
16854
+ }
16855
+
16856
+ // Add format listeners
16857
+ each(formats.split(','), function(format) {
16858
+ if (!formatChangeData[format]) {
16859
+ formatChangeData[format] = [];
16860
+ }
16861
+
16862
+ formatChangeData[format].push(callback);
16863
+ });
16864
+
16865
+ return this;
16866
+ };
16867
+
16593
16868
  // Expose to public
16594
16869
  tinymce.extend(this, {
16595
16870
  get : get,
@@ -16600,7 +16875,8 @@ tinymce.ForceBlocks = function(editor) {
16600
16875
  match : match,
16601
16876
  matchAll : matchAll,
16602
16877
  matchNode : matchNode,
16603
- canApply : canApply
16878
+ canApply : canApply,
16879
+ formatChanged: formatChanged
16604
16880
  });
16605
16881
 
16606
16882
  // Initialize
@@ -17506,6 +17782,21 @@ tinymce.ForceBlocks = function(editor) {
17506
17782
  }
17507
17783
  };
17508
17784
 
17785
+ // Checks if the parent caret container node isn't empty if that is the case it
17786
+ // will remove the bogus state on all children that isn't empty
17787
+ function unmarkBogusCaretParents() {
17788
+ var i, caretContainer, node;
17789
+
17790
+ caretContainer = getParentCaretContainer(selection.getStart());
17791
+ if (caretContainer && !dom.isEmpty(caretContainer)) {
17792
+ tinymce.walk(caretContainer, function(node) {
17793
+ if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
17794
+ dom.setAttrib(node, 'data-mce-bogus', null);
17795
+ }
17796
+ }, 'childNodes');
17797
+ }
17798
+ };
17799
+
17509
17800
  // Only bind the caret events once
17510
17801
  if (!self._hasCaretEvents) {
17511
17802
  // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
@@ -17525,6 +17816,7 @@ tinymce.ForceBlocks = function(editor) {
17525
17816
  tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
17526
17817
  ed[name].addToTop(function() {
17527
17818
  removeCaretContainer();
17819
+ unmarkBogusCaretParents();
17528
17820
  });
17529
17821
  });
17530
17822
 
@@ -17535,16 +17827,12 @@ tinymce.ForceBlocks = function(editor) {
17535
17827
  if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
17536
17828
  removeCaretContainer(getParentCaretContainer(selection.getStart()));
17537
17829
  }
17830
+
17831
+ unmarkBogusCaretParents();
17538
17832
  });
17539
17833
 
17540
17834
  // Remove bogus state if they got filled by contents using editor.selection.setContent
17541
- selection.onSetContent.add(function() {
17542
- dom.getParent(selection.getStart(), function(node) {
17543
- if (node.id !== caretContainerId && dom.getAttrib(node, 'data-mce-bogus') && !dom.isEmpty(node)) {
17544
- dom.setAttrib(node, 'data-mce-bogus', null);
17545
- }
17546
- });
17547
- });
17835
+ selection.onSetContent.add(unmarkBogusCaretParents);
17548
17836
 
17549
17837
  self._hasCaretEvents = true;
17550
17838
  }