tinymce-rails 3.5.2 → 3.5.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/lib/tinymce/rails/version.rb +2 -2
  2. data/vendor/assets/javascripts/tinymce/jquery.tinymce.js +1 -1
  3. data/vendor/assets/javascripts/tinymce/plugins/autolink/editor_plugin_src.js +181 -181
  4. data/vendor/assets/javascripts/tinymce/plugins/autoresize/editor_plugin_src.js +119 -119
  5. data/vendor/assets/javascripts/tinymce/plugins/autosave/editor_plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/autosave/editor_plugin_src.js +4 -2
  7. data/vendor/assets/javascripts/tinymce/plugins/contextmenu/editor_plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/contextmenu/editor_plugin_src.js +9 -7
  9. data/vendor/assets/javascripts/tinymce/plugins/emotions/langs/en_dlg.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/example_dependency/editor_plugin_src.js +50 -50
  11. data/vendor/assets/javascripts/tinymce/plugins/lists/editor_plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/lists/editor_plugin_src.js +952 -951
  13. data/vendor/assets/javascripts/tinymce/plugins/media/editor_plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/media/editor_plugin_src.js +22 -14
  15. data/vendor/assets/javascripts/tinymce/plugins/media/langs/en_dlg.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/style/langs/en_dlg.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/style/props.htm +845 -845
  18. data/vendor/assets/javascripts/tinymce/plugins/style/readme.txt +19 -19
  19. data/vendor/assets/javascripts/tinymce/plugins/tabfocus/editor_plugin_src.js +122 -122
  20. data/vendor/assets/javascripts/tinymce/plugins/table/editor_plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/table/editor_plugin_src.js +1449 -1446
  22. data/vendor/assets/javascripts/tinymce/plugins/table/js/table.js +4 -1
  23. data/vendor/assets/javascripts/tinymce/themes/advanced/js/color_picker.js +345 -345
  24. data/vendor/assets/javascripts/tinymce/themes/advanced/langs/en_dlg.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/themes/advanced/skins/default/content.css +0 -1
  26. data/vendor/assets/javascripts/tinymce/themes/advanced/skins/highcontrast/content.css +0 -1
  27. data/vendor/assets/javascripts/tinymce/themes/advanced/skins/o2k7/content.css +0 -1
  28. data/vendor/assets/javascripts/tinymce/themes/advanced/source_editor.htm +1 -1
  29. data/vendor/assets/javascripts/tinymce/tiny_mce.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/tiny_mce_jquery.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/tiny_mce_jquery_src.js +394 -158
  32. data/vendor/assets/javascripts/tinymce/tiny_mce_src.js +394 -158
  33. metadata +5 -4
@@ -6,9 +6,9 @@
6
6
  var tinymce = {
7
7
  majorVersion : '3',
8
8
 
9
- minorVersion : '5.2',
9
+ minorVersion : '5.4.1',
10
10
 
11
- releaseDate : '2012-05-31',
11
+ releaseDate : '2012-06-24',
12
12
 
13
13
  _init : function() {
14
14
  var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -1083,6 +1083,10 @@ tinymce.create('static tinymce.util.XHR', {
1083
1083
 
1084
1084
  modifierPressed: function (e) {
1085
1085
  return e.shiftKey || e.ctrlKey || e.altKey;
1086
+ },
1087
+
1088
+ metaKeyPressed: function(e) {
1089
+ return tinymce.isMac ? e.metaKey : e.ctrlKey;
1086
1090
  }
1087
1091
  };
1088
1092
  })(tinymce);
@@ -1098,6 +1102,12 @@ tinymce.util.Quirks = function(editor) {
1098
1102
  }
1099
1103
  }
1100
1104
 
