tinymce-rails 3.4.6 → 3.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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) {