tinymce-rails 3.5.2 → 3.5.4.1

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 (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