1105
+ function getDocumentMode() {
1106
+ var documentMode = editor.getDoc().documentMode;
1107
+
1108
+ return documentMode ? documentMode : 6;
1109
+ };
1110
+
1101
1111
  function cleanupStylesWhenDeleting() {
1102
1112
  function removeMergedFormatSpans(isDelete) {
1103
1113
  var rng, blockElm, node, clonedSpan;
@@ -1157,74 +1167,58 @@ tinymce.util.Quirks = function(editor) {
1157
1167
  };
1158
1168
 
1159
1169
  function emptyEditorWhenDeleting() {
1160
- function getEndPointNode(rng, start) {
1161
- var container, offset, prefix = start ? 'start' : 'end';
1162
-
1163
- container = rng[prefix + 'Container'];
1164
- offset = rng[prefix + 'Offset'];
1170
+ function serializeRng(rng) {
1171
+ var body = dom.create("body");
1172
+ var contents = rng.cloneContents();
1173
+ body.appendChild(contents);
1174
+ return selection.serializer.serialize(body, {format: 'html'});
1175
+ }
1165
1176
 
1166
- // Resolve indexed container
1167
- if (container.nodeType == 1 && container.hasChildNodes()) {
1168
- container = container.childNodes[Math.min(start ? offset : (offset > 0 ? offset - 1 : 0), container.childNodes.length - 1)]
1169
- }
1177
+ function allContentsSelected(rng) {
1178
+ var selection = serializeRng(rng);
1170
1179
 
1171
- return container;
1172
- };
1180
+ var allRng = dom.createRng();
1181
+ allRng.selectNode(editor.getBody());
1173
1182
 
1174
- function isAtStartEndOfBody(rng, start) {
1175
- var container, offset, root, childNode, prefix = start ? 'start' : 'end', isAfter;
1183
+ var allSelection = serializeRng(allRng);//console.log(selection, "----", allSelection);
1184
+ return selection === allSelection;
1185
+ }
1176
1186
 
1177
- container = rng[prefix + 'Container'];
1178
- offset = rng[prefix + 'Offset'];
1179
- root = dom.getRoot();
1187
+ editor.onKeyDown.add(function(editor, e) {
1188
+ var keyCode = e.keyCode, isCollapsed;
1180
1189
 
1181
- // Resolve indexed container
1182
- if (container.nodeType == 1) {
1183
- isAfter = offset >= container.childNodes.length;
1184
- container = getEndPointNode(rng, start);
1190
+ // Empty the editor if it's needed for example backspace at <p><b>|</b></p>
1191
+ if (!e.isDefaultPrevented() && (keyCode == DELETE || keyCode == BACKSPACE)) {
1192
+ isCollapsed = editor.selection.isCollapsed();
1185
1193
 
1186
- if (container.nodeType == 3) {
1187
- offset = start && !isAfter ? 0 : container.nodeValue.length;
1194
+ // Selection is collapsed but the editor isn't empty
1195
+ if (isCollapsed && !dom.isEmpty(editor.getBody())) {
1196
+ return;
1188
1197
  }
1189
- }
1190
1198
 
1191
- // Check if start/end is in the middle of text
1192
- if (container.nodeType == 3 && ((start && offset > 0) || (!start && offset < container.nodeValue.length))) {
1193
- return false;
1194
- }
1195
-
1196
- // Walk up the DOM tree to see if the endpoint is at the beginning/end of body
1197
- while (container !== root) {
1198
- childNode = container.parentNode[start ? 'firstChild' : 'lastChild'];
1199
-
1200
- // If first/last element is a BR then jump to it's sibling in case: <p>x<br></p>
1201
- if (childNode.nodeName == "BR") {
1202
- childNode = childNode[start ? 'nextSibling' : 'previousSibling'] || childNode;
1199
+ // IE deletes all contents correctly when everything is selected
1200
+ if (tinymce.isIE && !isCollapsed) {
1201
+ return;
1203
1202
  }
1204
1203
 
1205
- // If the childNode isn't the container node then break in case <p><span>A</span>[X]</p>
1206
- if (childNode !== container) {
1207
- return false;
1204
+ // Selection isn't collapsed but not all the contents is selected
1205
+ if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
1206
+ return;
1208
1207
  }
1209
1208
 
1210
- container = container.parentNode;
1209
+ // Manually empty the editor
1210
+ editor.setContent('');
1211
+ editor.selection.setCursorLocation(editor.getBody(), 0);
1212
+ editor.nodeChanged();
1211
1213
  }
1214
+ });
1215
+ };
1212
1216
 
1213
- return true;
1214
- };
1215
-
1216
- editor.onKeyDown.addToTop(function(editor, e) {
1217
- var rng, keyCode = e.keyCode;
1218
-
1219
- if (!e.isDefaultPrevented() && (keyCode == DELETE || keyCode == BACKSPACE)) {
1220
- rng = selection.getRng(true);
1221
-
1222
- if (isAtStartEndOfBody(rng, true) && isAtStartEndOfBody(rng, false) &&
1223
- (rng.collapsed || dom.findCommonAncestor(getEndPointNode(rng, true), getEndPointNode(rng)) === dom.getRoot())) {
1224
- editor.setContent('');
1225
- editor.nodeChanged();
1226
- e.preventDefault();
1227
- }
1217
+ function selectAll() {
1218
+ editor.onKeyDown.add(function(editor, e) {
1219
+ if (e.keyCode == 65 && VK.metaKeyPressed(e)) {
1220
+ e.preventDefault();
1221
+ editor.execCommand('SelectAll');
1228
1222
  }
1229
1223
  });
1230
1224
  };
@@ -1393,16 +1387,15 @@ tinymce.util.Quirks = function(editor) {
1393
1387
  }
1394
1388
 
1395
1389
  function addNewLinesBeforeBrInPre() {
1396
- var documentMode = editor.getDoc().documentMode;
1397
-
1398
1390
  // IE8+ rendering mode does the right thing with BR in PRE
1399
- if (documentMode && documentMode > 7) {
1391
+ if (getDocumentMode() > 7) {
1400
1392
  return;
1401
1393
  }
1402
1394
 
1403
1395
  // Enable display: none in area and add a specific class that hides all BR elements in PRE to
1404
1396
  // avoid the caret from getting stuck at the BR elements while pressing the right arrow key
1405
1397
  setEditorCommandState('RespectVisibilityInDesign', true);
1398
+ editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
1406
1399
  dom.addClass(editor.getBody(), 'mceHideBrInPre');
1407
1400
 
1408
1401
  // Adds a \n before all BR elements in PRE to get them visual
@@ -1608,13 +1601,105 @@ tinymce.util.Quirks = function(editor) {
1608
1601
  editor.onSetContent.add(repaint);
1609
1602
  };
1610
1603
 
1611
- function deleteImageOnBackSpace() {
1604
+ function deleteControlItemOnBackSpace() {
1612
1605
  editor.onKeyDown.add(function(editor, e) {
1613
- if (!e.isDefaultPrevented() && e.keyCode == 8 && selection.getNode().nodeName == 'IMG') {
1606
+ var rng;
1607
+
1608
+ if (!e.isDefaultPrevented() && e.keyCode == BACKSPACE) {
1609
+ rng = editor.getDoc().selection.createRange();
1610
+ if (rng && rng.item) {
1611
+ e.preventDefault();
1612
+ editor.undoManager.beforeChange();
1613
+ dom.remove(rng.item(0));
1614
+ editor.undoManager.add();
1615
+ }
1616
+ }
1617
+ });
1618
+ };
1619
+
1620
+ function renderEmptyBlocksFix() {
1621
+ var emptyBlocksCSS;
1622
+
1623
+ // IE10+
1624
+ if (getDocumentMode() >= 10) {
1625
+ emptyBlocksCSS = '';
1626
+ tinymce.each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
1627
+ emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
1628
+ });
1629
+
1630
+ editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
1631
+ }
1632
+ };
1633
+
1634
+ function fakeImageResize() {
1635
+ var mouseDownImg, startX, startY, startW, startH;
1636
+
1637
+ if (!settings.object_resizing || settings.webkit_fake_resize === false) {
1638
+ return;
1639
+ }
1640
+
1641
+ editor.contentStyles.push('.mceResizeImages img {cursor: se-resize !important}');
1642
+
1643
+ function resizeImage(e) {
1644
+ var deltaX, deltaY, ratio, width, height;
1645
+
1646
+ if (mouseDownImg) {
1647
+ deltaX = e.screenX - startX;
1648
+ deltaY = e.screenY - startY;
1649
+ ratio = Math.max((startW + deltaX) / startW, (startH + deltaY) / startH);
1650
+
1651
+ // Only update styles if the user draged one pixel or more
1652
+ if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
1653
+ // Constrain proportions
1654
+ width = Math.round(startW * ratio);
1655
+ height = Math.round(startH * ratio);
1656
+
1657
+ // Resize by using style or attribute
1658
+ if (mouseDownImg.style.width) {
1659
+ dom.setStyle(mouseDownImg, 'width', width);
1660
+ } else {
1661
+ dom.setAttrib(mouseDownImg, 'width', width);
1662
+ }
1663
+
1664
+ // Resize by using style or attribute
1665
+ if (mouseDownImg.style.height) {
1666
+ dom.setStyle(mouseDownImg, 'height', height);
1667
+ } else {
1668
+ dom.setAttrib(mouseDownImg, 'height', height);
1669
+ }
1670
+
1671
+ if (!dom.hasClass(editor.getBody(), 'mceResizeImages')) {
1672
+ dom.addClass(editor.getBody(), 'mceResizeImages');
1673
+ }
1674
+ }
1675
+ }
1676
+ };
1677
+
1678
+ editor.onMouseDown.add(function(editor, e) {
1679
+ var target = e.target;
1680
+
1681
+ if (target.nodeName == "IMG") {
1682
+ mouseDownImg = target;
1683
+ startX = e.screenX;
1684
+ startY = e.screenY;
1685
+ startW = mouseDownImg.clientWidth;
1686
+ startH = mouseDownImg.clientHeight;
1687
+ dom.bind(editor.getDoc(), 'mousemove', resizeImage);
1614
1688
  e.preventDefault();
1615
- editor.undoManager.beforeChange();
1616
- dom.remove(selection.getNode());
1617
- editor.undoManager.add();
1689
+ }
1690
+ });
1691
+
1692
+ // Unbind events on node change and restore resize cursor
1693
+ editor.onNodeChange.add(function() {
1694
+ if (mouseDownImg) {
1695
+ mouseDownImg = null;
1696
+ dom.unbind(editor.getDoc(), 'mousemove', resizeImage);
1697
+ }
1698
+
1699
+ if (selection.getNode().nodeName == "IMG") {
1700
+ dom.addClass(editor.getBody(), 'mceResizeImages');
1701
+ } else {
1702
+ dom.removeClass(editor.getBody(), 'mceResizeImages');
1618
1703
  }
1619
1704
  });
1620
1705
  };
@@ -1635,6 +1720,9 @@ tinymce.util.Quirks = function(editor) {
1635
1720
  // iOS
1636
1721
  if (tinymce.isIDevice) {
1637
1722
  selectionChangeNodeChanged();
1723
+ } else {
1724
+ fakeImageResize();
1725
+ selectAll();
1638
1726
  }
1639
1727
  }
1640
1728
 
@@ -1644,7 +1732,8 @@ tinymce.util.Quirks = function(editor) {
1644
1732
  ensureBodyHasRoleApplication();
1645
1733
  addNewLinesBeforeBrInPre();
1646
1734
  removePreSerializedStylesWhenSelectingControls();
1647
- deleteImageOnBackSpace();
1735
+ deleteControlItemOnBackSpace();
1736
+ renderEmptyBlocksFix();
1648
1737
  }
1649
1738
 
1650
1739
  // Gecko
@@ -2107,9 +2196,9 @@ tinymce.html.Styles = function(settings, schema) {
2107
2196
 
2108
2197
  if (!html5) {
2109
2198
  html5 = mapCache.html5 = unpack({
2110
- A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title',
2199
+ A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
2111
2200
  B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|' +
2112
- 'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video',
2201
+ 'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr',
2113
2202
  C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|' +
2114
2203
  'figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|' +
2115
2204
  'p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
@@ -2218,7 +2307,8 @@ tinymce.html.Styles = function(settings, schema) {
2218
2307
  'tbody[A][tr]' +
2219
2308
  'tr[A][th|td]' +
2220
2309
  'th[A|headers|rowspan|colspan|scope][B]' +
2221
- 'td[A|headers|rowspan|colspan][C]'
2310
+ 'td[A|headers|rowspan|colspan][C]' +
2311
+ 'wbr[A][]'
2222
2312
  );
2223
2313
  }
2224
2314
 
@@ -2399,7 +2489,7 @@ tinymce.html.Styles = function(settings, schema) {
2399
2489
  // Setup map objects
2400
2490
  whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script style textarea');
2401
2491
  selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
2402
- shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source');
2492
+ shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source wbr');
2403
2493
  boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls');
2404
2494
  nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object', shortEndedElementsMap);
2405
2495
  blockElementsMap = createLookupTable('block_elements', 'h1 h2 h3 h4 h5 h6 hr p div address pre form table tbody thead tfoot ' +
@@ -4657,7 +4747,7 @@ tinymce.dom = {};
4657
4747
 
4658
4748
  // Old API supported multiple targets
4659
4749
  if (target && target instanceof Array) {
4660
- var i = target;
4750
+ var i = target.length;
4661
4751
 
4662
4752
  while (i--) {
4663
4753
  self.add(target[i], events, func, scope);
@@ -5499,6 +5589,32 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
5499
5589
  return this.styles.serialize(o, name);
5500
5590
  },
5501
5591
 
5592
+ addStyle: function(cssText) {
5593
+ var doc = this.doc, head;
5594
+
5595
+ // Create style element if needed
5596
+ styleElm = doc.getElementById('mceDefaultStyles');
5597
+ if (!styleElm) {
5598
+ styleElm = doc.createElement('style'),
5599
+ styleElm.id = 'mceDefaultStyles';
5600
+ styleElm.type = 'text/css';
5601
+
5602
+ head = doc.getElementsByTagName('head')[0]
5603
+ if (head.firstChild) {
5604
+ head.insertBefore(styleElm, head.firstChild);
5605
+ } else {
5606
+ head.appendChild(styleElm);
5607
+ }
5608
+ }
5609
+
5610
+ // Append style data to old or new style element
5611
+ if (styleElm.styleSheet) {
5612
+ styleElm.styleSheet.cssText += cssText;
5613
+ } else {
5614
+ styleElm.appendChild(doc.createTextNode(cssText));
5615
+ }
5616
+ },
5617
+
5502
5618
  loadCSS : function(u) {
5503
5619
  var t = this, d = t.doc, head;
5504
5620
 
@@ -5622,13 +5738,13 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
5622
5738
  // This seems to fix this problem
5623
5739
 
5624
5740
  // Create new div with HTML contents and a BR infront to keep comments
5625
- element = self.create('div');
5626
- element.innerHTML = '<br />' + html;
5741
+ var newElement = self.create('div');
5742
+ newElement.innerHTML = '<br />' + html;
5627
5743
 
5628
5744
  // Add all children from div to target
5629
- each (element.childNodes, function(node, i) {
5745
+ each (tinymce.grep(newElement.childNodes), function(node, i) {
5630
5746
  // Skip br element
5631
- if (i)
5747
+ if (i && element.canHaveHTML)
5632
5748
  element.appendChild(node);
5633
5749
  });
5634
5750
  }
@@ -9069,7 +9185,7 @@ window.tinymce.dom.Sizzle = Sizzle;
9069
9185
  },
9070
9186
 
9071
9187
  getStart : function() {
9072
- var rng = this.getRng(), startElement, parentElement, checkRng, node;
9188
+ var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
9073
9189
 
9074
9190
  if (rng.duplicate || rng.item) {
9075
9191
  // Control selection, return first item
@@ -9080,6 +9196,9 @@ window.tinymce.dom.Sizzle = Sizzle;
9080
9196
  checkRng = rng.duplicate();
9081
9197
  checkRng.collapse(1);
9082
9198
  startElement = checkRng.parentElement();
9199
+ if (startElement.ownerDocument !== self.dom.doc) {
9200
+ startElement = self.dom.getRoot();
9201
+ }
9083
9202
 
9084
9203
  // Check if range parent is inside the start element, then return the inner parent element
9085
9204
  // This will fix issues when a single element is selected, IE would otherwise return the wrong start element
@@ -9106,31 +9225,34 @@ window.tinymce.dom.Sizzle = Sizzle;
9106
9225
  },
9107
9226
 
9108
9227
  getEnd : function() {
9109
- var t = this, r = t.getRng(), e, eo;
9228
+ var self = this, rng = self.getRng(), endElement, endOffset;
9110
9229
 
9111
- if (r.duplicate || r.item) {
9112
- if (r.item)
9113
- return r.item(0);
9230
+ if (rng.duplicate || rng.item) {
9231
+ if (rng.item)
9232
+ return rng.item(0);
9114
9233
 
9115
- r = r.duplicate();
9116
- r.collapse(0);
9117
- e = r.parentElement();
9234
+ rng = rng.duplicate();
9235
+ rng.collapse(0);
9236
+ endElement = rng.parentElement();
9237
+ if (endElement.ownerDocument !== self.dom.doc) {
9238
+ endElement = self.dom.getRoot();
9239
+ }
9118
9240
 
9119
- if (e && e.nodeName == 'BODY')
9120
- return e.lastChild || e;
9241
+ if (endElement && endElement.nodeName == 'BODY')
9242
+ return endElement.lastChild || endElement;
9121
9243
 
9122
- return e;
9244
+ return endElement;
9123
9245
  } else {
9124
- e = r.endContainer;
9125
- eo = r.endOffset;
9246
+ endElement = rng.endContainer;
9247
+ endOffset = rng.endOffset;
9126
9248
 
9127
- if (e.nodeType == 1 && e.hasChildNodes())
9128
- e = e.childNodes[eo > 0 ? eo - 1 : eo];
9249
+ if (endElement.nodeType == 1 && endElement.hasChildNodes())
9250
+ endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
9129
9251
 
9130
- if (e && e.nodeType == 3)
9131
- return e.parentNode;
9252
+ if (endElement && endElement.nodeType == 3)
9253
+ return endElement.parentNode;
9132
9254
 
9133
- return e;
9255
+ return endElement;
9134
9256
  }
9135
9257
  },
9136
9258
 
@@ -11475,7 +11597,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
11475
11597
  l = DOM.encode(s.label || '');
11476
11598
  h = '<a role="button" id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" aria-labelledby="' + this.id + '_voice" title="' + DOM.encode(s.title) + '">';
11477
11599
  if (s.image && !(this.editor &&this.editor.forcedHighContrastMode) )
11478
- h += '<img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" />' + l;
11600
+ h += '<span class="mceIcon ' + s['class'] + '"><img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" /></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
11479
11601
  else
11480
11602
  h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
11481
11603
 
@@ -12183,6 +12305,16 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
12183
12305
  DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
12184
12306
  }
12185
12307
 
12308
+ t.keyboardNav = new tinymce.ui.KeyboardNavigation({
12309
+ root: t.id + '_menu',
12310
+ items: DOM.select('a', t.id + '_menu'),
12311
+ onCancel: function() {
12312
+ t.hideMenu();
12313
+ t.focus();
12314
+ }
12315
+ });
12316
+
12317
+ t.keyboardNav.focus();
12186
12318
  t.isMenuVisible = 1;
12187
12319
  },
12188
12320
 
@@ -12203,6 +12335,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
12203
12335
 
12204
12336
  t.isMenuVisible = 0;
12205
12337
  t.onHideMenu.dispatch();
12338
+ t.keyboardNav.destroy();
12206
12339
  }
12207
12340
  },
12208
12341
 
@@ -12267,15 +12400,6 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
12267
12400
  }
12268
12401
 
12269
12402
  DOM.addClass(m, 'mceColorSplitMenu');
12270
-
12271
- new tinymce.ui.KeyboardNavigation({
12272
- root: t.id + '_menu',
12273
- items: DOM.select('a', t.id + '_menu'),
12274
- onCancel: function() {
12275
- t.hideMenu();
12276
- t.focus();
12277
- }
12278
- });
12279
12403
 
12280
12404
  // Prevent IE from scrolling and hindering click to occur #4019
12281
12405
  Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});
@@ -12317,11 +12441,17 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
12317
12441
  },
12318
12442
 
12319
12443
  destroy : function() {
12320
- this.parent();
12444
+ var self = this;
12321
12445
 
12322
- Event.clear(this.id + '_menu');
12323
- Event.clear(this.id + '_more');
12324
- DOM.remove(this.id + '_menu');
12446
+ self.parent();
12447
+
12448
+ Event.clear(self.id + '_menu');
12449
+ Event.clear(self.id + '_more');
12450
+ DOM.remove(self.id + '_menu');
12451
+
12452
+ if (self.keyboardNav) {
12453
+ self.keyboardNav.destroy();
12454
+ }
12325
12455
  }
12326
12456
  });
12327
12457
  })(tinymce);
@@ -12965,6 +13095,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12965
13095
 
12966
13096
  self.contentCSS = [];
12967
13097
 
13098
+ self.contentStyles = [];
13099
+
12968
13100
  // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
12969
13101
  self.setupEvents();
12970
13102
 
@@ -13156,12 +13288,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13156
13288
 
13157
13289
  t.controlManager = new tinymce.ControlManager(t);
13158
13290
 
13159
- t.onExecCommand.add(function(ed, c) {
13160
- // Don't refresh the select lists until caret move
13161
- if (!/^(FontName|FontSize)$/.test(c))
13162
- t.nodeChanged();
13163
- });
13164
-
13165
13291
  // Enables users to override the control factory
13166
13292
  t.onBeforeRenderUI.dispatch(t, t.controlManager);
13167
13293
 
@@ -13321,7 +13447,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13321
13447
  },
13322
13448
 
13323
13449
  initContentBody : function() {
13324
- var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body;
13450
+ var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body, contentCssText;
13325
13451
 
13326
13452
  // Setup iframe body
13327
13453
  if ((!isIE || !tinymce.relaxedDomain) && !settings.content_editable) {
@@ -13430,6 +13556,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13430
13556
  self.enterKey = new tinymce.EnterKey(self);
13431
13557
  self.editorCommands = new tinymce.EditorCommands(self);
13432
13558
 
13559
+ self.onExecCommand.add(function(editor, command) {
13560
+ // Don't refresh the select lists until caret move
13561
+ if (!/^(FontName|FontSize)$/.test(command))
13562
+ self.nodeChanged();
13563
+ });
13564
+
13433
13565
  // Pass through
13434
13566
  self.serializer.onPreProcess.add(function(se, o) {
13435
13567
  return self.onPreProcess.dispatch(self, o, se);
@@ -13492,6 +13624,17 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13492
13624
  self.focus(true);
13493
13625
  self.nodeChanged({initial : true});
13494
13626
 
13627
+ // Add editor specific CSS styles
13628
+ if (self.contentStyles.length > 0) {
13629
+ contentCssText = '';
13630
+
13631
+ each(self.contentStyles, function(style) {
13632
+ contentCssText += style + "\r\n";
13633
+ });
13634
+
13635
+ self.dom.addStyle(contentCssText);
13636
+ }
13637
+
13495
13638
  // Load specified content CSS last
13496
13639
  each(self.contentCSS, function(url) {
13497
13640
  self.dom.loadCSS(url);
@@ -14119,7 +14262,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14119
14262
  return;
14120
14263
 
14121
14264
  case 'A':
14122
- if (!elm.href) {
14265
+ if (!dom.getAttrib(elm, 'href', false)) {
14123
14266
  value = dom.getAttrib(elm, 'name') || elm.id;
14124
14267
  cls = 'mceItemAnchor';
14125
14268
 
@@ -14148,13 +14291,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14148
14291
  // Don't clear the window or document if content editable
14149
14292
  // is enabled since other instances might still be present
14150
14293
  if (!self.settings.content_editable) {
14151
- Event.clear(self.getWin());
14152
- Event.clear(self.getDoc());
14294
+ Event.unbind(self.getWin());
14295
+ Event.unbind(self.getDoc());
14153
14296
  }
14154
14297
 
14155
- Event.clear(self.getBody());
14156
- Event.clear(self.formElement);
14157
- Event.unbind(elm);
14298
+ Event.unbind(self.getBody());
14299
+ Event.clear(elm);
14158
14300
 
14159
14301
  self.execCallback('remove_instance_callback', self);
14160
14302
  self.onRemove.dispatch(self);
@@ -14356,8 +14498,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14356
14498
  // Handle legacy handle_event_callback option
14357
14499
  if (settings.handle_event_callback) {
14358
14500
  self.onEvent.add(function(ed, e, o) {
14359
- if (self.execCallback('handle_event_callback', e, ed, o) === false)
14360
- Event.cancel(e);
14501
+ if (self.execCallback('handle_event_callback', e, ed, o) === false) {
14502
+ e.preventDefault();
14503
+ e.stopPropagation();
14504
+ }
14361
14505
  });
14362
14506
  }
14363
14507
 
@@ -14423,9 +14567,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14423
14567
  self.focus(true);
14424
14568
  };
14425
14569
 
14426
- function nodeChanged() {
14427
- // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i>
14428
- self.selection.normalize();
14570
+ function nodeChanged(ed, e) {
14571
+ // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
14572
+ if (e.keyCode != 65 || !tinymce.VK.metaKeyPressed(e)) {
14573
+ self.selection.normalize();
14574
+ }
14575
+
14429
14576
  self.nodeChanged();
14430
14577
  }
14431
14578
 
@@ -14469,7 +14616,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14469
14616
  var keyCode = e.keyCode;
14470
14617
 
14471
14618
  if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey)
14472
- nodeChanged();
14619
+ nodeChanged(ed, e);
14473
14620
  });
14474
14621
 
14475
14622
  // Add reset handler
@@ -14896,6 +15043,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14896
15043
  editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));
14897
15044
  },
14898
15045
 
15046
+ mceToggleFormat : function(command, ui, value) {
15047
+ toggleFormat(value);
15048
+ },
15049
+
14899
15050
  mceSetContent : function(command, ui, value) {
14900
15051
  editor.setContent(value);
14901
15052
  },
@@ -15284,7 +15435,7 @@ tinymce.ForceBlocks = function(editor) {
15284
15435
  var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();
15285
15436
 
15286
15437
  function addRootBlocks() {
15287
- var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped;
15438
+ var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped, isInEditorDocument;
15288
15439
 
15289
15440
  if (!node || node.nodeType !== 1 || !settings.forced_root_block)
15290
15441
  return;
@@ -15312,6 +15463,7 @@ tinymce.ForceBlocks = function(editor) {
15312
15463
  rng.moveToElementText(node);
15313
15464
  }
15314
15465
 
15466
+ isInEditorDocument = rng.parentElement().ownerDocument === editor.getDoc();
15315
15467
  tmpRng = rng.duplicate();
15316
15468
  tmpRng.collapse(true);
15317
15469
  startOffset = tmpRng.move('character', offset) * -1;
@@ -15342,28 +15494,30 @@ tinymce.ForceBlocks = function(editor) {
15342
15494
  }
15343
15495
  }
15344
15496
 
15345
- if (rng.setStart) {
15346
- rng.setStart(startContainer, startOffset);
15347
- rng.setEnd(endContainer, endOffset);
15348
- selection.setRng(rng);
15349
- } else {
15350
- try {
15351
- rng = editor.getDoc().body.createTextRange();
15352
- rng.moveToElementText(rootNode);
15353
- rng.collapse(true);
15354
- rng.moveStart('character', startOffset);
15497
+ if (wrapped) {
15498
+ if (rng.setStart) {
15499
+ rng.setStart(startContainer, startOffset);
15500
+ rng.setEnd(endContainer, endOffset);
15501
+ selection.setRng(rng);
15502
+ } else {
15503
+ // Only select if the previous selection was inside the document to prevent auto focus in quirks mode
15504
+ if (isInEditorDocument) {
15505
+ try {
15506
+ rng = editor.getDoc().body.createTextRange();
15507
+ rng.moveToElementText(rootNode);
15508
+ rng.collapse(true);
15509
+ rng.moveStart('character', startOffset);
15355
15510
 
15356
- if (endOffset > 0)
15357
- rng.moveEnd('character', endOffset);
15511
+ if (endOffset > 0)
15512
+ rng.moveEnd('character', endOffset);
15358
15513
 
15359
- rng.select();
15360
- } catch (ex) {
15361
- // Ignore
15514
+ rng.select();
15515
+ } catch (ex) {
15516
+ // Ignore
15517
+ }
15518
+ }
15362
15519
  }
15363
- }
15364
15520
 
15365
- // Only trigger nodeChange when we wrapped nodes to prevent a forever loop
15366
- if (wrapped) {
15367
15521
  editor.nodeChanged();
15368
15522
  }
15369
15523
  };
@@ -17949,7 +18103,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
17949
18103
  var TreeWalker = tinymce.dom.TreeWalker;
17950
18104
 
17951
18105
  tinymce.EnterKey = function(editor) {
17952
- var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager;
18106
+ var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager, nonEmptyElementsMap = editor.schema.getNonEmptyElements();
17953
18107
 
17954
18108
  function handleEnterKey(evt) {
17955
18109
  var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode,
@@ -17959,11 +18113,48 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
17959
18113
  function canSplitBlock(node) {
17960
18114
  return node &&
17961
18115
  dom.isBlock(node) &&
17962
- !/^(TD|TH|CAPTION)$/.test(node.nodeName) &&
18116
+ !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
17963
18117
  !/^(fixed|absolute)/i.test(node.style.position) &&
17964
18118
  dom.getContentEditable(node) !== "true";
17965
18119
  };
17966
18120
 
18121
+ // Renders empty block on IE
18122
+ function renderBlockOnIE(block) {
18123
+ var oldRng;
18124
+
18125
+ if (tinymce.isIE && dom.isBlock(block)) {
18126
+ oldRng = selection.getRng();
18127
+ block.appendChild(dom.create('span', null, '\u00a0'));
18128
+ selection.select(block);
18129
+ block.lastChild.outerHTML = '';
18130
+ selection.setRng(oldRng);
18131
+ }
18132
+ };
18133
+
18134
+ // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
18135
+ function trimInlineElementsOnLeftSideOfBlock(block) {
18136
+ var node = block, firstChilds = [], i;
18137
+
18138
+ // Find inner most first child ex: <p><i><b>*</b></i></p>
18139
+ while (node = node.firstChild) {
18140
+ if (dom.isBlock(node)) {
18141
+ return;
18142
+ }
18143
+
18144
+ if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
18145
+ firstChilds.push(node);
18146
+ }
18147
+ }
18148
+
18149
+ i = firstChilds.length;
18150
+ while (i--) {
18151
+ node = firstChilds[i];
18152
+ if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
18153
+ dom.remove(node);
18154
+ }
18155
+ }
18156
+ };
18157
+
17967
18158
  // Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
17968
18159
  function moveToCaretPosition(root) {
17969
18160
  var walker, node, rng, y, viewPort, lastNode = root, tempElm;
@@ -17980,7 +18171,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
17980
18171
  break;
17981
18172
  }
17982
18173
 
17983
- if (/^(BR|IMG)$/.test(node.nodeName)) {
18174
+ if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
17984
18175
  rng.setStartBefore(node);
17985
18176
  rng.setEndBefore(node);
17986
18177
  break;
@@ -18077,6 +18268,11 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18077
18268
  return true;
18078
18269
  }
18079
18270
 
18271
+ // If the caret if before the first element in parentBlock
18272
+ if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
18273
+ return true;
18274
+ }
18275
+
18080
18276
  // Caret can be before/after a table
18081
18277
  if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
18082
18278
  return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
@@ -18084,21 +18280,35 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18084
18280
 
18085
18281
  // Walk the DOM and look for text nodes or non empty elements
18086
18282
  walker = new TreeWalker(container, parentBlock);
18087
- while (node = (start ? walker.prev() : walker.next())) {
18283
+
18284
+ // If caret is in beginning or end of a text block then jump to the next/previous node
18285
+ if (container.nodeType == 3) {
18286
+ if (start && offset == 0) {
18287
+ walker.prev();
18288
+ } else if (!start && offset == container.nodeValue.length) {
18289
+ walker.next();
18290
+ }
18291
+ }
18292
+
18293
+ while (node = walker.current()) {
18088
18294
  if (node.nodeType === 1) {
18089
18295
  // Ignore bogus elements
18090
- if (node.getAttribute('data-mce-bogus')) {
18091
- continue;
18092
- }
18093
-
18094
- // Keep empty elements like <img />
18095
- name = node.nodeName.toLowerCase();
18096
- if (name === 'IMG') {
18097
- return false;
18296
+ if (!node.getAttribute('data-mce-bogus')) {
18297
+ // Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
18298
+ name = node.nodeName.toLowerCase();
18299
+ if (nonEmptyElementsMap[name] && name !== 'br') {
18300
+ return false;
18301
+ }
18098
18302
  }
18099
18303
  } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
18100
18304
  return false;
18101
18305
  }
18306
+
18307
+ if (start) {
18308
+ walker.prev();
18309
+ } else {
18310
+ walker.next();
18311
+ }
18102
18312
  }
18103
18313
 
18104
18314
  return true;
@@ -18182,6 +18392,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18182
18392
  } else if (isFirstOrLastLi()) {
18183
18393
  // Last LI in list then temove LI and add text block after list
18184
18394
  dom.insertAfter(newBlock, containerBlock);
18395
+ renderBlockOnIE(newBlock);
18185
18396
  } else {
18186
18397
  // Middle LI in list the split the list and insert a text block in the middle
18187
18398
  // Extract after fragment and insert it after the current block
@@ -18273,6 +18484,22 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18273
18484
  return parent !== root ? editableRoot : root;
18274
18485
  };
18275
18486
 
18487
+ // Adds a BR at the end of blocks that only contains an IMG or INPUT since these might be floated and then they won't expand the block
18488
+ function addBrToBlockIfNeeded(block) {
18489
+ var lastChild;
18490
+
18491
+ // IE will render the blocks correctly other browsers needs a BR
18492
+ if (!tinymce.isIE) {
18493
+ block.normalize(); // Remove empty text nodes that got left behind by the extract
18494
+
18495
+ // Check if the block is empty or contains a floated last child
18496
+ lastChild = block.lastChild;
18497
+ if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
18498
+ dom.add(block, 'br');
18499
+ }
18500
+ }
18501
+ };
18502
+
18276
18503
  // Delete any selected contents
18277
18504
  if (!rng.collapsed) {
18278
18505
  editor.execCommand('Delete');
@@ -18295,7 +18522,11 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18295
18522
  if (container.nodeType == 1 && container.hasChildNodes()) {
18296
18523
  isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
18297
18524
  container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
18298
- offset = 0;
18525
+ if (isAfterLastNodeInContainer && container.nodeType == 3) {
18526
+ offset = container.nodeValue.length;
18527
+ } else {
18528
+ offset = 0;
18529
+ }
18299
18530
  }
18300
18531
 
18301
18532
  // Get editable root node normaly the body element but sometimes a div or span
@@ -18376,9 +18607,12 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18376
18607
  } else {
18377
18608
  dom.insertAfter(newBlock, parentBlock);
18378
18609
  }
18610
+
18611
+ moveToCaretPosition(newBlock);
18379
18612
  } else if (isCaretAtStartOrEndOfBlock(true)) {
18380
18613
  // Insert new block before
18381
18614
  newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
18615
+ renderBlockOnIE(newBlock);
18382
18616
  } else {
18383
18617
  // Extract after fragment and insert it after the current block
18384
18618
  tmpRng = rng.cloneRange();
@@ -18387,10 +18621,12 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
18387
18621
  trimLeadingLineBreaks(fragment);
18388
18622
  newBlock = fragment.firstChild;
18389
18623
  dom.insertAfter(fragment, parentBlock);
18624
+ trimInlineElementsOnLeftSideOfBlock(newBlock);
18625
+ addBrToBlockIfNeeded(parentBlock);
18626
+ moveToCaretPosition(newBlock);
18390
18627
  }
18391
18628
 
18392
18629
  dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
18393
- moveToCaretPosition(newBlock);
18394
18630
  undoManager.add();
18395
18631
  }
18396
18632