tinymce-rails 3.5 → 3.5.2

Sign up to get free protection for your applications and to get access to all the features.
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
  }