tinymce-rails 3.4.4.0.2 → 3.4.5

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 (46) hide show
  1. data/assets/precompiled/tinymce/langs/en.js +1 -223
  2. data/assets/precompiled/tinymce/plugins/advhr/langs/en_dlg.js +1 -7
  3. data/assets/precompiled/tinymce/plugins/advimage/js/image.js +7 -3
  4. data/assets/precompiled/tinymce/plugins/advimage/langs/en_dlg.js +1 -45
  5. data/assets/precompiled/tinymce/plugins/advlink/langs/en_dlg.js +1 -54
  6. data/assets/precompiled/tinymce/plugins/autolink/editor_plugin.js +1 -1
  7. data/assets/precompiled/tinymce/plugins/autolink/editor_plugin_src.js +1 -1
  8. data/assets/precompiled/tinymce/plugins/emotions/langs/en_dlg.js +1 -20
  9. data/assets/precompiled/tinymce/plugins/fullpage/editor_plugin.js +1 -1
  10. data/assets/precompiled/tinymce/plugins/fullpage/editor_plugin_src.js +9 -3
  11. data/assets/precompiled/tinymce/plugins/fullpage/langs/en_dlg.js +1 -85
  12. data/assets/precompiled/tinymce/plugins/fullscreen/editor_plugin.js +1 -1
  13. data/assets/precompiled/tinymce/plugins/fullscreen/editor_plugin_src.js +2 -2
  14. data/assets/precompiled/tinymce/plugins/fullscreen/fullscreen.htm +2 -1
  15. data/assets/precompiled/tinymce/plugins/lists/editor_plugin.js +1 -1
  16. data/assets/precompiled/tinymce/plugins/lists/editor_plugin_src.js +12 -0
  17. data/assets/precompiled/tinymce/plugins/media/langs/en_dlg.js +1 -112
  18. data/assets/precompiled/tinymce/plugins/media/media.htm +1 -1
  19. data/assets/precompiled/tinymce/plugins/nonbreaking/editor_plugin.js +1 -1
  20. data/assets/precompiled/tinymce/plugins/nonbreaking/editor_plugin_src.js +3 -2
  21. data/assets/precompiled/tinymce/plugins/paste/editor_plugin.js +1 -1
  22. data/assets/precompiled/tinymce/plugins/paste/editor_plugin_src.js +21 -102
  23. data/assets/precompiled/tinymce/plugins/paste/langs/en_dlg.js +1 -5
  24. data/assets/precompiled/tinymce/plugins/searchreplace/langs/en_dlg.js +1 -16
  25. data/assets/precompiled/tinymce/plugins/style/langs/en_dlg.js +1 -70
  26. data/assets/precompiled/tinymce/plugins/table/editor_plugin.js +1 -1
  27. data/assets/precompiled/tinymce/plugins/table/editor_plugin_src.js +3 -2
  28. data/assets/precompiled/tinymce/plugins/table/langs/en_dlg.js +1 -75
  29. data/assets/precompiled/tinymce/plugins/template/langs/en_dlg.js +1 -15
  30. data/assets/precompiled/tinymce/plugins/xhtmlxtras/langs/en_dlg.js +1 -32
  31. data/assets/precompiled/tinymce/themes/advanced/js/image.js +7 -3
  32. data/assets/precompiled/tinymce/themes/advanced/langs/en.js +1 -68
  33. data/assets/precompiled/tinymce/themes/advanced/langs/en_dlg.js +1 -54
  34. data/assets/precompiled/tinymce/themes/advanced/skins/default/content.css +1 -0
  35. data/assets/precompiled/tinymce/themes/advanced/skins/highcontrast/content.css +1 -0
  36. data/assets/precompiled/tinymce/themes/advanced/skins/o2k7/content.css +1 -0
  37. data/assets/precompiled/tinymce/themes/simple/langs/en.js +1 -11
  38. data/assets/precompiled/tinymce/tiny_mce.js +1 -1
  39. data/assets/precompiled/tinymce/tiny_mce_jquery.js +1 -1
  40. data/assets/precompiled/tinymce/tiny_mce_jquery_src.js +269 -171
  41. data/assets/precompiled/tinymce/tiny_mce_src.js +269 -171
  42. data/assets/precompiled/tinymce/utils/editable_selects.js +1 -1
  43. data/assets/vendor/tinymce/tiny_mce.js +269 -171
  44. data/assets/vendor/tinymce/tiny_mce_jquery.js +269 -171
  45. data/lib/tinymce/version.rb +2 -2
  46. metadata +6 -6
