tinymce-rails 3.4.6 → 3.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. data/assets/precompiled/tinymce/plugins/emotions/emotions.htm +18 -17
  2. data/assets/precompiled/tinymce/plugins/emotions/js/emotions.js +21 -0
  3. data/assets/precompiled/tinymce/plugins/emotions/langs/en_dlg.js +1 -1
  4. data/assets/precompiled/tinymce/plugins/lists/editor_plugin.js +1 -1
  5. data/assets/precompiled/tinymce/plugins/lists/editor_plugin_src.js +198 -54
  6. data/assets/precompiled/tinymce/plugins/media/js/media.js +27 -16
  7. data/assets/precompiled/tinymce/plugins/media/langs/en_dlg.js +1 -1
  8. data/assets/precompiled/tinymce/plugins/media/moxieplayer.swf +0 -0
  9. data/assets/precompiled/tinymce/plugins/table/editor_plugin.js +1 -1
  10. data/assets/precompiled/tinymce/plugins/table/editor_plugin_src.js +90 -46
  11. data/assets/precompiled/tinymce/plugins/table/js/table.js +44 -10
  12. data/assets/precompiled/tinymce/plugins/table/table.htm +1 -1
  13. data/assets/precompiled/tinymce/themes/advanced/charmap.htm +5 -1
  14. data/assets/precompiled/tinymce/themes/advanced/editor_template.js +1 -1
  15. data/assets/precompiled/tinymce/themes/advanced/editor_template_src.js +6 -2
  16. data/assets/precompiled/tinymce/themes/advanced/js/charmap.js +11 -3
  17. data/assets/precompiled/tinymce/themes/advanced/js/color_picker.js +4 -4
  18. data/assets/precompiled/tinymce/themes/advanced/langs/en_dlg.js +1 -1
  19. data/assets/precompiled/tinymce/tiny_mce.js +1 -1
  20. data/assets/precompiled/tinymce/tiny_mce_jquery.js +1 -1
  21. data/assets/precompiled/tinymce/tiny_mce_jquery_src.js +544 -295
  22. data/assets/precompiled/tinymce/tiny_mce_src.js +544 -295
  23. data/assets/vendor/tinymce/tiny_mce.js +544 -295
  24. data/assets/vendor/tinymce/tiny_mce_jquery.js +544 -295
  25. data/lib/tinymce/railtie.rb +2 -2
  26. data/lib/tinymce/version.rb +2 -2
  27. metadata +5 -5
