tinymce-rails 3.4.6 → 3.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. data/assets/precompiled/tinymce/plugins/emotions/emotions.htm +18 -17
  2. data/assets/precompiled/tinymce/plugins/emotions/js/emotions.js +21 -0
  3. data/assets/precompiled/tinymce/plugins/emotions/langs/en_dlg.js +1 -1
  4. data/assets/precompiled/tinymce/plugins/lists/editor_plugin.js +1 -1
  5. data/assets/precompiled/tinymce/plugins/lists/editor_plugin_src.js +198 -54
  6. data/assets/precompiled/tinymce/plugins/media/js/media.js +27 -16
  7. data/assets/precompiled/tinymce/plugins/media/langs/en_dlg.js +1 -1
  8. data/assets/precompiled/tinymce/plugins/media/moxieplayer.swf +0 -0
  9. data/assets/precompiled/tinymce/plugins/table/editor_plugin.js +1 -1
  10. data/assets/precompiled/tinymce/plugins/table/editor_plugin_src.js +90 -46
  11. data/assets/precompiled/tinymce/plugins/table/js/table.js +44 -10
  12. data/assets/precompiled/tinymce/plugins/table/table.htm +1 -1
  13. data/assets/precompiled/tinymce/themes/advanced/charmap.htm +5 -1
  14. data/assets/precompiled/tinymce/themes/advanced/editor_template.js +1 -1
  15. data/assets/precompiled/tinymce/themes/advanced/editor_template_src.js +6 -2
  16. data/assets/precompiled/tinymce/themes/advanced/js/charmap.js +11 -3
  17. data/assets/precompiled/tinymce/themes/advanced/js/color_picker.js +4 -4
  18. data/assets/precompiled/tinymce/themes/advanced/langs/en_dlg.js +1 -1
  19. data/assets/precompiled/tinymce/tiny_mce.js +1 -1
  20. data/assets/precompiled/tinymce/tiny_mce_jquery.js +1 -1
  21. data/assets/precompiled/tinymce/tiny_mce_jquery_src.js +544 -295
  22. data/assets/precompiled/tinymce/tiny_mce_src.js +544 -295
  23. data/assets/vendor/tinymce/tiny_mce.js +544 -295
  24. data/assets/vendor/tinymce/tiny_mce_jquery.js +544 -295
  25. data/lib/tinymce/railtie.rb +2 -2
  26. data/lib/tinymce/version.rb +2 -2
  27. metadata +5 -5
@@ -5,9 +5,9 @@
5
5
  var tinymce = {
6
6
  majorVersion : '3',
7
7
 
8
- minorVersion : '4.6',
8
+ minorVersion : '4.7',
9
9
 
10
- releaseDate : '2011-09-29',
10
+ releaseDate : '2011-11-03',
11
11
 
12
12
  _init : function() {
13
13
  var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -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) {