@@ -16,7 +16,7 @@ var TinyMCE_EditableSelects = {
16
16
 
17
17
  for (i=0; i<nl.length; i++) {
18
18
  if (nl[i].className.indexOf('mceEditableSelect') != -1) {
19
- o = new Option('(value)', '__mce_add_custom__');
19
+ o = new Option(tinyMCEPopup.editor.translate('value'), '__mce_add_custom__');
20
20
 
21
21
  o.className = 'mceAddSelectValue';
22
22
 
@@ -5,9 +5,9 @@
5
5
  var tinymce = {
6
6
  majorVersion : '3',
7
7
 
8
- minorVersion : '4.4',
8
+ minorVersion : '4.5',
9
9
 
10
- releaseDate : '2011-08-04',
10
+ releaseDate : '2011-09-06',
11
11
 
12
12
  _init : function() {
13
13
  var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -559,8 +559,9 @@ tinymce.create('tinymce.util.Dispatcher', {
559
559
  // Default settings
560
560
  s = t.settings = s || {};
561
561
 
562
- // Strange app protocol or local anchor
563
- if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
562
+ // Strange app protocol that isn't http/https or local anchor
563
+ // For example: mailto,skype,tel etc.
564
+ if (/^([\w\-]+):([^\/]{2})/i.test(u) || /^\s*#/.test(u)) {
564
565
  t.source = u;
565
566
  return;
566
567
  }
@@ -1045,31 +1046,89 @@ tinymce.create('static tinymce.util.XHR', {
1045
1046
  })(tinymce);
1046
1047
 
1047
1048
  (function(tinymce) {
1049
+ var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;
1050
+
1048
1051
  function cleanupStylesWhenDeleting(ed) {
1049
- var dom = ed.dom, selection = ed.selection, VK= tinymce.VK;
1050
- ed.onKeyUp.add(function(ed, e) {
1051
- if (e.keyCode == VK.DELETE ||e.keyCode == VK.BACKSPACE) {
1052
- var startContainer = selection.getRng().startContainer;
1053
- var blockElement = startContainer;
1054
- while (!dom.isBlock(blockElement)) {
1055
- blockElement = blockElement.parentNode;
1052
+ var dom = ed.dom, selection = ed.selection;
1053
+
1054
+ ed.onKeyDown.add(function(ed, e) {
1055
+ var rng, blockElm, node, clonedSpan, isDelete;
1056
+
1057
+ isDelete = e.keyCode == DELETE;
1058
+ if (isDelete || e.keyCode == BACKSPACE) {
1059
+ e.preventDefault();
1060
+ rng = selection.getRng();
1061
+
1062
+ // Find root block
1063
+ blockElm = dom.getParent(rng.startContainer, dom.isBlock);
1064
+
1065
+ // On delete clone the root span of the next block element
1066
+ if (isDelete)
1067
+ blockElm = dom.getNext(blockElm, dom.isBlock);
1068
+
1069
+ // Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace
1070
+ if (blockElm) {
1071
+ node = blockElm.firstChild;
1072
+
1073
+ if (node && node.nodeName === 'SPAN') {
1074
+ clonedSpan = node.cloneNode(false);
1056
1075
  }
1057
- var spans = dom.select("span.Apple-style-span", blockElement);
1058
- dom.remove(spans, true);
1059
1076
  }
1060
- });
1061
- }
1062
1077
 
1078
+ // Do the backspace/delete actiopn
1079
+ ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
1080
+
1081
+ // Find all odd apple-style-spans
1082
+ blockElm = dom.getParent(rng.startContainer, dom.isBlock);
1083
+ tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {
1084
+ var rng = dom.createRng();
1085
+
1086
+ // Set range selection before the span we are about to remove
1087
+ rng.setStartBefore(span);
1088
+ rng.setEndBefore(span);
1089
+
1090
+ if (clonedSpan) {
1091
+ dom.replace(clonedSpan.cloneNode(false), span, true);
1092
+ } else {
1093
+ dom.remove(span, true);
1094
+ }
1095
+
1096
+ // Restore the selection
1097
+ selection.setRng(rng);
1098
+ });
1099
+ }
1100
+ });
1101
+ };
1102
+
1103
+ function emptyEditorWhenDeleting(ed) {
1104
+ ed.onKeyUp.add(function(ed, e) {
1105
+ var keyCode = e.keyCode;
1106
+
1107
+ if (keyCode == DELETE || keyCode == BACKSPACE) {
1108
+ if (ed.dom.isEmpty(ed.getBody())) {
1109
+ ed.setContent('', {format : 'raw'});
1110
+ ed.nodeChanged();
1111
+ return;
1112
+ }
1113
+ }
1114
+ });
1115
+ };
1116
+
1063
1117
  tinymce.create('tinymce.util.Quirks', {
1064
1118
  Quirks: function(ed) {
1119
+ // Load WebKit specific fixed
1065
1120
  if (tinymce.isWebKit) {
1066
1121
  cleanupStylesWhenDeleting(ed);
1122
+ emptyEditorWhenDeleting(ed);
1123
+ }
1124
+
1125
+ // Load IE specific fixes
1126
+ if (tinymce.isIE) {
1127
+ emptyEditorWhenDeleting(ed);
1067
1128
  }
1068
-
1069
1129
  }
1070
1130
  });
1071
- })(tinymce);
1072
-
1131
+ })(tinymce);
1073
1132
  (function(tinymce) {
1074
1133
  var namedEntities, baseEntities, reverseEntities,
1075
1134
  attrsCharsRegExp = /[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
@@ -1481,7 +1540,7 @@ tinymce.html.Styles = function(settings, schema) {
1481
1540
 
1482
1541
  (function(tinymce) {
1483
1542
  var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap, customElementsMap = {},
1484
- whiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
1543
+ defaultWhiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
1485
1544
 
1486
1545
  function split(str, delim) {
1487
1546
  return str.split(delim || ',');
@@ -1645,11 +1704,11 @@ tinymce.html.Styles = function(settings, schema) {
1645
1704
  boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls');
1646
1705
  shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');
1647
1706
  nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,audio,object'), shortEndedElementsMap);
1648
- whiteSpaceElementsMap = makeMap('pre,script,style,textarea');
1707
+ defaultWhiteSpaceElementsMap = makeMap('pre,script,style,textarea');
1649
1708
  selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
1650
1709
 
1651
1710
  tinymce.html.Schema = function(settings) {
1652
- var self = this, elements = {}, children = {}, patternElements = [], validStyles;
1711
+ var self = this, elements = {}, children = {}, patternElements = [], validStyles, whiteSpaceElementsMap;
1653
1712
 
1654
1713
  settings = settings || {};
1655
1714
 
@@ -1667,6 +1726,8 @@ tinymce.html.Styles = function(settings, schema) {
1667
1726
  });
1668
1727
  }
1669
1728
 
1729
+ whiteSpaceElementsMap = settings.whitespace_elements ? makeMap(settings.whitespace_elements) : defaultWhiteSpaceElementsMap;
1730
+
1670
1731
  // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
1671
1732
  function patternToRegExp(str) {
1672
1733
  return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
@@ -2021,9 +2082,9 @@ tinymce.html.Styles = function(settings, schema) {
2021
2082
 
2022
2083
  self.parse = function(html) {
2023
2084
  var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name, isInternalElement, removeInternalElements,
2024
- shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue,
2085
+ shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue, invalidPrefixRegExp,
2025
2086
  validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,
2026
- tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing;
2087
+ tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing, isIE;
2027
2088
 
2028
2089
  function processEndTag(name) {
2029
2090
  var pos, i;
@@ -2074,6 +2135,8 @@ tinymce.html.Styles = function(settings, schema) {
2074
2135
  validate = settings.validate;
2075
2136
  removeInternalElements = settings.remove_internals;
2076
2137
  fixSelfClosing = settings.fix_self_closing;
2138
+ isIE = tinymce.isIE;
2139
+ invalidPrefixRegExp = /^:/;
2077
2140
 
2078
2141
  while (matches = tokenRegExp.exec(html)) {
2079
2142
  // Text
@@ -2081,9 +2144,20 @@ tinymce.html.Styles = function(settings, schema) {
2081
2144
  self.text(decode(html.substr(index, matches.index - index)));
2082
2145
 
2083
2146
  if (value = matches[6]) { // End element
2084
- processEndTag(value.toLowerCase());
2147
+ value = value.toLowerCase();
2148
+
2149
+ // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
2150
+ if (isIE && invalidPrefixRegExp.test(value))
2151
+ value = value.substr(1);
2152
+
2153
+ processEndTag(value);
2085
2154
  } else if (value = matches[7]) { // Start element
2086
2155
  value = value.toLowerCase();
2156
+
2157
+ // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
2158
+ if (isIE && invalidPrefixRegExp.test(value))
2159
+ value = value.substr(1);
2160
+
2087
2161
  isShortEnded = value in shortEndedElements;
2088
2162
 
2089
2163
  // Is self closing tag for example an <li> after an open <li>
@@ -3097,11 +3171,13 @@ tinymce.html.Styles = function(settings, schema) {
3097
3171
  elementRule = schema.getElementRule(parent.name);
3098
3172
 
3099
3173
  // Remove or padd the element depending on schema rule
3100
- if (elementRule.removeEmpty)
3101
- parent.remove();
3102
- else if (elementRule.paddEmpty)
3103
- parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
3104
- }
3174
+ if (elementRule) {
3175
+ if (elementRule.removeEmpty)
3176
+ parent.remove();
3177
+ else if (elementRule.paddEmpty)
3178
+ parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
3179
+ }
3180
+ }
3105
3181
  }
3106
3182
  }
3107
3183
  }
@@ -4399,7 +4475,7 @@ tinymce.html.Writer = function(settings) {
4399
4475
  },
4400
4476
 
4401
4477
  isEmpty : function(node, elements) {
4402
- var self = this, i, attributes, type, walker, name;
4478
+ var self = this, i, attributes, type, walker, name, parentNode;
4403
4479
 
4404
4480
  node = node.firstChild;
4405
4481
  if (node) {
@@ -4415,8 +4491,16 @@ tinymce.html.Writer = function(settings) {
4415
4491
  continue;
4416
4492
 
4417
4493
  // Keep empty elements like <img />
4418
- if (elements && elements[node.nodeName.toLowerCase()])
4494
+ name = node.nodeName.toLowerCase();
4495
+ if (elements && elements[name]) {
4496
+ // Ignore single BR elements in blocks like <p><br /></p>
4497
+ parentNode = node.parentNode;
4498
+ if (name === 'br' && self.isBlock(parentNode) && parentNode.firstChild === node && parentNode.lastChild === node) {
4499
+ continue;
4500
+ }
4501
+
4419
4502
  return false;
4503
+ }
4420
4504
 
4421
4505
  // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
4422
4506
  attributes = self.getAttribs(node);
@@ -7271,9 +7355,13 @@ window.tinymce.dom.Sizzle = Sizzle;
7271
7355
 
7272
7356
  if (n)
7273
7357
  e.appendChild(n);
7274
- } else if (is(r.item) || is(r.htmlText))
7275
- e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
7276
- else
7358
+ } else if (is(r.item) || is(r.htmlText)) {
7359
+ // IE will produce invalid markup if elements are present that
7360
+ // it doesn't understand like custom elements or HTML5 elements.
7361
+ // Adding a BR in front of the contents and then remoiving it seems to fix it though.
7362
+ e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText);
7363
+ e.removeChild(e.firstChild);
7364
+ } else
7277
7365
  e.innerHTML = r.toString();
7278
7366
 
7279
7367
  // Keep whitespace before and after
@@ -7358,7 +7446,12 @@ window.tinymce.dom.Sizzle = Sizzle;
7358
7446
  rng = self.getRng();
7359
7447
  }
7360
7448
 
7361
- rng.pasteHTML(content);
7449
+ // Explorer removes spaces from the beginning of pasted contents
7450
+ if (/^\s+/.test(content)) {
7451
+ rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
7452
+ self.dom.remove('__mce_tmp');
7453
+ } else
7454
+ rng.pasteHTML(content);
7362
7455
  }
7363
7456
 
7364
7457
  // Dispatch set content event
@@ -7965,26 +8058,29 @@ window.tinymce.dom.Sizzle = Sizzle;
7965
8058
  container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];
7966
8059
  offset = 0;
7967
8060
 
7968
- // Walk the DOM to find a text node to place the caret at or a BR
7969
- node = container;
7970
- walker = new tinymce.dom.TreeWalker(container, body);
7971
- do {
7972
- // Found a text node use that position
7973
- if (node.nodeType === 3) {
7974
- offset = start ? 0 : node.nodeValue.length - 1;
7975
- container = node;
7976
- break;
7977
- }
8061
+ // Don't walk into elements that doesn't have any child nodes like a IMG
8062
+ if (container.hasChildNodes()) {
8063
+ // Walk the DOM to find a text node to place the caret at or a BR
8064
+ node = container;
8065
+ walker = new tinymce.dom.TreeWalker(container, body);
8066
+ do {
8067
+ // Found a text node use that position
8068
+ if (node.nodeType === 3) {
8069
+ offset = start ? 0 : node.nodeValue.length - 1;
8070
+ container = node;
8071
+ break;
8072
+ }
7978
8073
 
7979
- // Found a BR element that we can place the caret before
7980
- if (node.nodeName === 'BR') {
7981
- offset = dom.nodeIndex(node);
7982
- container = node.parentNode;
7983
- break;
7984
- }
7985
- } while (node = (start ? walker.next() : walker.prev()));
8074
+ // Found a BR element that we can place the caret before
8075
+ if (node.nodeName === 'BR') {
8076
+ offset = dom.nodeIndex(node);
8077
+ container = node.parentNode;
8078
+ break;
8079
+ }
8080
+ } while (node = (start ? walker.next() : walker.prev()));
7986
8081
 
7987
- normalized = true;
8082
+ normalized = true;
8083
+ }
7988
8084
  }
7989
8085
  }
7990
8086
 
@@ -8278,7 +8374,7 @@ window.tinymce.dom.Sizzle = Sizzle;
8278
8374
 
8279
8375
  // Explorer won't clone contents of script and style and the
8280
8376
  // selected index of select elements are cleared on a clone operation.
8281
- if (isIE && dom.select('script,style,select').length > 0) {
8377
+ if (isIE && dom.select('script,style,select,map').length > 0) {
8282
8378
  content = node.innerHTML;
8283
8379
  node = node.cloneNode(false);
8284
8380
  dom.setHTML(node, content);
@@ -9979,6 +10075,11 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9979
10075
  Event.remove(t.id, 'blur', bf);
9980
10076
  });
9981
10077
 
10078
+ //prevent default left and right keys on chrome - so that the keyboard navigation is used.
10079
+ if (tinymce.isWebKit && (e.keyCode==37 ||e.keyCode==39)) {
10080
+ return Event.prevent(e);
10081
+ }
10082
+
9982
10083
  if (e.keyCode == 13 || e.keyCode == 32) {
9983
10084
  onChange(e);
9984
10085
  return Event.cancel(e);
@@ -9989,6 +10090,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9989
10090
  }
9990
10091
  });
9991
10092
  })(tinymce);
10093
+
9992
10094
  (function(tinymce) {
9993
10095
  var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
9994
10096
 
@@ -10244,6 +10346,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
10244
10346
  }
10245
10347
 
10246
10348
  t.isMenuVisible = 0;
10349
+ t.onHideMenu.dispatch();
10247
10350
  }
10248
10351
  },
10249
10352
 
@@ -11327,14 +11430,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11327
11430
 
11328
11431
  t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
11329
11432
 
11330
- // Firefox 2 doesn't load stylesheets correctly this way
11331
- if (!isGecko || !/Firefox\/2/.test(navigator.userAgent)) {
11332
- for (i = 0; i < t.contentCSS.length; i++)
11333
- t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';
11334
-
11335
- t.contentCSS = [];
11336
- }
11337
-
11338
11433
  bi = s.body_id || 'tinymce';
11339
11434
  if (bi.indexOf('=') != -1) {
11340
11435
  bi = t.getParam('body_id', '', 'hash');
@@ -11347,7 +11442,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11347
11442
  bc = bc[t.id] || '';
11348
11443
  }
11349
11444
 
11350
- t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
11445
+ t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';
11351
11446
 
11352
11447
  // Domain relaxing enabled, then set document domain
11353
11448
  if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
@@ -11365,7 +11460,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11365
11460
  title : s.aria_label,
11366
11461
  style : {
11367
11462
  width : '100%',
11368
- height : h
11463
+ height : h,
11464
+ display : 'block' // Important for Gecko to render the iframe correctly
11369
11465
  }
11370
11466
  });
11371
11467
 
@@ -11380,50 +11476,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11380
11476
  e = n = o = null; // Cleanup
11381
11477
  },
11382
11478
 
11383
- setupIframe : function(filled) {
11479
+ setupIframe : function() {
11384
11480
  var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
11385
11481
 
11386
11482
  // Setup iframe body
11387
- if ((!isIE || !tinymce.relaxedDomain) && !filled) {
11388
- // We need to wait for the load event on Gecko
11389
- if (isGecko && !s.readonly) {
11390
- t.getWin().addEventListener("DOMContentLoaded", function() {
11391
- window.setTimeout(function() {
11392
- var b = t.getBody(), undef;
11393
-
11394
- // Editable element needs to have some contents or backspace/delete won't work properly for some odd reason on FF 3.6 or older
11395
- b.innerHTML = '<br>';
11396
-
11397
- // Check if Gecko supports contentEditable mode FF2 doesn't
11398
- if (b.contentEditable !== undef) {
11399
- // Setting the contentEditable off/on seems to force caret mode in the editor and enabled auto focus
11400
- b.contentEditable = false;
11401
- b.contentEditable = true;
11402
-
11403
- // Caret doesn't get rendered when you mousedown on the HTML element on FF 3.x
11404
- t.onMouseDown.add(function(ed, e) {
11405
- if (e.target.nodeName === "HTML") {
11406
- // Setting the contentEditable off/on seems to force caret mode in the editor and enabled auto focus
11407
- b.contentEditable = false;
11408
- b.contentEditable = true;
11409
-
11410
- d.designMode = 'on'; // Render the caret
11411
-
11412
- // Remove design mode again after a while so it has some time to execute
11413
- window.setTimeout(function() {
11414
- d.designMode = 'off';
11415
- t.getBody().focus();
11416
- }, 1);
11417
- }
11418
- });
11419
- } else
11420
- d.designMode = 'on';
11421
-
11422
- // Call setup frame once the contentEditable/designMode has been initialized
11423
- // since the caret won't be rendered some times otherwise.
11424
- t.setupIframe(true);
11425
- }, 1);
11426
- }, false);
11483
+ if (!isIE || !tinymce.relaxedDomain) {
11484
+ // Fix for a focus bug in FF 3.x where the body element
11485
+ // wouldn't get proper focus if the user clicked on the HTML element
11486
+ if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
11487
+ t.onMouseDown.add(function(ed, e) {
11488
+ if (e.target.nodeName === "HTML") {
11489
+ var body = t.getBody();
11490
+
11491
+ // Blur the body it's focused but not correctly focused
11492
+ body.blur();
11493
+
11494
+ // Refocus the body after a little while
11495
+ setTimeout(function() {
11496
+ body.focus();
11497
+ }, 0);
11498
+ }
11499
+ });
11427
11500
  }
11428
11501
 
11429
11502
  d.open();
@@ -11432,17 +11505,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11432
11505
 
11433
11506
  if (tinymce.relaxedDomain)
11434
11507
  d.domain = tinymce.relaxedDomain;
11435
-
11436
- // Wait for iframe onload event on Gecko
11437
- if (isGecko && !s.readonly)
11438
- return;
11439
11508
  }
11440
11509
 
11441
11510
  // It will not steal focus while setting contentEditable
11442
11511
  b = t.getBody();
11443
11512
  b.disabled = true;
11444
11513
 
11445
- if (!isGecko && !s.readonly)
11514
+ if (!s.readonly)
11446
11515
  b.contentEditable = true;
11447
11516
 
11448
11517
  b.disabled = false;
@@ -11592,6 +11661,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11592
11661
  subscript : {inline : 'sub'},
11593
11662
  superscript : {inline : 'sup'},
11594
11663
 
11664
+ link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
11665
+ onmatch : function(node) {
11666
+ return true;
11667
+ },
11668
+
11669
+ onformat : function(elm, fmt, vars) {
11670
+ each(vars, function(value, key) {
11671
+ t.dom.setAttrib(elm, key, value);
11672
+ });
11673
+ }
11674
+ },
11675
+
11595
11676
  removeformat : [
11596
11677
  {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
11597
11678
  {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
@@ -11844,6 +11925,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11844
11925
  controlElm = ieRng.item(0);
11845
11926
  }
11846
11927
 
11928
+ t._refreshContentEditable();
11847
11929
  selection.normalize();
11848
11930
 
11849
11931
  // Is not content editable
@@ -12580,16 +12662,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12580
12662
  var t = this, d = t.getDoc(), s = t.settings;
12581
12663
 
12582
12664
  if (isGecko && !s.readonly) {
12583
- if (t._isHidden()) {
12584
- try {
12585
- if (!s.content_editable) {
12586
- d.body.contentEditable = false;
12587
- d.body.contentEditable = true;
12588
- }
12589
- } catch (ex) {
12590
- // Fails if it's hidden
12591
- }
12592
- }
12665
+ t._refreshContentEditable();
12593
12666
 
12594
12667
  try {
12595
12668
  // Try new Gecko method
@@ -12865,7 +12938,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12865
12938
  if (tinymce.isWebKit) {
12866
12939
  dom.bind(t.getDoc(), 'selectionchange', function() {
12867
12940
  if (t.selectionTimer) {
12868
- window.clearTimeout(t.selectionTimer);
12941
+ clearTimeout(t.selectionTimer);
12869
12942
  t.selectionTimer = 0;
12870
12943
  }
12871
12944
 
@@ -12927,6 +13000,21 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12927
13000
  }
12928
13001
  },
12929
13002
 
13003
+ _refreshContentEditable : function() {
13004
+ var self = this, body, parent;
13005
+
13006
+ // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
13007
+ if (self._isHidden()) {
13008
+ body = self.getBody();
13009
+ parent = body.parentNode;
13010
+
13011
+ parent.removeChild(body);
13012
+ parent.appendChild(body);
13013
+
13014
+ body.focus();
13015
+ }
13016
+ },
13017
+
12930
13018
  _isHidden : function() {
12931
13019
  var s;
12932
13020
 
@@ -12949,6 +13037,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12949
13037
  selection = editor.selection,
12950
13038
  commands = {state: {}, exec : {}, value : {}},
12951
13039
  settings = editor.settings,
13040
+ formatter = editor.formatter,
12952
13041
  bookmark;
12953
13042
 
12954
13043
  function execCommand(command, ui, value) {
@@ -13014,11 +13103,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13014
13103
  };
13015
13104
 
13016
13105
  function isFormatMatch(name) {
13017
- return editor.formatter.match(name);
13106
+ return formatter.match(name);
13018
13107
  };
13019
13108
 
13020
13109
  function toggleFormat(name, value) {
13021
- editor.formatter.toggle(name, value ? {value : value} : undefined);
13110
+ formatter.toggle(name, value ? {value : value} : undefined);
13022
13111
  };
13023
13112
 
13024
13113
  function storeSelection(type) {
@@ -13078,7 +13167,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13078
13167
  // Remove all other alignments first
13079
13168
  each('left,center,right,full'.split(','), function(name) {
13080
13169
  if (align != name)
13081
- editor.formatter.remove('align' + name);
13170
+ formatter.remove('align' + name);
13082
13171
  });
13083
13172
 
13084
13173
  toggleFormat('align' + align);
@@ -13135,7 +13224,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13135
13224
  },
13136
13225
 
13137
13226
  RemoveFormat : function(command) {
13138
- editor.formatter.remove(command);
13227
+ formatter.remove(command);
13139
13228
  },
13140
13229
 
13141
13230
  mceBlockQuote : function(command) {
@@ -13358,7 +13447,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13358
13447
  },
13359
13448
 
13360
13449
  mceToggleFormat : function(command, ui, value) {
13361
- editor.formatter.toggle(value);
13450
+ formatter.toggle(value);
13362
13451
  },
13363
13452
 
13364
13453
  InsertHorizontalRule : function() {
@@ -13375,47 +13464,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13375
13464
  },
13376
13465
 
13377
13466
  mceInsertLink : function(command, ui, value) {
13378
- var link = dom.getParent(selection.getNode(), 'a'), img, style, cls;
13467
+ var anchor;
13379
13468
 
13380
- if (tinymce.is(value, 'string'))
13469
+ if (typeof(value) == 'string')
13381
13470
  value = {href : value};
13382
13471
 
13472
+ anchor = dom.getParent(selection.getNode(), 'a');
13473
+
13383
13474
  // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
13384
13475
  value.href = value.href.replace(' ', '%20');
13385
13476
 
13386
- if (!link) {
13387
- // WebKit can't create links on floated images for some odd reason
13388
- // So, just remove styles and restore it later
13389
- if (tinymce.isWebKit) {
13390
- img = dom.getParent(selection.getNode(), 'img');
13391
-
13392
- if (img) {
13393
- style = img.style.cssText;
13394
- cls = img.className;
13395
- img.style.cssText = null;
13396
- img.className = null;
13397
- }
13398
- }
13399
-
13400
- execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');
13477
+ // Remove existing links if there could be child links or that the href isn't specified
13478
+ if (!anchor || !value.href) {
13479
+ formatter.remove('link');
13480
+ }
13401
13481
 
13402
- // Restore styles
13403
- if (style)
13404
- img.style.cssText = style;
13405
- if (cls)
13406
- img.className = cls;
13407
-
13408
- each(dom.select("a[href='javascript:mctmp(0);']"), function(link) {
13409
- dom.setAttribs(link, value);
13410
- });
13411
- } else {
13412
- if (value.href)
13413
- dom.setAttribs(link, value);
13414
- else
13415
- editor.dom.remove(link, TRUE);
13482
+ // Apply new link to selection
13483
+ if (value.href) {
13484
+ formatter.apply('link', value, anchor);
13416
13485
  }
13417
13486
  },
13418
-
13487
+
13419
13488
  selectAll : function() {
13420
13489
  var root = dom.getRoot(), rng = dom.createRng();
13421
13490
 
@@ -13987,6 +14056,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13987
14056
  ra.setStart(en, 0);
13988
14057
  }
13989
14058
 
14059
+ // If the body is totally empty add a BR element this might happen on webkit
14060
+ if (!d.body.hasChildNodes()) {
14061
+ d.body.appendChild(dom.create('br'));
14062
+ }
14063
+
13990
14064
  // Never use body as start or end node
13991
14065
  sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
13992
14066
  sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
@@ -14846,6 +14920,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14846
14920
  fmt = fmt || format;
14847
14921
 
14848
14922
  if (elm) {
14923
+ if (fmt.onformat) {
14924
+ fmt.onformat(elm, fmt, vars, node);
14925
+ }
14926
+
14849
14927
  each(fmt.styles, function(value, name) {
14850
14928
  dom.setStyle(elm, name, replaceVars(value, vars));
14851
14929
  });
@@ -14863,7 +14941,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14863
14941
  }
14864
14942
  };
14865
14943
  function adjustSelectionToVisibleSelection() {
14866
-
14867
14944
  function findSelectionEnd(start, end) {
14868
14945
  var walker = new TreeWalker(end);
14869
14946
  for (node = walker.current(); node; node = walker.prev()) {
@@ -14871,37 +14948,49 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14871
14948
  return node;
14872
14949
  }
14873
14950
  }
14874
- }
14951
+ };
14875
14952
 
14876
14953
  // Adjust selection so that a end container with a end offset of zero is not included in the selection
14877
14954
  // as this isn't visible to the user.
14878
14955
  var rng = ed.selection.getRng();
14879
14956
  var start = rng.startContainer;
14880
14957
  var end = rng.endContainer;
14958
+
14881
14959
  if (start != end && rng.endOffset == 0) {
14882
14960
  var newEnd = findSelectionEnd(start, end);
14883
14961
  var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
14962
+
14884
14963
  rng.setEnd(newEnd, endOffset);
14885
14964
  }
14965
+
14886
14966
  return rng;
14887
14967
  }
14888
14968
 
14889
14969
  function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){
14890
- var nodes =[], listIndex =-1, list, startIndex = -1, endIndex = -1, currentWrapElm;
14970
+ var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;
14891
14971
 
14892
14972
  // find the index of the first child list.
14893
14973
  each(node.childNodes, function(n, index) {
14894
- if (n.nodeName==="UL"||n.nodeName==="OL") {listIndex = index; list=n; return false; }
14974
+ if (n.nodeName === "UL" || n.nodeName === "OL") {
14975
+ listIndex = index;
14976
+ list = n;
14977
+ return false;
14978
+ }
14895
14979
  });
14896
14980
 
14897
14981
  // get the index of the bookmarks
14898
14982
  each(node.childNodes, function(n, index) {
14899
- if (n.nodeName==="SPAN" &&dom.getAttrib(n, "data-mce-type")=="bookmark" && n.id==bookmark.id+"_start") {startIndex=index}
14900
- if (n.nodeName==="SPAN" &&dom.getAttrib(n, "data-mce-type")=="bookmark" && n.id==bookmark.id+"_end") {endIndex=index}
14983
+ if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
14984
+ if (n.id == bookmark.id + "_start") {
14985
+ startIndex = index;
14986
+ } else if (n.id == bookmark.id + "_end") {
14987
+ endIndex = index;
14988
+ }
14989
+ }
14901
14990
  });
14902
14991
 
14903
14992
  // if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally
14904
- if (listIndex<=0 || (startIndex<listIndex&&endIndex>listIndex)) {
14993
+ if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {
14905
14994
  each(tinymce.grep(node.childNodes), process);
14906
14995
  return 0;
14907
14996
  } else {
@@ -14909,22 +14998,26 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14909
14998
 
14910
14999
  // create a list of the nodes on the same side of the list as the selection
14911
15000
  each(tinymce.grep(node.childNodes), function(n, index) {
14912
- if ((startIndex<listIndex && index <listIndex) || (startIndex>listIndex && index >listIndex)) {
15001
+ if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
14913
15002
  nodes.push(n);
14914
- n.parentNode.removeChild(n);
15003
+ n.parentNode.removeChild(n);
14915
15004
  }
14916
15005
  });
14917
15006
 
14918
15007
  // insert the wrapping element either before or after the list.
14919
- if (startIndex<listIndex) {
15008
+ if (startIndex < listIndex) {
14920
15009
  node.insertBefore(currentWrapElm, list);
14921
- } else if (startIndex>listIndex) {
15010
+ } else if (startIndex > listIndex) {
14922
15011
  node.insertBefore(currentWrapElm, list.nextSibling);
14923
15012
  }
14924
15013
 
14925
15014
  // add the new nodes to the list.
14926
15015
  newWrappers.push(currentWrapElm);
14927
- each(nodes, function(node){currentWrapElm.appendChild(node)});
15016
+
15017
+ each(nodes, function(node) {
15018
+ currentWrapElm.appendChild(node);
15019
+ });
15020
+
14928
15021
  return currentWrapElm;
14929
15022
  }
14930
15023
  };
@@ -15138,7 +15231,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15138
15231
  }
15139
15232
 
15140
15233
  // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
15141
- if (node) {
15234
+ if (node && format.merge_siblings !== false) {
15142
15235
  node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
15143
15236
  node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
15144
15237
  }
@@ -15404,6 +15497,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
15404
15497
  function matchItems(node, format, item_name) {
15405
15498
  var key, value, items = format[item_name], i;
15406
15499
 
15500
+ // Custom match
15501
+ if (format.onmatch) {
15502
+ return format.onmatch(node, format, item_name);
15503
+ }
15504
+
15407
15505
  // Check all items
15408
15506
  if (items) {
15409
15507
  // Non indexed object