@@ -5,9 +5,9 @@
5
5
  var tinymce = {
6
6
  majorVersion : '3',
7
7
 
8
- minorVersion : '4.6',
8
+ minorVersion : '4.7',
9
9
 
10
- releaseDate : '2011-09-29',
10
+ releaseDate : '2011-11-03',
11
11
 
12
12
  _init : function() {
13
13
  var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -901,8 +901,11 @@ tinymce.create('tinymce.util.Dispatcher', {
901
901
 
902
902
  v = '{';
903
903
 
904
- for (i in o)
905
- v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
904
+ for (i in o) {
905
+ if (o.hasOwnProperty(i)) {
906
+ v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
907
+ }
908
+ }
906
909
 
907
910
  return v + '}';
908
911
  }
@@ -923,6 +926,7 @@ tinymce.create('tinymce.util.Dispatcher', {
923
926
 
924
927
  };
925
928
  })();
929
+
926
930
  tinymce.create('static tinymce.util.XHR', {
927
931
  send : function(o) {
928
932
  var x, t, w = window, c = 0;
@@ -1038,11 +1042,14 @@ tinymce.create('static tinymce.util.XHR', {
1038
1042
  }());
1039
1043
  (function(tinymce){
1040
1044
  tinymce.VK = {
1041
- DELETE:46,
1042
- BACKSPACE:8
1043
-
1045
+ DELETE: 46,
1046
+ BACKSPACE: 8,
1047
+ ENTER: 13,
1048
+ TAB: 9,
1049
+ SPACEBAR: 32,
1050
+ UP: 38,
1051
+ DOWN: 40
1044
1052
  }
1045
-
1046
1053
  })(tinymce);
1047
1054
 
1048
1055
  (function(tinymce) {
@@ -1071,7 +1078,7 @@ tinymce.create('static tinymce.util.XHR', {
1071
1078
  node = blockElm.firstChild;
1072
1079
 
1073
1080
  // Ignore empty text nodes
1074
- while (node.nodeType == 3 && node.nodeValue.length == 0)
1081
+ while (node && node.nodeType == 3 && node.nodeValue.length == 0)
1075
1082
  node = node.nextSibling;
1076
1083
 
1077
1084
  if (node && node.nodeName === 'SPAN') {
@@ -1120,6 +1127,21 @@ tinymce.create('static tinymce.util.XHR', {
1120
1127
  });
1121
1128
  };
1122
1129
 
1130
+ function removeHrOnBackspace(ed) {
1131
+ ed.onKeyDown.add(function(ed, e) {
1132
+ if (e.keyCode === BACKSPACE) {
1133
+ if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {
1134
+ var node = ed.selection.getNode();
1135
+ var previousSibling = node.previousSibling;
1136
+ if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
1137
+ ed.dom.remove(previousSibling);
1138
+ tinymce.dom.Event.cancel(e);
1139
+ }
1140
+ }
1141
+ }
1142
+ })
1143
+ }
1144
+
1123
1145
  function focusBody(ed) {
1124
1146
  // Fix for a focus bug in FF 3.x where the body element
1125
1147
  // wouldn't get proper focus if the user clicked on the HTML element
@@ -1157,6 +1179,31 @@ tinymce.create('static tinymce.util.XHR', {
1157
1179
  });
1158
1180
  };
1159
1181
 
1182
+ function selectionChangeNodeChanged(ed) {
1183
+ var lastRng, selectionTimer;
1184
+
1185
+ ed.dom.bind(ed.getDoc(), 'selectionchange', function() {
1186
+ if (selectionTimer) {
1187
+ clearTimeout(selectionTimer);
1188
+ selectionTimer = 0;
1189
+ }
1190
+
1191
+ selectionTimer = window.setTimeout(function() {
1192
+ var rng = ed.selection.getRng();
1193
+
1194
+ // Compare the ranges to see if it was a real change or not
1195
+ if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {
1196
+ ed.nodeChanged();
1197
+ lastRng = rng;
1198
+ }
1199
+ }, 50);
1200
+ });
1201
+ }
1202
+
1203
+ function ensureBodyHasRoleApplication(ed) {
1204
+ document.body.setAttribute("role", "application");
1205
+ }
1206
+
1160
1207
  tinymce.create('tinymce.util.Quirks', {
1161
1208
  Quirks: function(ed) {
1162
1209
  // WebKit
@@ -1165,24 +1212,33 @@ tinymce.create('static tinymce.util.XHR', {
1165
1212
  emptyEditorWhenDeleting(ed);
1166
1213
  inputMethodFocus(ed);
1167
1214
  selectControlElements(ed);
1215
+
1216
+ // iOS
1217
+ if (tinymce.isIDevice) {
1218
+ selectionChangeNodeChanged(ed);
1219
+ }
1168
1220
  }
1169
1221
 
1170
1222
  // IE
1171
1223
  if (tinymce.isIE) {
1224
+ removeHrOnBackspace(ed);
1172
1225
  emptyEditorWhenDeleting(ed);
1226
+ ensureBodyHasRoleApplication(ed);
1173
1227
  }
1174
1228
 
1175
1229
  // Gecko
1176
1230
  if (tinymce.isGecko) {
1231
+ removeHrOnBackspace(ed);
1177
1232
  focusBody(ed);
1178
1233
  }
1179
1234
  }
1180
1235
  });
1181
1236
  })(tinymce);
1237
+
1182
1238
  (function(tinymce) {
1183
1239
  var namedEntities, baseEntities, reverseEntities,
1184
- attrsCharsRegExp = /[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1185
- textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1240
+ attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1241
+ textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1186
1242
  rawCharsRegExp = /[<>&\"\']/g,
1187
1243
  entityRegExp = /&(#x|#)?([\w]+);/g,
1188
1244
  asciiMap = {
@@ -2168,7 +2224,7 @@ tinymce.html.Styles = function(settings, schema) {
2168
2224
  '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
2169
2225
  '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
2170
2226
  '(?:\\/([^>]+)>)|' + // End element
2171
- '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element
2227
+ '(?:([^\\s\\/<>]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/)>)' + // Start element
2172
2228
  ')', 'g');
2173
2229
 
2174
2230
  attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;
@@ -3873,48 +3929,48 @@ tinymce.html.Writer = function(settings) {
3873
3929
 
3874
3930
  return this.run(e, function(e) {
3875
3931
  var s = t.settings;
3932
+ if (v !== null) {
3933
+ switch (n) {
3934
+ case "style":
3935
+ if (!is(v, 'string')) {
3936
+ each(v, function(v, n) {
3937
+ t.setStyle(e, n, v);
3938
+ });
3876
3939
 
3877
- switch (n) {
3878
- case "style":
3879
- if (!is(v, 'string')) {
3880
- each(v, function(v, n) {
3881
- t.setStyle(e, n, v);
3882
- });
3883
-
3884
- return;
3885
- }
3940
+ return;
3941
+ }
3886
3942
 
3887
- // No mce_style for elements with these since they might get resized by the user
3888
- if (s.keep_values) {
3889
- if (v && !t._isRes(v))
3890
- e.setAttribute('data-mce-style', v, 2);
3891
- else
3892
- e.removeAttribute('data-mce-style', 2);
3893
- }
3943
+ // No mce_style for elements with these since they might get resized by the user
3944
+ if (s.keep_values) {
3945
+ if (v && !t._isRes(v))
3946
+ e.setAttribute('data-mce-style', v, 2);
3947
+ else
3948
+ e.removeAttribute('data-mce-style', 2);
3949
+ }
3894
3950
 
3895
- e.style.cssText = v;
3896
- break;
3951
+ e.style.cssText = v;
3952
+ break;
3897
3953
 
3898
- case "class":
3899
- e.className = v || ''; // Fix IE null bug
3900
- break;
3954
+ case "class":
3955
+ e.className = v || ''; // Fix IE null bug
3956
+ break;
3901
3957
 
3902
- case "src":
3903
- case "href":
3904
- if (s.keep_values) {
3905
- if (s.url_converter)
3906
- v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
3958
+ case "src":
3959
+ case "href":
3960
+ if (s.keep_values) {
3961
+ if (s.url_converter)
3962
+ v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
3907
3963
 
3908
- t.setAttrib(e, 'data-mce-' + n, v, 2);
3909
- }
3964
+ t.setAttrib(e, 'data-mce-' + n, v, 2);
3965
+ }
3910
3966
 
3911
- break;
3967
+ break;
3912
3968
 
3913
- case "shape":
3914
- e.setAttribute('data-mce-style', v);
3915
- break;
3969
+ case "shape":
3970
+ e.setAttribute('data-mce-style', v);
3971
+ break;
3972
+ }
3916
3973
  }
3917
-
3918
3974
  if (is(v) && v !== null && v.length !== 0)
3919
3975
  e.setAttribute(n, '' + v, 2);
3920
3976
  else
@@ -5690,7 +5746,7 @@ tinymce.html.Writer = function(settings) {
5690
5746
  parent = node.parentNode;
5691
5747
  root = dom.getRoot().parentNode;
5692
5748
 
5693
- while (parent != root) {
5749
+ while (parent != root && parent.nodeType !== 9) {
5694
5750
  children = parent.children;
5695
5751
 
5696
5752
  i = children.length;
@@ -8069,7 +8125,8 @@ window.tinymce.dom.Sizzle = Sizzle;
8069
8125
  if (sb && eb && sb != eb) {
8070
8126
  n = sb;
8071
8127
 
8072
- while ((n = n.nextSibling) && n != eb) {
8128
+ var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot());
8129
+ while ((n = walker.next()) && n != eb) {
8073
8130
  if (dom.isBlock(n))
8074
8131
  bl.push(n);
8075
8132
  }
@@ -8475,7 +8532,7 @@ window.tinymce.dom.Sizzle = Sizzle;
8475
8532
 
8476
8533
  // Replace all BOM characters for now until we can find a better solution
8477
8534
  if (!args.cleanup)
8478
- args.content = args.content.replace(/\uFEFF/g, '');
8535
+ args.content = args.content.replace(/\uFEFF|\u200B/g, '');
8479
8536
 
8480
8537
  // Post process
8481
8538
  if (!args.no_events)
@@ -8769,6 +8826,24 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8769
8826
  return;
8770
8827
  }
8771
8828
 
8829
+ function exclude(nodes) {
8830
+ var node;
8831
+
8832
+ // First node is excluded
8833
+ node = nodes[0];
8834
+ if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
8835
+ nodes.splice(0, 1);
8836
+ }
8837
+
8838
+ // Last node is excluded
8839
+ node = nodes[nodes.length - 1];
8840
+ if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
8841
+ nodes.splice(nodes.length - 1, 1);
8842
+ }
8843
+
8844
+ return nodes;
8845
+ };
8846
+
8772
8847
  function collectSiblings(node, name, end_node) {
8773
8848
  var siblings = [];
8774
8849
 
@@ -8798,7 +8873,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8798
8873
  if (!next)
8799
8874
  siblings.reverse();
8800
8875
 
8801
- callback(siblings);
8876
+ callback(exclude(siblings));
8802
8877
  }
8803
8878
  }
8804
8879
  };
@@ -8811,28 +8886,28 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8811
8886
  if (endContainer.nodeType == 1 && endContainer.hasChildNodes())
8812
8887
  endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
8813
8888
 
8814
- // Find common ancestor and end points
8815
- ancestor = dom.findCommonAncestor(startContainer, endContainer);
8816
-
8817
8889
  // Same container
8818
8890
  if (startContainer == endContainer)
8819
- return callback([startContainer]);
8891
+ return callback(exclude([startContainer]));
8820
8892
 
8893
+ // Find common ancestor and end points
8894
+ ancestor = dom.findCommonAncestor(startContainer, endContainer);
8895
+
8821
8896
  // Process left side
8822
8897
  for (node = startContainer; node; node = node.parentNode) {
8823
- if (node == endContainer)
8898
+ if (node === endContainer)
8824
8899
  return walkBoundary(startContainer, ancestor, true);
8825
8900
 
8826
- if (node == ancestor)
8901
+ if (node === ancestor)
8827
8902
  break;
8828
8903
  }
8829
8904
 
8830
8905
  // Process right side
8831
8906
  for (node = endContainer; node; node = node.parentNode) {
8832
- if (node == startContainer)
8907
+ if (node === startContainer)
8833
8908
  return walkBoundary(endContainer, ancestor);
8834
8909
 
8835
- if (node == ancestor)
8910
+ if (node === ancestor)
8836
8911
  break;
8837
8912
  }
8838
8913
 
@@ -8851,48 +8926,46 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8851
8926
  );
8852
8927
 
8853
8928
  if (siblings.length)
8854
- callback(siblings);
8929
+ callback(exclude(siblings));
8855
8930
 
8856
8931
  // Walk right leaf
8857
8932
  walkBoundary(endContainer, endPoint);
8858
8933
  };
8859
8934
 
8860
- /* this.split = function(rng) {
8935
+ this.split = function(rng) {
8861
8936
  var startContainer = rng.startContainer,
8862
8937
  startOffset = rng.startOffset,
8863
8938
  endContainer = rng.endContainer,
8864
8939
  endOffset = rng.endOffset;
8865
8940
 
8866
8941
  function splitText(node, offset) {
8867
- if (offset == node.nodeValue.length)
8868
- node.appendData(INVISIBLE_CHAR);
8869
-
8870
- node = node.splitText(offset);
8871
-
8872
- if (node.nodeValue === INVISIBLE_CHAR)
8873
- node.nodeValue = '';
8874
-
8875
- return node;
8942
+ return node.splitText(offset);
8876
8943
  };
8877
8944
 
8878
8945
  // Handle single text node
8879
- if (startContainer == endContainer) {
8880
- if (startContainer.nodeType == 3) {
8881
- if (startOffset != 0)
8882
- startContainer = endContainer = splitText(startContainer, startOffset);
8883
-
8884
- if (endOffset - startOffset != startContainer.nodeValue.length)
8885
- splitText(startContainer, endOffset - startOffset);
8946
+ if (startContainer == endContainer && startContainer.nodeType == 3) {
8947
+ if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
8948
+ endContainer = splitText(startContainer, startOffset);
8949
+ startContainer = endContainer.previousSibling;
8950
+
8951
+ if (endOffset > startOffset) {
8952
+ endOffset = endOffset - startOffset;
8953
+ startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
8954
+ endOffset = endContainer.nodeValue.length;
8955
+ startOffset = 0;
8956
+ } else {
8957
+ endOffset = 0;
8958
+ }
8886
8959
  }
8887
8960
  } else {
8888
8961
  // Split startContainer text node if needed
8889
- if (startContainer.nodeType == 3 && startOffset != 0) {
8962
+ if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
8890
8963
  startContainer = splitText(startContainer, startOffset);
8891
8964
  startOffset = 0;
8892
8965
  }
8893
8966
 
8894
8967
  // Split endContainer text node if needed
8895
- if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {
8968
+ if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
8896
8969
  endContainer = splitText(endContainer, endOffset).previousSibling;
8897
8970
  endOffset = endContainer.nodeValue.length;
8898
8971
  }
@@ -8905,7 +8978,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8905
8978
  endOffset : endOffset
8906
8979
  };
8907
8980
  };
8908
- */
8981
+
8909
8982
  };
8910
8983
 
8911
8984
  tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {
@@ -11192,6 +11265,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11192
11265
  visual_table_class : 'mceItemTable',
11193
11266
  visual : 1,
11194
11267
  font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
11268
+ font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
11195
11269
  apply_source_formatting : 1,
11196
11270
  directionality : 'ltr',
11197
11271
  forced_root_block : 'p',
@@ -11623,10 +11697,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11623
11697
 
11624
11698
  // Keep scripts from executing
11625
11699
  t.parser.addNodeFilter('script', function(nodes, name) {
11626
- var i = nodes.length;
11700
+ var i = nodes.length, node;
11627
11701
 
11628
- while (i--)
11629
- nodes[i].attr('type', 'mce-text/javascript');
11702
+ while (i--) {
11703
+ node = nodes[i];
11704
+ node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
11705
+ }
11630
11706
  });
11631
11707
 
11632
11708
  t.parser.addNodeFilter('#cdata', function(nodes, name) {
@@ -12967,21 +13043,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12967
13043
  });
12968
13044
  }
12969
13045
 
12970
- // Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5
12971
- // It only fires the nodeChange event every 50ms since it would other wise update the UI when you type and it hogs the CPU
12972
- if (tinymce.isWebKit) {
12973
- dom.bind(t.getDoc(), 'selectionchange', function() {
12974
- if (t.selectionTimer) {
12975
- clearTimeout(t.selectionTimer);
12976
- t.selectionTimer = 0;
12977
- }
12978
-
12979
- t.selectionTimer = window.setTimeout(function() {
12980
- t.nodeChanged();
12981
- }, 50);
12982
- });
12983
- }
12984
-
12985
13046
  // Bug fix for FireFox keeping styles from end of selection instead of start.
12986
13047
  if (tinymce.isGecko) {
12987
13048
  function getAttributeApplyFunction() {
@@ -12991,7 +13052,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12991
13052
  var target = t.selection.getStart();
12992
13053
 
12993
13054
  if (target !== t.getBody()) {
12994
- t.dom.removeAllAttribs(target);
13055
+ t.dom.setAttrib(target, "style", null);
12995
13056
 
12996
13057
  each(template, function(attr) {
12997
13058
  target.setAttributeNode(attr.cloneNode(true));
@@ -14844,8 +14905,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14844
14905
  MCE_ATTR_RE = /^(src|href|style)$/,
14845
14906
  FALSE = false,
14846
14907
  TRUE = true,
14847
- undefined,
14848
- pendingFormats = {apply : [], remove : []};
14908
+ undefined;
14849
14909
 
14850
14910
  function isArray(obj) {
14851
14911
  return obj instanceof Array;
@@ -15061,7 +15121,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15061
15121
  }
15062
15122
  };
15063
15123
 
15064
- function applyRngStyle(rng, bookmark) {
15124
+ function applyRngStyle(rng, bookmark, node_specific) {
15065
15125
  var newWrappers = [], wrapName, wrapElm;
15066
15126
 
15067
15127
  // Setup wrapper element
@@ -15125,7 +15185,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15125
15185
 
15126
15186
  // Is it valid to wrap this item
15127
15187
  if (isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
15128
- !(node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279)) {
15188
+ !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && node.id !== '_mce_caret') {
15129
15189
  // Start wrapping
15130
15190
  if (!currentWrapElm) {
15131
15191
  // Wrap the node
@@ -15280,12 +15340,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15280
15340
 
15281
15341
  if (format) {
15282
15342
  if (node) {
15283
- rng = dom.createRng();
15284
-
15285
- rng.setStartBefore(node);
15286
- rng.setEndAfter(node);
15287
-
15288
- applyRngStyle(expandRng(rng, formatList));
15343
+ if (node.nodeType) {
15344
+ rng = dom.createRng();
15345
+ rng.setStartBefore(node);
15346
+ rng.setEndAfter(node);
15347
+ applyRngStyle(expandRng(rng, formatList), null, true);
15348
+ } else {
15349
+ applyRngStyle(node, null, true);
15350
+ }
15289
15351
  } else {
15290
15352
  if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {
15291
15353
  // Obtain selection node before selection is unselected by applyRngStyle()
@@ -15499,10 +15561,15 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15499
15561
 
15500
15562
  // Handle node
15501
15563
  if (node) {
15502
- rng = dom.createRng();
15503
- rng.setStartBefore(node);
15504
- rng.setEndAfter(node);
15505
- removeRngStyle(rng);
15564
+ if (node.nodeType) {
15565
+ rng = dom.createRng();
15566
+ rng.setStartBefore(node);
15567
+ rng.setEndAfter(node);
15568
+ removeRngStyle(rng);
15569
+ } else {
15570
+ removeRngStyle(node);
15571
+ }
15572
+
15506
15573
  return;
15507
15574
  }
15508
15575
 
@@ -15519,6 +15586,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15519
15586
  ed.nodeChanged();
15520
15587
  } else
15521
15588
  performCaretAction('remove', name, vars);
15589
+
15590
+ // When you remove formatting from a table cell in WebKit (cell, not the contents of a cell) there is a rendering issue with column width
15591
+ if (tinymce.isWebKit) {
15592
+ ed.execCommand('mceCleanup');
15593
+ }
15522
15594
  };
15523
15595
 
15524
15596
  function toggle(name, vars, node) {
@@ -15593,7 +15665,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15593
15665
  };
15594
15666
 
15595
15667
  function match(name, vars, node) {
15596
- var startNode, i;
15668
+ var startNode;
15597
15669
 
15598
15670
  function matchParents(node) {
15599
15671
  // Find first node with similar format settings
@@ -15609,21 +15681,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15609
15681
  if (node)
15610
15682
  return matchParents(node);
15611
15683
 
15612
- // Check pending formats
15613
- if (selection.isCollapsed()) {
15614
- for (i = pendingFormats.apply.length - 1; i >= 0; i--) {
15615
- if (pendingFormats.apply[i].name == name)
15616
- return true;
15617
- }
15618
-
15619
- for (i = pendingFormats.remove.length - 1; i >= 0; i--) {
15620
- if (pendingFormats.remove[i].name == name)
15621
- return false;
15622
- }
15623
-
15624
- return matchParents(selection.getNode());
15625
- }
15626
-
15627
15684
  // Check selected node
15628
15685
  node = selection.getNode();
15629
15686
  if (matchParents(node))
@@ -15642,33 +15699,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15642
15699
  function matchAll(names, vars) {
15643
15700
  var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;
15644
15701
 
15645
- // If the selection is collapsed then check pending formats
15646
- if (selection.isCollapsed()) {
15647
- for (ni = 0; ni < names.length; ni++) {
15648
- // If the name is to be removed, then stop it from being added
15649
- for (i = pendingFormats.remove.length - 1; i >= 0; i--) {
15650
- name = names[ni];
15651
-
15652
- if (pendingFormats.remove[i].name == name) {
15653
- checkedMap[name] = true;
15654
- break;
15655
- }
15656
- }
15657
- }
15658
-
15659
- // If the format is to be applied
15660
- for (i = pendingFormats.apply.length - 1; i >= 0; i--) {
15661
- for (ni = 0; ni < names.length; ni++) {
15662
- name = names[ni];
15663
-
15664
- if (!checkedMap[name] && pendingFormats.apply[i].name == name) {
15665
- checkedMap[name] = true;
15666
- matchedFormatNames.push(name);
15667
- }
15668
- }
15669
- }
15670
- }
15671
-
15672
15702
  // Check start of selection for formats
15673
15703
  startElement = selection.getStart();
15674
15704
  dom.getParent(startElement, function(node) {
@@ -15777,7 +15807,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15777
15807
  };
15778
15808
 
15779
15809
  function isWhiteSpaceNode(node) {
15780
- return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);
15810
+ return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
15781
15811
  };
15782
15812
 
15783
15813
  function wrap(node, name, attrs) {
@@ -15793,31 +15823,37 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15793
15823
  var startContainer = rng.startContainer,
15794
15824
  startOffset = rng.startOffset,
15795
15825
  endContainer = rng.endContainer,
15796
- endOffset = rng.endOffset, sibling, lastIdx, leaf;
15826
+ endOffset = rng.endOffset, sibling, lastIdx, leaf, endPoint;
15797
15827
 
15798
15828
  // This function walks up the tree if there is no siblings before/after the node
15799
- function findParentContainer(container, child_name, sibling_name, root) {
15800
- var parent, child;
15829
+ function findParentContainer(start) {
15830
+ var container, parent, child, sibling, siblingName;
15801
15831
 
15802
- root = root || dom.getRoot();
15832
+ container = parent = start ? startContainer : endContainer;
15833
+ siblingName = start ? 'previousSibling' : 'nextSibling';
15834
+ root = dom.getRoot();
15803
15835
 
15804
- for (;;) {
15805
- // Check if we can move up are we at root level or body level
15806
- parent = container.parentNode;
15836
+ // If it's a text node and the offset is inside the text
15837
+ if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
15838
+ if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
15839
+ return container;
15840
+ }
15841
+ }
15807
15842
 
15843
+ for (;;) {
15808
15844
  // Stop expanding on block elements or root depending on format
15809
15845
  if (parent == root || (!format[0].block_expand && isBlock(parent)))
15810
- return container;
15846
+ return parent;
15811
15847
 
15812
- for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {
15813
- if (sibling.nodeType == 1 && !isBookmarkNode(sibling))
15814
- return container;
15815
-
15816
- if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))
15817
- return container;
15848
+ // Walk left/right
15849
+ for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
15850
+ if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling)) {
15851
+ return parent;
15852
+ }
15818
15853
  }
15819
15854
 
15820
- container = container.parentNode;
15855
+ // Check if we can move up are we at root level or body level
15856
+ parent = parent.parentNode;
15821
15857
  }
15822
15858
 
15823
15859
  return container;
@@ -15855,23 +15891,103 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15855
15891
  }
15856
15892
 
15857
15893
  // Exclude bookmark nodes if possible
15858
- if (isBookmarkNode(startContainer.parentNode))
15859
- startContainer = startContainer.parentNode;
15860
-
15861
- if (isBookmarkNode(startContainer))
15894
+ if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
15895
+ startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
15862
15896
  startContainer = startContainer.nextSibling || startContainer;
15863
15897
 
15864
- if (isBookmarkNode(endContainer.parentNode)) {
15865
- endOffset = dom.nodeIndex(endContainer);
15866
- endContainer = endContainer.parentNode;
15898
+ if (startContainer.nodeType == 3)
15899
+ startOffset = 0;
15867
15900
  }
15868
15901
 
15869
- if (isBookmarkNode(endContainer) && endContainer.previousSibling) {
15870
- endContainer = endContainer.previousSibling;
15871
- endOffset = endContainer.length;
15902
+ if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
15903
+ endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
15904
+ endContainer = endContainer.previousSibling || endContainer;
15905
+
15906
+ if (endContainer.nodeType == 3)
15907
+ endOffset = endContainer.length;
15872
15908
  }
15873
15909
 
15874
15910
  if (format[0].inline) {
15911
+ if (rng.collapsed) {
15912
+ function findWordEndPoint(container, offset, start) {
15913
+ var walker, node, pos, lastTextNode;
15914
+
15915
+ function findSpace(node, offset) {
15916
+ var pos, pos2, str = node.nodeValue;
15917
+
15918
+ if (typeof(offset) == "undefined") {
15919
+ offset = start ? str.length : 0;
15920
+ }
15921
+
15922
+ if (start) {
15923
+ pos = str.lastIndexOf(' ', offset);
15924
+ pos2 = str.lastIndexOf('\u00a0', offset);
15925
+ pos = pos > pos2 ? pos : pos2;
15926
+
15927
+ // Include the space on remove to avoid tag soup
15928
+ if (pos !== -1 && !remove) {
15929
+ pos++;
15930
+ }
15931
+ } else {
15932
+ pos = str.indexOf(' ', offset);
15933
+ pos2 = str.indexOf('\u00a0', offset);
15934
+ pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
15935
+ }
15936
+
15937
+ return pos;
15938
+ };
15939
+
15940
+ if (container.nodeType === 3) {
15941
+ pos = findSpace(container, offset);
15942
+
15943
+ if (pos !== -1) {
15944
+ return {container : container, offset : pos};
15945
+ }
15946
+
15947
+ lastTextNode = container;
15948
+ }
15949
+
15950
+ // Walk the nodes inside the block
15951
+ walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
15952
+ while (node = walker[start ? 'prev' : 'next']()) {
15953
+ if (node.nodeType === 3) {
15954
+ lastTextNode = node;
15955
+ pos = findSpace(node);
15956
+
15957
+ if (pos !== -1) {
15958
+ return {container : node, offset : pos};
15959
+ }
15960
+ } else if (isBlock(node)) {
15961
+ break;
15962
+ }
15963
+ }
15964
+
15965
+ if (lastTextNode) {
15966
+ if (start) {
15967
+ offset = 0;
15968
+ } else {
15969
+ offset = lastTextNode.length;
15970
+ }
15971
+
15972
+ return {container: lastTextNode, offset: offset};
15973
+ }
15974
+ }
15975
+
15976
+ // Expand left to closest word boundery
15977
+ endPoint = findWordEndPoint(startContainer, startOffset, true);
15978
+ if (endPoint) {
15979
+ startContainer = endPoint.container;
15980
+ startOffset = endPoint.offset;
15981
+ }
15982
+
15983
+ // Expand right to closest word boundery
15984
+ endPoint = findWordEndPoint(endContainer, endOffset);
15985
+ if (endPoint) {
15986
+ endContainer = endPoint.container;
15987
+ endOffset = endPoint.offset;
15988
+ }
15989
+ }
15990
+
15875
15991
  // Avoid applying formatting to a trailing space.
15876
15992
  leaf = findLeaf(endContainer, endOffset);
15877
15993
  if (leaf.node) {
@@ -15885,19 +16001,25 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15885
16001
  endContainer = leaf.node;
15886
16002
  endContainer.splitText(leaf.offset - 1);
15887
16003
  } else if (leaf.node.previousSibling) {
15888
- endContainer = leaf.node.previousSibling;
16004
+ // TODO: Figure out why this is in here
16005
+ //endContainer = leaf.node.previousSibling;
15889
16006
  }
15890
16007
  }
15891
16008
  }
15892
16009
  }
15893
-
16010
+
15894
16011
  // Move start/end point up the tree if the leaves are sharp and if we are in different containers
15895
16012
  // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
15896
16013
  // This will reduce the number of wrapper elements that needs to be created
15897
16014
  // Move start point up the tree
15898
16015
  if (format[0].inline || format[0].block_expand) {
15899
- startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');
15900
- endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');
16016
+ if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
16017
+ startContainer = findParentContainer(true);
16018
+ }
16019
+
16020
+ if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
16021
+ endContainer = findParentContainer();
16022
+ }
15901
16023
  }
15902
16024
 
15903
16025
  // Expand start/end container to matching selector
@@ -15971,10 +16093,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15971
16093
  // Non block element then try to expand up the leaf
15972
16094
  if (format[0].block) {
15973
16095
  if (!isBlock(startContainer))
15974
- startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');
16096
+ startContainer = findParentContainer(true);
15975
16097
 
15976
16098
  if (!isBlock(endContainer))
15977
- endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');
16099
+ endContainer = findParentContainer();
15978
16100
  }
15979
16101
  }
15980
16102
 
@@ -16267,7 +16389,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
16267
16389
  };
16268
16390
 
16269
16391
  function getContainer(rng, start) {
16270
- var container, offset, lastIdx;
16392
+ var container, offset, lastIdx, walker;
16271
16393
 
16272
16394
  container = rng[start ? 'startContainer' : 'endContainer'];
16273
16395
  offset = rng[start ? 'startOffset' : 'endOffset'];
@@ -16281,140 +16403,267 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
16281
16403
  container = container.childNodes[offset > lastIdx ? lastIdx : offset];
16282
16404
  }
16283
16405
 
16406
+ // If start text node is excluded then walk to the next node
16407
+ if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
16408
+ container = new TreeWalker(container, ed.getBody()).next() || container;
16409
+ }
16410
+
16411
+ // If end text node is excluded then walk to the previous node
16412
+ if (container.nodeType === 3 && !start && offset == 0) {
16413
+ container = new TreeWalker(container, ed.getBody()).prev() || container;
16414
+ }
16415
+
16284
16416
  return container;
16285
16417
  };
16286
16418
 
16287
16419
  function performCaretAction(type, name, vars) {
16288
- var i, currentPendingFormats = pendingFormats[type],
16289
- otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];
16420
+ var invisibleChar, caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
16421
+
16422
+ // Setup invisible character use zero width space on Gecko since it doesn't change the heigt of the container
16423
+ invisibleChar = tinymce.isGecko ? '\u200B' : INVISIBLE_CHAR;
16290
16424
 
16291
- function hasPending() {
16292
- return pendingFormats.apply.length || pendingFormats.remove.length;
16425
+ // Creates a caret container bogus element
16426
+ function createCaretContainer(fill) {
16427
+ var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
16428
+
16429
+ if (fill) {
16430
+ caretContainer.appendChild(ed.getDoc().createTextNode(invisibleChar));
16431
+ }
16432
+
16433
+ return caretContainer;
16293
16434
  };
16294
16435
 
16295
- function resetPending() {
16296
- pendingFormats.apply = [];
16297
- pendingFormats.remove = [];
16436
+ function isCaretContainerEmpty(node, nodes) {
16437
+ while (node) {
16438
+ if ((node.nodeType === 3 && node.nodeValue !== invisibleChar) || node.childNodes.length > 1) {
16439
+ return false;
16440
+ }
16441
+
16442
+ // Collect nodes
16443
+ if (nodes && node.nodeType === 1) {
16444
+ nodes.push(node);
16445
+ }
16446
+
16447
+ node = node.firstChild;
16448
+ }
16449
+
16450
+ return true;
16298
16451
  };
16452
+
16453
+ // Returns any parent caret container element
16454
+ function getParentCaretContainer(node) {
16455
+ while (node) {
16456
+ if (node.id === caretContainerId) {
16457
+ return node;
16458
+ }
16299
16459
 
16300
- function perform(caret_node) {
16301
- // Apply pending formats
16302
- each(pendingFormats.apply.reverse(), function(item) {
16303
- apply(item.name, item.vars, caret_node);
16460
+ node = node.parentNode;
16461
+ }
16462
+ };
16304
16463
 
16305
- // Colored nodes should be underlined so that the color of the underline matches the text color.
16306
- if (item.name === 'forecolor' && item.vars.value)
16307
- processUnderlineAndColor(caret_node.parentNode);
16308
- });
16464
+ // Finds the first text node in the specified node
16465
+ function findFirstTextNode(node) {
16466
+ var walker;
16309
16467
 
16310
- // Remove pending formats
16311
- each(pendingFormats.remove.reverse(), function(item) {
16312
- remove(item.name, item.vars, caret_node);
16313
- });
16468
+ if (node) {
16469
+ walker = new TreeWalker(node, node);
16470
+
16471
+ for (node = walker.current(); node; node = walker.next()) {
16472
+ if (node.nodeType === 3) {
16473
+ return node;
16474
+ }
16475
+ }
16476
+ }
16477
+ };
16478
+
16479
+ // Removes the caret container for the specified node or all on the current document
16480
+ function removeCaretContainer(node, move_caret) {
16481
+ var child, rng;
16482
+
16483
+ if (!node) {
16484
+ node = getParentCaretContainer(selection.getStart());
16485
+
16486
+ if (!node) {
16487
+ while (node = dom.get(caretContainerId)) {
16488
+ removeCaretContainer(node, false);
16489
+ }
16490
+ }
16491
+ } else {
16492
+ rng = selection.getRng(true);
16493
+
16494
+ if (isCaretContainerEmpty(node)) {
16495
+ if (move_caret !== false) {
16496
+ rng.setStartBefore(node);
16497
+ rng.setEndBefore(node);
16498
+ }
16499
+
16500
+ dom.remove(node);
16501
+ } else {
16502
+ child = findFirstTextNode(node);
16503
+ child = child.deleteData(0, 1);
16504
+ dom.remove(node, 1);
16505
+ }
16506
+
16507
+ selection.setRng(rng);
16508
+ }
16509
+ };
16510
+
16511
+ // Applies formatting to the caret postion
16512
+ function applyCaretFormat() {
16513
+ var rng, caretContainer, textNode, offset, bookmark, container, text;
16514
+
16515
+ rng = selection.getRng(true);
16516
+ offset = rng.startOffset;
16517
+ container = rng.startContainer;
16518
+ text = container.nodeValue;
16519
+
16520
+ caretContainer = getParentCaretContainer(selection.getStart());
16521
+ if (caretContainer) {
16522
+ textNode = findFirstTextNode(caretContainer);
16523
+ }
16524
+
16525
+ // Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
16526
+ if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
16527
+ // Get bookmark of caret position
16528
+ bookmark = selection.getBookmark();
16529
+
16530
+ // Collapse bookmark range (WebKit)
16531
+ rng.collapse(true);
16532
+
16533
+ // Expand the range to the closest word and split it at those points
16534
+ rng = expandRng(rng, get(name));
16535
+ rng = rangeUtils.split(rng);
16536
+
16537
+ // Apply the format to the range
16538
+ apply(name, vars, rng);
16539
+
16540
+ // Move selection back to caret position
16541
+ selection.moveToBookmark(bookmark);
16542
+ } else {
16543
+ if (!caretContainer || textNode.nodeValue !== invisibleChar) {
16544
+ caretContainer = createCaretContainer(true);
16545
+ textNode = caretContainer.firstChild;
16546
+
16547
+ rng.insertNode(caretContainer);
16548
+ offset = 1;
16549
+
16550
+ apply(name, vars, caretContainer);
16551
+ } else {
16552
+ apply(name, vars, caretContainer);
16553
+ }
16314
16554
 
16315
- dom.remove(caret_node, 1);
16316
- resetPending();
16555
+ // Move selection to text node
16556
+ selection.setCursorLocation(textNode, offset);
16557
+ }
16317
16558
  };
16318
16559
 
16319
- // Check if it already exists then ignore it
16320
- for (i = currentPendingFormats.length - 1; i >= 0; i--) {
16321
- if (currentPendingFormats[i].name == name)
16560
+ function removeCaretFormat() {
16561
+ var rng = selection.getRng(true), container, offset, bookmark,
16562
+ hasContentAfter, node, formatNode, parents = [], i, caretContainer;
16563
+
16564
+ container = rng.startContainer;
16565
+ offset = rng.startOffset;
16566
+ node = container;
16567
+
16568
+ if (container.nodeType == 3) {
16569
+ if (offset != container.nodeValue.length || container.nodeValue === invisibleChar) {
16570
+ hasContentAfter = true;
16571
+ }
16572
+
16573
+ node = node.parentNode;
16574
+ }
16575
+
16576
+ while (node) {
16577
+ if (matchNode(node, name, vars)) {
16578
+ formatNode = node;
16579
+ break;
16580
+ }
16581
+
16582
+ if (node.nextSibling) {
16583
+ hasContentAfter = true;
16584
+ }
16585
+
16586
+ parents.push(node);
16587
+ node = node.parentNode;
16588
+ }
16589
+
16590
+ // Node doesn't have the specified format
16591
+ if (!formatNode) {
16322
16592
  return;
16323
- }
16593
+ }
16324
16594
 
16325
- currentPendingFormats.push({name : name, vars : vars});
16595
+ // Is there contents after the caret then remove the format on the element
16596
+ if (hasContentAfter) {
16597
+ // Get bookmark of caret position
16598
+ bookmark = selection.getBookmark();
16326
16599
 
16327
- // Check if it's in the other type, then remove it
16328
- for (i = otherPendingFormats.length - 1; i >= 0; i--) {
16329
- if (otherPendingFormats[i].name == name)
16330
- otherPendingFormats.splice(i, 1);
16331
- }
16600
+ // Collapse bookmark range (WebKit)
16601
+ rng.collapse(true);
16332
16602
 
16333
- // Pending apply or remove formats
16334
- if (hasPending()) {
16335
- ed.getDoc().execCommand('FontName', false, 'mceinline');
16336
- pendingFormats.lastRng = selection.getRng();
16603
+ // Expand the range to the closest word and split it at those points
16604
+ rng = expandRng(rng, get(name), true);
16605
+ rng = rangeUtils.split(rng);
16337
16606
 
16338
- // IE will convert the current word
16339
- each(dom.select('font,span'), function(node) {
16340
- var bookmark;
16607
+ // Remove the format from the range
16608
+ remove(name, vars, rng);
16341
16609
 
16342
- if (isCaretNode(node)) {
16343
- bookmark = selection.getBookmark();
16344
- perform(node);
16345
- selection.moveToBookmark(bookmark);
16346
- ed.nodeChanged();
16610
+ // Move selection back to caret position
16611
+ selection.moveToBookmark(bookmark);
16612
+ } else {
16613
+ caretContainer = createCaretContainer();
16614
+
16615
+ node = caretContainer;
16616
+ for (i = parents.length - 1; i >= 0; i--) {
16617
+ node.appendChild(parents[i].cloneNode(false));
16618
+ node = node.firstChild;
16347
16619
  }
16348
- });
16349
16620
 
16350
- // Only register listeners once if we need to
16351
- if (!pendingFormats.isListening && hasPending()) {
16352
- pendingFormats.isListening = true;
16353
- function performPendingFormat(node, textNode) {
16354
- var rng = dom.createRng();
16355
- perform(node);
16621
+ // Insert invisible character into inner most format element
16622
+ node.appendChild(dom.doc.createTextNode(invisibleChar));
16623
+ node = node.firstChild;
16356
16624
 
16357
- rng.setStart(textNode, textNode.nodeValue.length);
16358
- rng.setEnd(textNode, textNode.nodeValue.length);
16359
- selection.setRng(rng);
16360
- ed.nodeChanged();
16625
+ // Insert caret container after the formated node
16626
+ dom.insertAfter(caretContainer, formatNode);
16627
+
16628
+ // Move selection to text node
16629
+ selection.setCursorLocation(node, 1);
16630
+ }
16631
+ };
16632
+
16633
+ // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
16634
+ ed.onBeforeGetContent.addToTop(function() {
16635
+ var nodes = [], i;
16636
+
16637
+ if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
16638
+ // Mark children
16639
+ i = nodes.length;
16640
+ while (i--) {
16641
+ dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
16361
16642
  }
16362
- var enterKeyPressed = false;
16643
+ }
16644
+ });
16363
16645
 
16364
- each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {
16365
- ed[event].addToTop(function(ed, e) {
16366
- if (e.keyCode==13 && !e.shiftKey) {
16367
- enterKeyPressed = true;
16368
- return;
16369
- }
16370
- // Do we have pending formats and is the selection moved has moved
16371
- if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {
16372
- var foundCaret = false;
16373
- each(dom.select('font,span'), function(node) {
16374
- var textNode, rng;
16375
-
16376
- // Look for marker
16377
- if (isCaretNode(node)) {
16378
- foundCaret = true;
16379
- textNode = node.firstChild;
16380
-
16381
- // Find the first text node within node
16382
- while (textNode && textNode.nodeType != 3)
16383
- textNode = textNode.firstChild;
16384
-
16385
- if (textNode)
16386
- performPendingFormat(node, textNode);
16387
- else
16388
- dom.remove(node);
16389
- }
16390
- });
16391
-
16392
- // no caret - so we are
16393
- if (enterKeyPressed && !foundCaret) {
16394
- var node = selection.getNode();
16395
- var textNode = node;
16396
-
16397
- // Find the first text node within node
16398
- while (textNode && textNode.nodeType != 3)
16399
- textNode = textNode.firstChild;
16400
- if (textNode) {
16401
- node=textNode.parentNode;
16402
- while (!isBlock(node)){
16403
- node=node.parentNode;
16404
- }
16405
- performPendingFormat(node, textNode);
16406
- }
16407
- }
16646
+ // Remove caret container on mouse up and on key up
16647
+ tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
16648
+ ed[name].addToTop(function() {
16649
+ removeCaretContainer();
16650
+ });
16651
+ });
16408
16652
 
16409
- // Always unbind and clear pending styles on keyup
16410
- if (e.type == 'keyup' || e.type == 'mouseup') {
16411
- resetPending();
16412
- enterKeyPressed=false;
16413
- }
16414
- }
16415
- });
16416
- });
16653
+ // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
16654
+ ed.onKeyDown.addToTop(function(ed, e) {
16655
+ var keyCode = e.keyCode;
16656
+
16657
+ if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
16658
+ removeCaretContainer(getParentCaretContainer(selection.getStart()));
16417
16659
  }
16660
+ });
16661
+
16662
+ // Do apply or remove caret format
16663
+ if (type == "apply") {
16664
+ applyCaretFormat();
16665
+ } else {
16666
+ removeCaretFormat();
16418
16667
  }
16419
16668
  };
16420
16669
  };
@@ -16424,7 +16673,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
16424
16673
  var filters, fontSizes, dom, settings = ed.settings;
16425
16674
 
16426
16675
  if (settings.inline_styles) {
16427
- fontSizes = tinymce.explode(settings.font_size_style_values);
16676
+ fontSizes = tinymce.explode(settings.font_size_legacy_values);
16428
16677
 
16429
16678
  function replaceWithSpan(node, styles) {
16430
16679
  tinymce.each(styles, function(value, name) {