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;
@@ -1176,8 +1176,11 @@ tinymce.create('tinymce.util.Dispatcher', {
1176
1176
 
1177
1177
  v = '{';
1178
1178
 
1179
- for (i in o)
1180
- v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
1179
+ for (i in o) {
1180
+ if (o.hasOwnProperty(i)) {
1181
+ v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
1182
+ }
1183
+ }
1181
1184
 
1182
1185
  return v + '}';
1183
1186
  }
@@ -1198,6 +1201,7 @@ tinymce.create('tinymce.util.Dispatcher', {
1198
1201
 
1199
1202
  };
1200
1203
  })();
1204
+
1201
1205
  tinymce.create('static tinymce.util.XHR', {
1202
1206
  send : function(o) {
1203
1207
  var x, t, w = window, c = 0;
@@ -1313,11 +1317,14 @@ tinymce.create('static tinymce.util.XHR', {
1313
1317
  }());
1314
1318
  (function(tinymce){
1315
1319
  tinymce.VK = {
1316
- DELETE:46,
1317
- BACKSPACE:8
1318
-
1320
+ DELETE: 46,
1321
+ BACKSPACE: 8,
1322
+ ENTER: 13,
1323
+ TAB: 9,
1324
+ SPACEBAR: 32,
1325
+ UP: 38,
1326
+ DOWN: 40
1319
1327
  }
1320
-
1321
1328
  })(tinymce);
1322
1329
 
1323
1330
  (function(tinymce) {
@@ -1346,7 +1353,7 @@ tinymce.create('static tinymce.util.XHR', {
1346
1353
  node = blockElm.firstChild;
1347
1354
 
1348
1355
  // Ignore empty text nodes
1349
- while (node.nodeType == 3 && node.nodeValue.length == 0)
1356
+ while (node && node.nodeType == 3 && node.nodeValue.length == 0)
1350
1357
  node = node.nextSibling;
1351
1358
 
1352
1359
  if (node && node.nodeName === 'SPAN') {
@@ -1395,6 +1402,21 @@ tinymce.create('static tinymce.util.XHR', {
1395
1402
  });
1396
1403
  };
1397
1404
 
1405
+ function removeHrOnBackspace(ed) {
1406
+ ed.onKeyDown.add(function(ed, e) {
1407
+ if (e.keyCode === BACKSPACE) {
1408
+ if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {
1409
+ var node = ed.selection.getNode();
1410
+ var previousSibling = node.previousSibling;
1411
+ if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
1412
+ ed.dom.remove(previousSibling);
1413
+ tinymce.dom.Event.cancel(e);
1414
+ }
1415
+ }
1416
+ }
1417
+ })
1418
+ }
1419
+
1398
1420
  function focusBody(ed) {
1399
1421
  // Fix for a focus bug in FF 3.x where the body element
1400
1422
  // wouldn't get proper focus if the user clicked on the HTML element
@@ -1432,6 +1454,31 @@ tinymce.create('static tinymce.util.XHR', {
1432
1454
  });
1433
1455
  };
1434
1456
 
1457
+ function selectionChangeNodeChanged(ed) {
1458
+ var lastRng, selectionTimer;
1459
+
1460
+ ed.dom.bind(ed.getDoc(), 'selectionchange', function() {
1461
+ if (selectionTimer) {
1462
+ clearTimeout(selectionTimer);
1463
+ selectionTimer = 0;
1464
+ }
1465
+
1466
+ selectionTimer = window.setTimeout(function() {
1467
+ var rng = ed.selection.getRng();
1468
+
1469
+ // Compare the ranges to see if it was a real change or not
1470
+ if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {
1471
+ ed.nodeChanged();
1472
+ lastRng = rng;
1473
+ }
1474
+ }, 50);
1475
+ });
1476
+ }
1477
+
1478
+ function ensureBodyHasRoleApplication(ed) {
1479
+ document.body.setAttribute("role", "application");
1480
+ }
1481
+
1435
1482
  tinymce.create('tinymce.util.Quirks', {
1436
1483
  Quirks: function(ed) {
1437
1484
  // WebKit
@@ -1440,24 +1487,33 @@ tinymce.create('static tinymce.util.XHR', {
1440
1487
  emptyEditorWhenDeleting(ed);
1441
1488
  inputMethodFocus(ed);
1442
1489
  selectControlElements(ed);
1490
+
1491
+ // iOS
1492
+ if (tinymce.isIDevice) {
1493
+ selectionChangeNodeChanged(ed);
1494
+ }
1443
1495
  }
1444
1496
 
1445
1497
  // IE
1446
1498
  if (tinymce.isIE) {
1499
+ removeHrOnBackspace(ed);
1447
1500
  emptyEditorWhenDeleting(ed);
1501
+ ensureBodyHasRoleApplication(ed);
1448
1502
  }
1449
1503
 
1450
1504
  // Gecko
1451
1505
  if (tinymce.isGecko) {
1506
+ removeHrOnBackspace(ed);
1452
1507
  focusBody(ed);
1453
1508
  }
1454
1509
  }
1455
1510
  });
1456
1511
  })(tinymce);
1512
+
1457
1513
  (function(tinymce) {
1458
1514
  var namedEntities, baseEntities, reverseEntities,
1459
- attrsCharsRegExp = /[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1460
- textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1515
+ attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1516
+ textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1461
1517
  rawCharsRegExp = /[<>&\"\']/g,
1462
1518
  entityRegExp = /&(#x|#)?([\w]+);/g,
1463
1519
  asciiMap = {
@@ -2443,7 +2499,7 @@ tinymce.html.Styles = function(settings, schema) {
2443
2499
  '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
2444
2500
  '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
2445
2501
  '(?:\\/([^>]+)>)|' + // End element
2446
- '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element
2502
+ '(?:([^\\s\\/<>]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/)>)' + // Start element
2447
2503
  ')', 'g');
2448
2504
 
2449
2505
  attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;
@@ -4115,48 +4171,48 @@ tinymce.html.Writer = function(settings) {
4115
4171
 
4116
4172
  return this.run(e, function(e) {
4117
4173
  var s = t.settings;
4174
+ if (v !== null) {
4175
+ switch (n) {
4176
+ case "style":
4177
+ if (!is(v, 'string')) {
4178
+ each(v, function(v, n) {
4179
+ t.setStyle(e, n, v);
4180
+ });
4118
4181
 
4119
- switch (n) {
4120
- case "style":
4121
- if (!is(v, 'string')) {
4122
- each(v, function(v, n) {
4123
- t.setStyle(e, n, v);
4124
- });
4125
-
4126
- return;
4127
- }
4182
+ return;
4183
+ }
4128
4184
 
4129
- // No mce_style for elements with these since they might get resized by the user
4130
- if (s.keep_values) {
4131
- if (v && !t._isRes(v))
4132
- e.setAttribute('data-mce-style', v, 2);
4133
- else
4134
- e.removeAttribute('data-mce-style', 2);
4135
- }
4185
+ // No mce_style for elements with these since they might get resized by the user
4186
+ if (s.keep_values) {
4187
+ if (v && !t._isRes(v))
4188
+ e.setAttribute('data-mce-style', v, 2);
4189
+ else
4190
+ e.removeAttribute('data-mce-style', 2);
4191
+ }
4136
4192
 
4137
- e.style.cssText = v;
4138
- break;
4193
+ e.style.cssText = v;
4194
+ break;
4139
4195
 
4140
- case "class":
4141
- e.className = v || ''; // Fix IE null bug
4142
- break;
4196
+ case "class":
4197
+ e.className = v || ''; // Fix IE null bug
4198
+ break;
4143
4199
 
4144
- case "src":
4145
- case "href":
4146
- if (s.keep_values) {
4147
- if (s.url_converter)
4148
- v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
4200
+ case "src":
4201
+ case "href":
4202
+ if (s.keep_values) {
4203
+ if (s.url_converter)
4204
+ v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
4149
4205
 
4150
- t.setAttrib(e, 'data-mce-' + n, v, 2);
4151
- }
4206
+ t.setAttrib(e, 'data-mce-' + n, v, 2);
4207
+ }
4152
4208
 
4153
- break;
4209
+ break;
4154
4210
 
4155
- case "shape":
4156
- e.setAttribute('data-mce-style', v);
4157
- break;
4211
+ case "shape":
4212
+ e.setAttribute('data-mce-style', v);
4213
+ break;
4214
+ }
4158
4215
  }
4159
-
4160
4216
  if (is(v) && v !== null && v.length !== 0)
4161
4217
  e.setAttribute(n, '' + v, 2);
4162
4218
  else
@@ -5932,7 +5988,7 @@ tinymce.html.Writer = function(settings) {
5932
5988
  parent = node.parentNode;
5933
5989
  root = dom.getRoot().parentNode;
5934
5990
 
5935
- while (parent != root) {
5991
+ while (parent != root && parent.nodeType !== 9) {
5936
5992
  children = parent.children;
5937
5993
 
5938
5994
  i = children.length;
@@ -7241,7 +7297,8 @@ tinymce.html.Writer = function(settings) {
7241
7297
  if (sb && eb && sb != eb) {
7242
7298
  n = sb;
7243
7299
 
7244
- while ((n = n.nextSibling) && n != eb) {
7300
+ var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot());
7301
+ while ((n = walker.next()) && n != eb) {
7245
7302
  if (dom.isBlock(n))
7246
7303
  bl.push(n);
7247
7304
  }
@@ -7647,7 +7704,7 @@ tinymce.html.Writer = function(settings) {
7647
7704
 
7648
7705
  // Replace all BOM characters for now until we can find a better solution
7649
7706
  if (!args.cleanup)
7650
- args.content = args.content.replace(/\uFEFF/g, '');
7707
+ args.content = args.content.replace(/\uFEFF|\u200B/g, '');
7651
7708
 
7652
7709
  // Post process
7653
7710
  if (!args.no_events)
@@ -7941,6 +7998,24 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
7941
7998
  return;
7942
7999
  }
7943
8000
 
8001
+ function exclude(nodes) {
8002
+ var node;
8003
+
8004
+ // First node is excluded
8005
+ node = nodes[0];
8006
+ if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
8007
+ nodes.splice(0, 1);
8008
+ }
8009
+
8010
+ // Last node is excluded
8011
+ node = nodes[nodes.length - 1];
8012
+ if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
8013
+ nodes.splice(nodes.length - 1, 1);
8014
+ }
8015
+
8016
+ return nodes;
8017
+ };
8018
+
7944
8019
  function collectSiblings(node, name, end_node) {
7945
8020
  var siblings = [];
7946
8021
 
@@ -7970,7 +8045,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
7970
8045
  if (!next)
7971
8046
  siblings.reverse();
7972
8047
 
7973
- callback(siblings);
8048
+ callback(exclude(siblings));
7974
8049
  }
7975
8050
  }
7976
8051
  };
@@ -7983,28 +8058,28 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
7983
8058
  if (endContainer.nodeType == 1 && endContainer.hasChildNodes())
7984
8059
  endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
7985
8060
 
7986
- // Find common ancestor and end points
7987
- ancestor = dom.findCommonAncestor(startContainer, endContainer);
7988
-
7989
8061
  // Same container
7990
8062
  if (startContainer == endContainer)
7991
- return callback([startContainer]);
8063
+ return callback(exclude([startContainer]));
7992
8064
 
8065
+ // Find common ancestor and end points
8066
+ ancestor = dom.findCommonAncestor(startContainer, endContainer);
8067
+
7993
8068
  // Process left side
7994
8069
  for (node = startContainer; node; node = node.parentNode) {
7995
- if (node == endContainer)
8070
+ if (node === endContainer)
7996
8071
  return walkBoundary(startContainer, ancestor, true);
7997
8072
 
7998
- if (node == ancestor)
8073
+ if (node === ancestor)
7999
8074
  break;
8000
8075
  }
8001
8076
 
8002
8077
  // Process right side
8003
8078
  for (node = endContainer; node; node = node.parentNode) {
8004
- if (node == startContainer)
8079
+ if (node === startContainer)
8005
8080
  return walkBoundary(endContainer, ancestor);
8006
8081
 
8007
- if (node == ancestor)
8082
+ if (node === ancestor)
8008
8083
  break;
8009
8084
  }
8010
8085
 
@@ -8023,48 +8098,46 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8023
8098
  );
8024
8099
 
8025
8100
  if (siblings.length)
8026
- callback(siblings);
8101
+ callback(exclude(siblings));
8027
8102
 
8028
8103
  // Walk right leaf
8029
8104
  walkBoundary(endContainer, endPoint);
8030
8105
  };
8031
8106
 
8032
- /* this.split = function(rng) {
8107
+ this.split = function(rng) {
8033
8108
  var startContainer = rng.startContainer,
8034
8109
  startOffset = rng.startOffset,
8035
8110
  endContainer = rng.endContainer,
8036
8111
  endOffset = rng.endOffset;
8037
8112
 
8038
8113
  function splitText(node, offset) {
8039
- if (offset == node.nodeValue.length)
8040
- node.appendData(INVISIBLE_CHAR);
8041
-
8042
- node = node.splitText(offset);
8043
-
8044
- if (node.nodeValue === INVISIBLE_CHAR)
8045
- node.nodeValue = '';
8046
-
8047
- return node;
8114
+ return node.splitText(offset);
8048
8115
  };
8049
8116
 
8050
8117
  // Handle single text node
8051
- if (startContainer == endContainer) {
8052
- if (startContainer.nodeType == 3) {
8053
- if (startOffset != 0)
8054
- startContainer = endContainer = splitText(startContainer, startOffset);
8055
-
8056
- if (endOffset - startOffset != startContainer.nodeValue.length)
8057
- splitText(startContainer, endOffset - startOffset);
8118
+ if (startContainer == endContainer && startContainer.nodeType == 3) {
8119
+ if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
8120
+ endContainer = splitText(startContainer, startOffset);
8121
+ startContainer = endContainer.previousSibling;
8122
+
8123
+ if (endOffset > startOffset) {
8124
+ endOffset = endOffset - startOffset;
8125
+ startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
8126
+ endOffset = endContainer.nodeValue.length;
8127
+ startOffset = 0;
8128
+ } else {
8129
+ endOffset = 0;
8130
+ }
8058
8131
  }
8059
8132
  } else {
8060
8133
  // Split startContainer text node if needed
8061
- if (startContainer.nodeType == 3 && startOffset != 0) {
8134
+ if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
8062
8135
  startContainer = splitText(startContainer, startOffset);
8063
8136
  startOffset = 0;
8064
8137
  }
8065
8138
 
8066
8139
  // Split endContainer text node if needed
8067
- if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {
8140
+ if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
8068
8141
  endContainer = splitText(endContainer, endOffset).previousSibling;
8069
8142
  endOffset = endContainer.nodeValue.length;
8070
8143
  }
@@ -8077,7 +8150,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
8077
8150
  endOffset : endOffset
8078
8151
  };
8079
8152
  };
8080
- */
8153
+
8081
8154
  };
8082
8155
 
8083
8156
  tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {
@@ -10369,6 +10442,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10369
10442
  visual_table_class : 'mceItemTable',
10370
10443
  visual : 1,
10371
10444
  font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
10445
+ 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
10372
10446
  apply_source_formatting : 1,
10373
10447
  directionality : 'ltr',
10374
10448
  forced_root_block : 'p',
@@ -10800,10 +10874,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10800
10874
 
10801
10875
  // Keep scripts from executing
10802
10876
  t.parser.addNodeFilter('script', function(nodes, name) {
10803
- var i = nodes.length;
10877
+ var i = nodes.length, node;
10804
10878
 
10805
- while (i--)
10806
- nodes[i].attr('type', 'mce-text/javascript');
10879
+ while (i--) {
10880
+ node = nodes[i];
10881
+ node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
10882
+ }
10807
10883
  });
10808
10884
 
10809
10885
  t.parser.addNodeFilter('#cdata', function(nodes, name) {
@@ -12144,21 +12220,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12144
12220
  });
12145
12221
  }
12146
12222
 
12147
- // Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5
12148
- // It only fires the nodeChange event every 50ms since it would other wise update the UI when you type and it hogs the CPU
12149
- if (tinymce.isWebKit) {
12150
- dom.bind(t.getDoc(), 'selectionchange', function() {
12151
- if (t.selectionTimer) {
12152
- clearTimeout(t.selectionTimer);
12153
- t.selectionTimer = 0;
12154
- }
12155
-
12156
- t.selectionTimer = window.setTimeout(function() {
12157
- t.nodeChanged();
12158
- }, 50);
12159
- });
12160
- }
12161
-
12162
12223
  // Bug fix for FireFox keeping styles from end of selection instead of start.
12163
12224
  if (tinymce.isGecko) {
12164
12225
  function getAttributeApplyFunction() {
@@ -12168,7 +12229,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12168
12229
  var target = t.selection.getStart();
12169
12230
 
12170
12231
  if (target !== t.getBody()) {
12171
- t.dom.removeAllAttribs(target);
12232
+ t.dom.setAttrib(target, "style", null);
12172
12233
 
12173
12234
  each(template, function(attr) {
12174
12235
  target.setAttributeNode(attr.cloneNode(true));
@@ -14021,8 +14082,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14021
14082
  MCE_ATTR_RE = /^(src|href|style)$/,
14022
14083
  FALSE = false,
14023
14084
  TRUE = true,
14024
- undefined,
14025
- pendingFormats = {apply : [], remove : []};
14085
+ undefined;
14026
14086
 
14027
14087
  function isArray(obj) {
14028
14088
  return obj instanceof Array;
@@ -14238,7 +14298,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14238
14298
  }
14239
14299
  };
14240
14300
 
14241
- function applyRngStyle(rng, bookmark) {
14301
+ function applyRngStyle(rng, bookmark, node_specific) {
14242
14302
  var newWrappers = [], wrapName, wrapElm;
14243
14303
 
14244
14304
  // Setup wrapper element
@@ -14302,7 +14362,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14302
14362
 
14303
14363
  // Is it valid to wrap this item
14304
14364
  if (isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
14305
- !(node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279)) {
14365
+ !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && node.id !== '_mce_caret') {
14306
14366
  // Start wrapping
14307
14367
  if (!currentWrapElm) {
14308
14368
  // Wrap the node
@@ -14457,12 +14517,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14457
14517
 
14458
14518
  if (format) {
14459
14519
  if (node) {
14460
- rng = dom.createRng();
14461
-
14462
- rng.setStartBefore(node);
14463
- rng.setEndAfter(node);
14464
-
14465
- applyRngStyle(expandRng(rng, formatList));
14520
+ if (node.nodeType) {
14521
+ rng = dom.createRng();
14522
+ rng.setStartBefore(node);
14523
+ rng.setEndAfter(node);
14524
+ applyRngStyle(expandRng(rng, formatList), null, true);
14525
+ } else {
14526
+ applyRngStyle(node, null, true);
14527
+ }
14466
14528
  } else {
14467
14529
  if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {
14468
14530
  // Obtain selection node before selection is unselected by applyRngStyle()
@@ -14676,10 +14738,15 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14676
14738
 
14677
14739
  // Handle node
14678
14740
  if (node) {
14679
- rng = dom.createRng();
14680
- rng.setStartBefore(node);
14681
- rng.setEndAfter(node);
14682
- removeRngStyle(rng);
14741
+ if (node.nodeType) {
14742
+ rng = dom.createRng();
14743
+ rng.setStartBefore(node);
14744
+ rng.setEndAfter(node);
14745
+ removeRngStyle(rng);
14746
+ } else {
14747
+ removeRngStyle(node);
14748
+ }
14749
+
14683
14750
  return;
14684
14751
  }
14685
14752
 
@@ -14696,6 +14763,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14696
14763
  ed.nodeChanged();
14697
14764
  } else
14698
14765
  performCaretAction('remove', name, vars);
14766
+
14767
+ // 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
14768
+ if (tinymce.isWebKit) {
14769
+ ed.execCommand('mceCleanup');
14770
+ }
14699
14771
  };
14700
14772
 
14701
14773
  function toggle(name, vars, node) {
@@ -14770,7 +14842,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14770
14842
  };
14771
14843
 
14772
14844
  function match(name, vars, node) {
14773
- var startNode, i;
14845
+ var startNode;
14774
14846
 
14775
14847
  function matchParents(node) {
14776
14848
  // Find first node with similar format settings
@@ -14786,21 +14858,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14786
14858
  if (node)
14787
14859
  return matchParents(node);
14788
14860
 
14789
- // Check pending formats
14790
- if (selection.isCollapsed()) {
14791
- for (i = pendingFormats.apply.length - 1; i >= 0; i--) {
14792
- if (pendingFormats.apply[i].name == name)
14793
- return true;
14794
- }
14795
-
14796
- for (i = pendingFormats.remove.length - 1; i >= 0; i--) {
14797
- if (pendingFormats.remove[i].name == name)
14798
- return false;
14799
- }
14800
-
14801
- return matchParents(selection.getNode());
14802
- }
14803
-
14804
14861
  // Check selected node
14805
14862
  node = selection.getNode();
14806
14863
  if (matchParents(node))
@@ -14819,33 +14876,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14819
14876
  function matchAll(names, vars) {
14820
14877
  var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;
14821
14878
 
14822
- // If the selection is collapsed then check pending formats
14823
- if (selection.isCollapsed()) {
14824
- for (ni = 0; ni < names.length; ni++) {
14825
- // If the name is to be removed, then stop it from being added
14826
- for (i = pendingFormats.remove.length - 1; i >= 0; i--) {
14827
- name = names[ni];
14828
-
14829
- if (pendingFormats.remove[i].name == name) {
14830
- checkedMap[name] = true;
14831
- break;
14832
- }
14833
- }
14834
- }
14835
-
14836
- // If the format is to be applied
14837
- for (i = pendingFormats.apply.length - 1; i >= 0; i--) {
14838
- for (ni = 0; ni < names.length; ni++) {
14839
- name = names[ni];
14840
-
14841
- if (!checkedMap[name] && pendingFormats.apply[i].name == name) {
14842
- checkedMap[name] = true;
14843
- matchedFormatNames.push(name);
14844
- }
14845
- }
14846
- }
14847
- }
14848
-
14849
14879
  // Check start of selection for formats
14850
14880
  startElement = selection.getStart();
14851
14881
  dom.getParent(startElement, function(node) {
@@ -14954,7 +14984,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14954
14984
  };
14955
14985
 
14956
14986
  function isWhiteSpaceNode(node) {
14957
- return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);
14987
+ return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
14958
14988
  };
14959
14989
 
14960
14990
  function wrap(node, name, attrs) {
@@ -14970,31 +15000,37 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14970
15000
  var startContainer = rng.startContainer,
14971
15001
  startOffset = rng.startOffset,
14972
15002
  endContainer = rng.endContainer,
14973
- endOffset = rng.endOffset, sibling, lastIdx, leaf;
15003
+ endOffset = rng.endOffset, sibling, lastIdx, leaf, endPoint;
14974
15004
 
14975
15005
  // This function walks up the tree if there is no siblings before/after the node
14976
- function findParentContainer(container, child_name, sibling_name, root) {
14977
- var parent, child;
15006
+ function findParentContainer(start) {
15007
+ var container, parent, child, sibling, siblingName;
14978
15008
 
14979
- root = root || dom.getRoot();
15009
+ container = parent = start ? startContainer : endContainer;
15010
+ siblingName = start ? 'previousSibling' : 'nextSibling';
15011
+ root = dom.getRoot();
14980
15012
 
14981
- for (;;) {
14982
- // Check if we can move up are we at root level or body level
14983
- parent = container.parentNode;
15013
+ // If it's a text node and the offset is inside the text
15014
+ if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
15015
+ if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
15016
+ return container;
15017
+ }
15018
+ }
14984
15019
 
15020
+ for (;;) {
14985
15021
  // Stop expanding on block elements or root depending on format
14986
15022
  if (parent == root || (!format[0].block_expand && isBlock(parent)))
14987
- return container;
14988
-
14989
- for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {
14990
- if (sibling.nodeType == 1 && !isBookmarkNode(sibling))
14991
- return container;
15023
+ return parent;
14992
15024
 
14993
- if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))
14994
- return container;
15025
+ // Walk left/right
15026
+ for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
15027
+ if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling)) {
15028
+ return parent;
15029
+ }
14995
15030
  }
14996
15031
 
14997
- container = container.parentNode;
15032
+ // Check if we can move up are we at root level or body level
15033
+ parent = parent.parentNode;
14998
15034
  }
14999
15035
 
15000
15036
  return container;
@@ -15032,23 +15068,103 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15032
15068
  }
15033
15069
 
15034
15070
  // Exclude bookmark nodes if possible
15035
- if (isBookmarkNode(startContainer.parentNode))
15036
- startContainer = startContainer.parentNode;
15037
-
15038
- if (isBookmarkNode(startContainer))
15071
+ if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
15072
+ startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
15039
15073
  startContainer = startContainer.nextSibling || startContainer;
15040
15074
 
15041
- if (isBookmarkNode(endContainer.parentNode)) {
15042
- endOffset = dom.nodeIndex(endContainer);
15043
- endContainer = endContainer.parentNode;
15075
+ if (startContainer.nodeType == 3)
15076
+ startOffset = 0;
15044
15077
  }
15045
15078
 
15046
- if (isBookmarkNode(endContainer) && endContainer.previousSibling) {
15047
- endContainer = endContainer.previousSibling;
15048
- endOffset = endContainer.length;
15079
+ if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
15080
+ endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
15081
+ endContainer = endContainer.previousSibling || endContainer;
15082
+
15083
+ if (endContainer.nodeType == 3)
15084
+ endOffset = endContainer.length;
15049
15085
  }
15050
15086
 
15051
15087
  if (format[0].inline) {
15088
+ if (rng.collapsed) {
15089
+ function findWordEndPoint(container, offset, start) {
15090
+ var walker, node, pos, lastTextNode;
15091
+
15092
+ function findSpace(node, offset) {
15093
+ var pos, pos2, str = node.nodeValue;
15094
+
15095
+ if (typeof(offset) == "undefined") {
15096
+ offset = start ? str.length : 0;
15097
+ }
15098
+
15099
+ if (start) {
15100
+ pos = str.lastIndexOf(' ', offset);
15101
+ pos2 = str.lastIndexOf('\u00a0', offset);
15102
+ pos = pos > pos2 ? pos : pos2;
15103
+
15104
+ // Include the space on remove to avoid tag soup
15105
+ if (pos !== -1 && !remove) {
15106
+ pos++;
15107
+ }
15108
+ } else {
15109
+ pos = str.indexOf(' ', offset);
15110
+ pos2 = str.indexOf('\u00a0', offset);
15111
+ pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
15112
+ }
15113
+
15114
+ return pos;
15115
+ };
15116
+
15117
+ if (container.nodeType === 3) {
15118
+ pos = findSpace(container, offset);
15119
+
15120
+ if (pos !== -1) {
15121
+ return {container : container, offset : pos};
15122
+ }
15123
+
15124
+ lastTextNode = container;
15125
+ }
15126
+
15127
+ // Walk the nodes inside the block
15128
+ walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
15129
+ while (node = walker[start ? 'prev' : 'next']()) {
15130
+ if (node.nodeType === 3) {
15131
+ lastTextNode = node;
15132
+ pos = findSpace(node);
15133
+
15134
+ if (pos !== -1) {
15135
+ return {container : node, offset : pos};
15136
+ }
15137
+ } else if (isBlock(node)) {
15138
+ break;
15139
+ }
15140
+ }
15141
+
15142
+ if (lastTextNode) {
15143
+ if (start) {
15144
+ offset = 0;
15145
+ } else {
15146
+ offset = lastTextNode.length;
15147
+ }
15148
+
15149
+ return {container: lastTextNode, offset: offset};
15150
+ }
15151
+ }
15152
+
15153
+ // Expand left to closest word boundery
15154
+ endPoint = findWordEndPoint(startContainer, startOffset, true);
15155
+ if (endPoint) {
15156
+ startContainer = endPoint.container;
15157
+ startOffset = endPoint.offset;
15158
+ }
15159
+
15160
+ // Expand right to closest word boundery
15161
+ endPoint = findWordEndPoint(endContainer, endOffset);
15162
+ if (endPoint) {
15163
+ endContainer = endPoint.container;
15164
+ endOffset = endPoint.offset;
15165
+ }
15166
+ }
15167
+
15052
15168
  // Avoid applying formatting to a trailing space.
15053
15169
  leaf = findLeaf(endContainer, endOffset);
15054
15170
  if (leaf.node) {
@@ -15062,19 +15178,25 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15062
15178
  endContainer = leaf.node;
15063
15179
  endContainer.splitText(leaf.offset - 1);
15064
15180
  } else if (leaf.node.previousSibling) {
15065
- endContainer = leaf.node.previousSibling;
15181
+ // TODO: Figure out why this is in here
15182
+ //endContainer = leaf.node.previousSibling;
15066
15183
  }
15067
15184
  }
15068
15185
  }
15069
15186
  }
15070
-
15187
+
15071
15188
  // Move start/end point up the tree if the leaves are sharp and if we are in different containers
15072
15189
  // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
15073
15190
  // This will reduce the number of wrapper elements that needs to be created
15074
15191
  // Move start point up the tree
15075
15192
  if (format[0].inline || format[0].block_expand) {
15076
- startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');
15077
- endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');
15193
+ if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
15194
+ startContainer = findParentContainer(true);
15195
+ }
15196
+
15197
+ if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
15198
+ endContainer = findParentContainer();
15199
+ }
15078
15200
  }
15079
15201
 
15080
15202
  // Expand start/end container to matching selector
@@ -15148,10 +15270,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15148
15270
  // Non block element then try to expand up the leaf
15149
15271
  if (format[0].block) {
15150
15272
  if (!isBlock(startContainer))
15151
- startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');
15273
+ startContainer = findParentContainer(true);
15152
15274
 
15153
15275
  if (!isBlock(endContainer))
15154
- endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');
15276
+ endContainer = findParentContainer();
15155
15277
  }
15156
15278
  }
15157
15279
 
@@ -15444,7 +15566,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15444
15566
  };
15445
15567
 
15446
15568
  function getContainer(rng, start) {
15447
- var container, offset, lastIdx;
15569
+ var container, offset, lastIdx, walker;
15448
15570
 
15449
15571
  container = rng[start ? 'startContainer' : 'endContainer'];
15450
15572
  offset = rng[start ? 'startOffset' : 'endOffset'];
@@ -15458,140 +15580,267 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15458
15580
  container = container.childNodes[offset > lastIdx ? lastIdx : offset];
15459
15581
  }
15460
15582
 
15583
+ // If start text node is excluded then walk to the next node
15584
+ if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
15585
+ container = new TreeWalker(container, ed.getBody()).next() || container;
15586
+ }
15587
+
15588
+ // If end text node is excluded then walk to the previous node
15589
+ if (container.nodeType === 3 && !start && offset == 0) {
15590
+ container = new TreeWalker(container, ed.getBody()).prev() || container;
15591
+ }
15592
+
15461
15593
  return container;
15462
15594
  };
15463
15595
 
15464
15596
  function performCaretAction(type, name, vars) {
15465
- var i, currentPendingFormats = pendingFormats[type],
15466
- otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];
15597
+ var invisibleChar, caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
15598
+
15599
+ // Setup invisible character use zero width space on Gecko since it doesn't change the heigt of the container
15600
+ invisibleChar = tinymce.isGecko ? '\u200B' : INVISIBLE_CHAR;
15601
+
15602
+ // Creates a caret container bogus element
15603
+ function createCaretContainer(fill) {
15604
+ var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
15467
15605
 
15468
- function hasPending() {
15469
- return pendingFormats.apply.length || pendingFormats.remove.length;
15606
+ if (fill) {
15607
+ caretContainer.appendChild(ed.getDoc().createTextNode(invisibleChar));
15608
+ }
15609
+
15610
+ return caretContainer;
15470
15611
  };
15471
15612
 
15472
- function resetPending() {
15473
- pendingFormats.apply = [];
15474
- pendingFormats.remove = [];
15613
+ function isCaretContainerEmpty(node, nodes) {
15614
+ while (node) {
15615
+ if ((node.nodeType === 3 && node.nodeValue !== invisibleChar) || node.childNodes.length > 1) {
15616
+ return false;
15617
+ }
15618
+
15619
+ // Collect nodes
15620
+ if (nodes && node.nodeType === 1) {
15621
+ nodes.push(node);
15622
+ }
15623
+
15624
+ node = node.firstChild;
15625
+ }
15626
+
15627
+ return true;
15475
15628
  };
15629
+
15630
+ // Returns any parent caret container element
15631
+ function getParentCaretContainer(node) {
15632
+ while (node) {
15633
+ if (node.id === caretContainerId) {
15634
+ return node;
15635
+ }
15476
15636
 
15477
- function perform(caret_node) {
15478
- // Apply pending formats
15479
- each(pendingFormats.apply.reverse(), function(item) {
15480
- apply(item.name, item.vars, caret_node);
15637
+ node = node.parentNode;
15638
+ }
15639
+ };
15481
15640
 
15482
- // Colored nodes should be underlined so that the color of the underline matches the text color.
15483
- if (item.name === 'forecolor' && item.vars.value)
15484
- processUnderlineAndColor(caret_node.parentNode);
15485
- });
15641
+ // Finds the first text node in the specified node
15642
+ function findFirstTextNode(node) {
15643
+ var walker;
15486
15644
 
15487
- // Remove pending formats
15488
- each(pendingFormats.remove.reverse(), function(item) {
15489
- remove(item.name, item.vars, caret_node);
15490
- });
15645
+ if (node) {
15646
+ walker = new TreeWalker(node, node);
15647
+
15648
+ for (node = walker.current(); node; node = walker.next()) {
15649
+ if (node.nodeType === 3) {
15650
+ return node;
15651
+ }
15652
+ }
15653
+ }
15654
+ };
15655
+
15656
+ // Removes the caret container for the specified node or all on the current document
15657
+ function removeCaretContainer(node, move_caret) {
15658
+ var child, rng;
15659
+
15660
+ if (!node) {
15661
+ node = getParentCaretContainer(selection.getStart());
15662
+
15663
+ if (!node) {
15664
+ while (node = dom.get(caretContainerId)) {
15665
+ removeCaretContainer(node, false);
15666
+ }
15667
+ }
15668
+ } else {
15669
+ rng = selection.getRng(true);
15670
+
15671
+ if (isCaretContainerEmpty(node)) {
15672
+ if (move_caret !== false) {
15673
+ rng.setStartBefore(node);
15674
+ rng.setEndBefore(node);
15675
+ }
15676
+
15677
+ dom.remove(node);
15678
+ } else {
15679
+ child = findFirstTextNode(node);
15680
+ child = child.deleteData(0, 1);
15681
+ dom.remove(node, 1);
15682
+ }
15491
15683
 
15492
- dom.remove(caret_node, 1);
15493
- resetPending();
15684
+ selection.setRng(rng);
15685
+ }
15494
15686
  };
15687
+
15688
+ // Applies formatting to the caret postion
15689
+ function applyCaretFormat() {
15690
+ var rng, caretContainer, textNode, offset, bookmark, container, text;
15691
+
15692
+ rng = selection.getRng(true);
15693
+ offset = rng.startOffset;
15694
+ container = rng.startContainer;
15695
+ text = container.nodeValue;
15696
+
15697
+ caretContainer = getParentCaretContainer(selection.getStart());
15698
+ if (caretContainer) {
15699
+ textNode = findFirstTextNode(caretContainer);
15700
+ }
15701
+
15702
+ // Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
15703
+ if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
15704
+ // Get bookmark of caret position
15705
+ bookmark = selection.getBookmark();
15706
+
15707
+ // Collapse bookmark range (WebKit)
15708
+ rng.collapse(true);
15709
+
15710
+ // Expand the range to the closest word and split it at those points
15711
+ rng = expandRng(rng, get(name));
15712
+ rng = rangeUtils.split(rng);
15713
+
15714
+ // Apply the format to the range
15715
+ apply(name, vars, rng);
15716
+
15717
+ // Move selection back to caret position
15718
+ selection.moveToBookmark(bookmark);
15719
+ } else {
15720
+ if (!caretContainer || textNode.nodeValue !== invisibleChar) {
15721
+ caretContainer = createCaretContainer(true);
15722
+ textNode = caretContainer.firstChild;
15723
+
15724
+ rng.insertNode(caretContainer);
15725
+ offset = 1;
15726
+
15727
+ apply(name, vars, caretContainer);
15728
+ } else {
15729
+ apply(name, vars, caretContainer);
15730
+ }
15731
+
15732
+ // Move selection to text node
15733
+ selection.setCursorLocation(textNode, offset);
15734
+ }
15735
+ };
15736
+
15737
+ function removeCaretFormat() {
15738
+ var rng = selection.getRng(true), container, offset, bookmark,
15739
+ hasContentAfter, node, formatNode, parents = [], i, caretContainer;
15740
+
15741
+ container = rng.startContainer;
15742
+ offset = rng.startOffset;
15743
+ node = container;
15744
+
15745
+ if (container.nodeType == 3) {
15746
+ if (offset != container.nodeValue.length || container.nodeValue === invisibleChar) {
15747
+ hasContentAfter = true;
15748
+ }
15749
+
15750
+ node = node.parentNode;
15751
+ }
15752
+
15753
+ while (node) {
15754
+ if (matchNode(node, name, vars)) {
15755
+ formatNode = node;
15756
+ break;
15757
+ }
15758
+
15759
+ if (node.nextSibling) {
15760
+ hasContentAfter = true;
15761
+ }
15762
+
15763
+ parents.push(node);
15764
+ node = node.parentNode;
15765
+ }
15495
15766
 
15496
- // Check if it already exists then ignore it
15497
- for (i = currentPendingFormats.length - 1; i >= 0; i--) {
15498
- if (currentPendingFormats[i].name == name)
15767
+ // Node doesn't have the specified format
15768
+ if (!formatNode) {
15499
15769
  return;
15500
- }
15770
+ }
15501
15771
 
15502
- currentPendingFormats.push({name : name, vars : vars});
15772
+ // Is there contents after the caret then remove the format on the element
15773
+ if (hasContentAfter) {
15774
+ // Get bookmark of caret position
15775
+ bookmark = selection.getBookmark();
15503
15776
 
15504
- // Check if it's in the other type, then remove it
15505
- for (i = otherPendingFormats.length - 1; i >= 0; i--) {
15506
- if (otherPendingFormats[i].name == name)
15507
- otherPendingFormats.splice(i, 1);
15508
- }
15777
+ // Collapse bookmark range (WebKit)
15778
+ rng.collapse(true);
15509
15779
 
15510
- // Pending apply or remove formats
15511
- if (hasPending()) {
15512
- ed.getDoc().execCommand('FontName', false, 'mceinline');
15513
- pendingFormats.lastRng = selection.getRng();
15780
+ // Expand the range to the closest word and split it at those points
15781
+ rng = expandRng(rng, get(name), true);
15782
+ rng = rangeUtils.split(rng);
15514
15783
 
15515
- // IE will convert the current word
15516
- each(dom.select('font,span'), function(node) {
15517
- var bookmark;
15784
+ // Remove the format from the range
15785
+ remove(name, vars, rng);
15518
15786
 
15519
- if (isCaretNode(node)) {
15520
- bookmark = selection.getBookmark();
15521
- perform(node);
15522
- selection.moveToBookmark(bookmark);
15523
- ed.nodeChanged();
15787
+ // Move selection back to caret position
15788
+ selection.moveToBookmark(bookmark);
15789
+ } else {
15790
+ caretContainer = createCaretContainer();
15791
+
15792
+ node = caretContainer;
15793
+ for (i = parents.length - 1; i >= 0; i--) {
15794
+ node.appendChild(parents[i].cloneNode(false));
15795
+ node = node.firstChild;
15524
15796
  }
15525
- });
15526
15797
 
15527
- // Only register listeners once if we need to
15528
- if (!pendingFormats.isListening && hasPending()) {
15529
- pendingFormats.isListening = true;
15530
- function performPendingFormat(node, textNode) {
15531
- var rng = dom.createRng();
15532
- perform(node);
15798
+ // Insert invisible character into inner most format element
15799
+ node.appendChild(dom.doc.createTextNode(invisibleChar));
15800
+ node = node.firstChild;
15533
15801
 
15534
- rng.setStart(textNode, textNode.nodeValue.length);
15535
- rng.setEnd(textNode, textNode.nodeValue.length);
15536
- selection.setRng(rng);
15537
- ed.nodeChanged();
15802
+ // Insert caret container after the formated node
15803
+ dom.insertAfter(caretContainer, formatNode);
15804
+
15805
+ // Move selection to text node
15806
+ selection.setCursorLocation(node, 1);
15807
+ }
15808
+ };
15809
+
15810
+ // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
15811
+ ed.onBeforeGetContent.addToTop(function() {
15812
+ var nodes = [], i;
15813
+
15814
+ if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
15815
+ // Mark children
15816
+ i = nodes.length;
15817
+ while (i--) {
15818
+ dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
15538
15819
  }
15539
- var enterKeyPressed = false;
15820
+ }
15821
+ });
15540
15822
 
15541
- each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {
15542
- ed[event].addToTop(function(ed, e) {
15543
- if (e.keyCode==13 && !e.shiftKey) {
15544
- enterKeyPressed = true;
15545
- return;
15546
- }
15547
- // Do we have pending formats and is the selection moved has moved
15548
- if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {
15549
- var foundCaret = false;
15550
- each(dom.select('font,span'), function(node) {
15551
- var textNode, rng;
15552
-
15553
- // Look for marker
15554
- if (isCaretNode(node)) {
15555
- foundCaret = true;
15556
- textNode = node.firstChild;
15557
-
15558
- // Find the first text node within node
15559
- while (textNode && textNode.nodeType != 3)
15560
- textNode = textNode.firstChild;
15561
-
15562
- if (textNode)
15563
- performPendingFormat(node, textNode);
15564
- else
15565
- dom.remove(node);
15566
- }
15567
- });
15568
-
15569
- // no caret - so we are
15570
- if (enterKeyPressed && !foundCaret) {
15571
- var node = selection.getNode();
15572
- var textNode = node;
15573
-
15574
- // Find the first text node within node
15575
- while (textNode && textNode.nodeType != 3)
15576
- textNode = textNode.firstChild;
15577
- if (textNode) {
15578
- node=textNode.parentNode;
15579
- while (!isBlock(node)){
15580
- node=node.parentNode;
15581
- }
15582
- performPendingFormat(node, textNode);
15583
- }
15584
- }
15823
+ // Remove caret container on mouse up and on key up
15824
+ tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
15825
+ ed[name].addToTop(function() {
15826
+ removeCaretContainer();
15827
+ });
15828
+ });
15585
15829
 
15586
- // Always unbind and clear pending styles on keyup
15587
- if (e.type == 'keyup' || e.type == 'mouseup') {
15588
- resetPending();
15589
- enterKeyPressed=false;
15590
- }
15591
- }
15592
- });
15593
- });
15830
+ // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
15831
+ ed.onKeyDown.addToTop(function(ed, e) {
15832
+ var keyCode = e.keyCode;
15833
+
15834
+ if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
15835
+ removeCaretContainer(getParentCaretContainer(selection.getStart()));
15594
15836
  }
15837
+ });
15838
+
15839
+ // Do apply or remove caret format
15840
+ if (type == "apply") {
15841
+ applyCaretFormat();
15842
+ } else {
15843
+ removeCaretFormat();
15595
15844
  }
15596
15845
  };
15597
15846
  };
@@ -15601,7 +15850,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
15601
15850
  var filters, fontSizes, dom, settings = ed.settings;
15602
15851
 
15603
15852
  if (settings.inline_styles) {
15604
- fontSizes = tinymce.explode(settings.font_size_style_values);
15853
+ fontSizes = tinymce.explode(settings.font_size_legacy_values);
15605
15854
 
15606
15855
  function replaceWithSpan(node, styles) {
15607
15856
  tinymce.each(styles, function(value, name) {